Fossil

Check-in Differences
Login

Check-in Differences

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Difference From js-hamburger-menu To trunk

2024-04-24
21:25
Fix or disable brittle test cases that were broken by changes in 2.23. ... (Leaf check-in: 5ad70808 user: drh tags: branch-2.24)
21:19
Fix or disable brittle test cases that were broken by changes in 2.23. ... (Leaf check-in: e198bfd1 user: drh tags: trunk)
2024-04-23
18:22
Further improvement to rail selection in the /timeline graph layout algorithm: Move merge lines that go into the branch rail that is on the left, over toward the left when using the r=BRANCH query parameter. ... (check-in: b71363e5 user: drh tags: trunk)
2019-01-02
15:31
Enhancements to the hamburger menu mechanism. ... (check-in: 724a9b8f user: drh tags: trunk)
07:24
Oops, fallen for testing built-in versus customized skin, fix the previous check-in. ... (Closed-Leaf check-in: dac9293b user: florian tags: js-hamburger-menu)
07:03
Less rigid string-to-number conversion for the custom "data-anim-ms" attribute. ... (check-in: e4a18b67 user: florian tags: js-hamburger-menu)

Deleted .dockerignore.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26


























-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
_FOSSIL_
.fslckout
ajax
art
autosetup
bld
compat
debian
fossil
fossil.exe
setup
src
test
tools
win
wbld
win
www
*.a
*.lib
*.log
*.manifest
*.o
*.obj
*.pdb
*.res

Added .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

Changes to .fossil-settings/binary-glob.

1
2
3
4
5
6
7

8
9
10
11

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

Changes to .fossil-settings/clean-glob.

1
2
3
4
5
6
7
8
9
10
11

12
13
14
15
16
17
18
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19











+







*.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

Changes to .fossil-settings/ignore-glob.

1
2


3
4
5
6
7
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.*

Changes to BUILD.txt.

53
54
55
56
57
58
59
60
61


62
63
64
65
66
67


68
69
70
71


72
73

74
75
76
77
78

53
54
55
56
57
58
59


60
61
62
63
64
65


66
67
68
69


70
71
72

73
74
75
76
77

78







-
-
+
+




-
-
+
+


-
-
+
+

-
+




-
+

* 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 src/makemake.tcl.
  Do not edit src/main.mk directly.  Update src/makemake.tcl and
  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 src/makeheaders.c.  Documentation is found in
  src/makeheaders.html.
  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 src/translate.c.
  A header comment in src/translate.c explains in detail what it does.
  "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 src/mkindex.c program generates some C code that implements
* 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://www.fossil-scm.org/fossil/doc/trunk/www/makefile.wiki
http://fossil-scm.org/home/doc/trunk/www/makefile.wiki

Changes to 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








1


2
3
4
5

6

7
8


9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

26


27
28
29
30
31
32
33
34
35
36
37
38
39

40
41
42
43
44
45
46
47


48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64

65
66
67
68
69

70
71
72
73
74
75
76
77

78
79
80
81
82
83
84
85

86
87
88
89
90
91
-
-
+
-
-
+

+
+
-
+
-

+
-
-
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
-
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+

-
-
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
-
+

+
+
+
-
+
+
+
+
+
+
+

-
+
+
+
+

+
+
+
-
+
+
+
+
+
+
###
#   Dockerfile for Fossil
# syntax=docker/dockerfile:1.3
###
FROM fedora:28
# See www/containers.md for documentation on how to use this file.

## ---------------------------------------------------------------------
## STAGE 1: Build static Fossil binary
### Now install some additional parts we will need for the build
## ---------------------------------------------------------------------
RUN dnf update -y && dnf install -y gcc make tcl tcl-devel zlib-devel openssl-devel tar && dnf clean all && groupadd -r fossil -g 433 && useradd -u 431 -r -g fossil -d /opt/fossil -s /sbin/nologin -c "Fossil user" fossil

### We aren't pinning to a more stable version of Alpine because we want
### If you want to build "trunk", change the next line accordingly.
ENV FOSSIL_INSTALL_VERSION release
### 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
RUN curl "https://www.fossil-scm.org/index.html/tarball/fossil-src.tar.gz?name=fossil-src&uuid=${FOSSIL_INSTALL_VERSION}" | tar zx

RUN cd fossil-src && ./configure --disable-fusefs --json --with-th1-docs --with-th1-hooks --with-tcl=1 --with-tcl-stubs --with-tcl-private-stubs
RUN cd fossil-src/src && mv main.c main.c.orig && sed s/\"now\"/0/ <main.c.orig >main.c
### 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}"
RUN cd fossil-src && make && strip fossil && cp fossil /usr/bin && cd .. && rm -rf fossil-src && chmod a+rx /usr/bin/fossil && mkdir -p /opt/fossil && chown fossil:fossil /opt/fossil
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

### Build is done, remove modules no longer needed
RUN dnf remove -y gcc make zlib-devel tcl-devel openssl-devel tar && dnf clean all

## ---------------------------------------------------------------------
## 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
USER fossil


## ---------------------------------------------------------------------
## STAGE 3: Drop BusyBox, too, now that we're done with its /bin/sh &c
## ---------------------------------------------------------------------
ENV HOME /opt/fossil

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/

EXPOSE 8080

## ---------------------------------------------------------------------
## RUN!
## ---------------------------------------------------------------------

ENV PATH "/bin"
EXPOSE 8080/tcp
USER fossil
CMD ["/usr/bin/fossil", "server", "--create", "--user", "admin", "/opt/fossil/repository.fossil"]
ENTRYPOINT [ "fossil", "server" ]
CMD [                       \
    "--create",             \
    "--jsmode", "bundled",  \
    "--user",   "admin",    \
    "museum/repo.fossil" ]

Changes to Makefile.classic.

1
2
3
4
5
6
7
8
9
10
11






12
13
14
15
16
17
18
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24











+
+
+
+
+
+







#!/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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66

67
68

69
70

71
72
73
74

75
76
77
78
79
80
81
82
83
84
85
86
87








88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104


105
40
41
42
43
44
45
46




47
48
49



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64

65


66


67

68
69

70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111







-
-
-
-



-
-
-















-
+
-
-
+
-
-
+
-


-
+













+
+
+
+
+
+
+
+

















+
+

#    the finished binary for fossil.  The BCC compiler above is used
#    for building intermediate code-generator tools.
#
#TCC = gcc -O6
#TCC = gcc -g -O0 -Wall -fprofile-arcs -ftest-coverage
TCC = gcc -g -Os -Wall

# To use the included miniz library
# FOSSIL_ENABLE_MINIZ = 1
# TCC += -DFOSSIL_ENABLE_MINIZ

# To add support for HTTPS
TCC += -DFOSSIL_ENABLE_SSL

# To enable legacy mv/rm support
TCC += -DFOSSIL_ENABLE_LEGACY_MV_RM=1

#### 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 unless the miniz
#    to link against the Z-Lib compression library.  There are no
#    library in the source tree is being used.  There are no other
#    required dependencies.
#    other required dependencies.
ZLIB_LIB.0 = -lz
ZLIB_LIB.1 =
ZLIB_LIB = -lz
ZLIB_LIB.  = $(ZLIB_LIB.0)

# If using zlib:
LIB += $(ZLIB_LIB.$(FOSSIL_ENABLE_MINIZ)) $(LDFLAGS)
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

Changes to Makefile.in.

1
2
3
4
5
6
7
8
9
10
11







12
13
14
15
16
17
18
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25











+
+
+
+
+
+
+







#!/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.
#
35
36
37
38
39
40
41

42
43
44








45
46









47
48
49




50

















51
52
53
54
55

56
57
58
59




60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
















































42
43
44
45
46
47
48
49
50
51

52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77

78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173







+


-
+
+
+
+
+
+
+
+


+
+
+
+
+
+
+
+
+



+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





+




+
+
+
+

















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

#### 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 -D_HAVE_SQLITE_CONFIG_H
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.
FOSSIL_ENABLE_MINIZ = @FOSSIL_ENABLE_MINIZ@

#### 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)

Changes to Makefile.osx-jaguar.

36
37
38
39
40
41
42
43

44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64

65
66
67
68
69
70
71
72
73
36
37
38
39
40
41
42

43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61

62

63
64
65
66

67
68
69
70
71







-
+


















-

-
+



-





#
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 = 
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
TCC += -DFOSSIL_ENABLE_LEGACY_MV_RM=1
INSTALLDIR = $(DESTDIR)/usr/local/bin
USE_SYSTEM_SQLITE = 
USE_SYSTEM_SQLITE =
USE_LINENOISE = 1
# FOSSIL_ENABLE_TCL = @FOSSIL_ENABLE_TCL@
FOSSIL_ENABLE_TCL = 0
FOSSIL_ENABLE_MINIZ = 0

include $(SRCDIR)/main.mk

distclean: clean
	rm -f autoconfig.h config.log Makefile

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.

Changes to VERSION.

1


1
-
+
2.7
2.25

Deleted ajax/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






































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
This is the README for how to set up the Fossil/JSON test web page
under Apache on Unix systems. This is only intended only for
Fossil/JSON developers/tinkerers:

First, copy cgi-bin/fossil-json.cgi.example to
cgi-bin/fossil-json.cgi.  Edit it and correct the paths to the fossil
binary and the repo you want to serve. Make it executable.

MAKE SURE that the fossil repo you use is world-writable OR that your
Web/CGI server is set up to run as the user ID of the owner of the
fossil file. ALSO: the DIRECTORY CONTAINING the repo file must be
writable by the CGI process.

Next, set up an apache vhost entry. Mine looks like:

<VirtualHost *:80>
    ServerAlias fjson
    ScriptAlias /cgi-bin/ /home/stephan/cvs/fossil/fossil-json/ajax/cgi-bin/
    DocumentRoot /home/stephan/cvs/fossil/fossil-json/ajax
</VirtualHost>

Now add your preferred vhost name (fjson in the above example) to /etc/hosts:

  127.0.0.1 ...other aliases... fjson

Restart your Apache.

Now visit: http://fjson/

that will show the test/demo page. If it doesn't, edit index.html and
make sure that:

  WhAjaj.Connector.options.ajax.url = ...;

points to your CGI script. In theory you can also do this over fossil
standalone server mode, but i haven't yet tested that particular test
page in that mode.

Deleted ajax/cgi-bin/fossil-json.cgi.example.

1
2


-
-
#!/path/to/fossil/binary
repository: /path/to/repo.fsl

Deleted ajax/i-test/rhino-shell.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
















































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
var FShell = {
    serverUrl:
        'http://localhost:8080'
        //'http://fjson/cgi-bin/fossil-json.cgi'
        //'http://192.168.1.62:8080'
        //'http://fossil.wanderinghorse.net/repos/fossil-json-java/index.cgi'
        ,
    verbose:false,
    prompt:"fossil shell > ",
    wiki:{},
    consol:java.lang.System.console(),
    v:function(msg){
        if(this.verbose){
            print("VERBOSE: "+msg);
        }
    }
};
(function bootstrap() {
    var srcdir = '../js/';
    var includes = [srcdir+'json2.js',
                    srcdir+'whajaj.js',
                    srcdir+'fossil-ajaj.js'
                    ];
    for( var i in includes ) {
        load(includes[i]);
    }
    WhAjaj.Connector.prototype.sendImpl = WhAjaj.Connector.sendImpls.rhino;
    FShell.fossil = new FossilAjaj({
        asynchronous:false, /* rhino-based impl doesn't support async. */
        timeout:10000,
        url:FShell.serverUrl
    });
    print("Server: "+FShell.serverUrl);
    var cb = FShell.fossil.ajaj.callbacks;
    cb.beforeSend = function(req,opt){
        if(!FShell.verbose) return;
        print("SENDING REQUEST: AJAJ options="+JSON.stringify(opt));
        if(req) print("Request envelope="+WhAjaj.stringify(req));
    };
    cb.afterSend = function(req,opt){
        //if(!FShell.verbose) return;
        //print("REQUEST RETURNED: opt="+JSON.stringify(opt));
        //if(req) print("Request="+WhAjaj.stringify(req));
    };
    cb.onError = function(req,opt){
        //if(!FShell.verbose) return;
        print("ERROR: "+WhAjaj.stringify(opt));
    };
    cb.onResponse = function(resp,req){
        if(!FShell.verbose) return;
        if(resp && resp.resultCode){
            print("Response contains error info: "+resp.resultCode+": "+resp.resultText);
        }
        print("GOT RESPONSE: "+(('string'===typeof resp) ? resp : WhAjaj.stringify(resp)));
    };
    FShell.fossil.HAI({
        onResponse:function(resp,opt){
            assertResponseOK(resp);
        }
    });
})();

/**
    Throws an exception of cond is a falsy value.
*/
function assert(cond, descr){
    descr = descr || "Undescribed condition.";
    if(!cond){
        throw new Error("Assertion failed: "+descr);
    }else{
        //print("Assertion OK: "+descr);
    }
}

/**
    Convenience form of FShell.fossil.sendCommand(command,payload,ajajOpt).
*/
function send(command,payload, ajajOpt){
    FShell.fossil.sendCommand(command,payload,ajajOpt);
}

/**
    Asserts that resp is-a Object, resp.fossil is-a string, and
    !resp.resultCode.
*/
function assertResponseOK(resp){
    assert('object' === typeof resp,'Response is-a object.');
    assert( 'string' === typeof resp.fossil, 'Response contains fossil property.');
    assert( !resp.resultCode, 'resp.resultCode='+resp.resultCode);
}
/**
    Asserts that resp is-a Object, resp.fossil is-a string, and
    resp.resultCode is a truthy value. If expectCode is set then
    it also asserts that (resp.resultCode=='FOSSIL-'+expectCode).
*/
function assertResponseError(resp,expectCode){
    assert('object' === typeof resp,'Response is-a object.');
    assert( 'string' === typeof resp.fossil, 'Response contains fossil property.');
    assert( resp.resultCode, 'resp.resultCode='+resp.resultCode);
    if(expectCode){
        assert( 'FOSSIL-'+expectCode == resp.resultCode, 'Expecting result code '+expectCode );
    }
}

FShell.readline = (typeof readline === 'function') ? (readline) : (function() {
     importPackage(java.io);
     importPackage(java.lang);
     var stdin = new BufferedReader(new InputStreamReader(System['in']));
     var self = this;
     return function(prompt) {
        if(prompt) print(prompt);
        var x = stdin.readLine();
        return null===x ? x : String(x) /*convert to JS string!*/;
     };
}());

FShell.dispatchLine = function(line){
    var av = line.split(' '); // FIXME: to shell-like tokenization. Too tired!
    var cmd = av[0];
    var key, h;
    if('/' == cmd[0]) key = '/';
    else key = this.commandAliases[cmd];
    if(!key) key = cmd;
    h = this.commandHandlers[key];
    if(!h){
        print("Command not known: "+cmd +" ("+key+")");
    }else if(!WhAjaj.isFunction(h)){
        print("Not a function: "+key);
    }
    else{
        print("Sending ["+key+"] command... ");
        try{h(av);}
        catch(e){ print("EXCEPTION: "+e); }
    }
};

FShell.onResponseDefault = function(callback){
    return function(resp,req){
        assertResponseOK(resp);
        print("Payload: "+(resp.payload ? WhAjaj.stringify(resp.payload) : "none"));
        if(WhAjaj.isFunction(callback)){
            callback(resp,req);
        }
    };
};
FShell.commandHandlers = {
    "?":function(args){
        var k;
        print("Available commands...\n");
        var o = FShell.commandHandlers;
        for(k in o){
            if(! o.hasOwnProperty(k)) continue;
            print("\t"+k);
        }
    },
    "/":function(args){
        FShell.fossil.sendCommand('/json'+args[0],undefined,{
            beforeSend:function(req,opt){
                print("Sending to: "+opt.url);
            },
            onResponse:FShell.onResponseDefault()
        });
    },
    "eval":function(args){
        eval(args.join(' '));
    },
    "login":function(args){
        FShell.fossil.login(args[1], args[2], {
            onResponse:FShell.onResponseDefault()
        });
    },
    "whoami":function(args){
        FShell.fossil.whoami({
            onResponse:FShell.onResponseDefault()
        });
    },
    "HAI":function(args){
        FShell.fossil.HAI({
            onResponse:FShell.onResponseDefault()
        });
    }

};
FShell.commandAliases = {
    "li":"login",
    "lo":"logout",
    "who":"whoami",
    "hi":"HAI",
    "tci":"/timeline/ci?limit=3"
};
FShell.mainLoop = function(){
    var line;
    var check = /\S/;
    //var isJavaNull = /java\.lang\.null/;
    //print(typeof java.lang['null']);
    while( null != (line=this.readline(this.prompt)) ){
        if(null===line) break /*EOF*/;
        else if( "" === line ) continue;
        //print("Got line: "+line);
        else if(!check.test(line)) continue;
        print('typeof line = '+typeof line);
        this.dispatchLine(line);
        print("");
    }
    print("Bye!");
};

FShell.mainLoop();

Deleted ajax/i-test/rhino-test.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























































































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
var TestApp = {
    serverUrl:
        'http://localhost:8080'
        //'http://fjson/cgi-bin/fossil-json.cgi'
        //'http://192.168.1.62:8080'
        //'http://fossil.wanderinghorse.net/repos/fossil-json-java/index.cgi'
        ,
    verbose:true,
    fossilBinary:'fossil',
    wiki:{}
};
(function bootstrap() {
    var srcdir = '../js/';
    var includes = [srcdir+'json2.js',
                    srcdir+'whajaj.js',
                    srcdir+'fossil-ajaj.js'
                    ];
    for( var i in includes ) {
        load(includes[i]);
    }
    WhAjaj.Connector.prototype.sendImpl = WhAjaj.Connector.sendImpls.rhino;
    TestApp.fossil = new FossilAjaj({
        asynchronous:false, /* rhino-based impl doesn't support async or timeout. */
        timeout:0,
        url:TestApp.serverUrl,
        fossilBinary:TestApp.fossilBinary
    });
    var cb = TestApp.fossil.ajaj.callbacks;
    cb.beforeSend = function(req,opt){
        if(!TestApp.verbose) return;
        print("SENDING REQUEST: AJAJ options="+JSON.stringify(opt));
        if(req) print("Request envelope="+WhAjaj.stringify(req));
    };
    cb.afterSend = function(req,opt){
        //if(!TestApp.verbose) return;
        //print("REQUEST RETURNED: opt="+JSON.stringify(opt));
        //if(req) print("Request="+WhAjaj.stringify(req));
    };
    cb.onError = function(req,opt){
        if(!TestApp.verbose) return;
        print("ERROR: "+WhAjaj.stringify(opt));
    };
    cb.onResponse = function(resp,req){
        if(!TestApp.verbose) return;
        print("GOT RESPONSE: "+(('string'===typeof resp) ? resp : WhAjaj.stringify(resp)));
    };

})();

/**
    Throws an exception of cond is a falsy value.
*/
function assert(cond, descr){
    descr = descr || "Undescribed condition.";
    if(!cond){
        print("Assertion FAILED: "+descr);
        throw new Error("Assertion failed: "+descr);
        // aarrgghh. Exceptions are of course swallowed by
        // the AJAX layer, to keep from killing a browser's
        // script environment.
    }else{
        if(TestApp.verbose) print("Assertion OK: "+descr);
    }
}

/**
    Calls func() in a try/catch block and throws an exception if
    func() does NOT throw.
*/
function assertThrows(func, descr){
    descr = descr || "Undescribed condition failed.";
    var ex;
    try{
        func();
    }catch(e){
        ex = e;
    }
    if(!ex){
        throw new Error("Function did not throw (as expected): "+descr);
    }else{
        if(TestApp.verbose) print("Function threw (as expected): "+descr+": "+ex);
    }
}

/**
    Convenience form of TestApp.fossil.sendCommand(command,payload,ajajOpt).
*/
function send(command,payload, ajajOpt){
    TestApp.fossil.sendCommand(command,payload,ajajOpt);
}

/**
    Asserts that resp is-a Object, resp.fossil is-a string, and
    !resp.resultCode.
*/
function assertResponseOK(resp){
    assert('object' === typeof resp,'Response is-a object.');
    assert( 'string' === typeof resp.fossil, 'Response contains fossil property.');
    assert( undefined === resp.resultCode, 'resp.resultCode is not set');
}
/**
    Asserts that resp is-a Object, resp.fossil is-a string, and
    resp.resultCode is a truthy value. If expectCode is set then
    it also asserts that (resp.resultCode=='FOSSIL-'+expectCode).
*/
function assertResponseError(resp,expectCode){
    assert('object' === typeof resp,'Response is-a object.');
    assert( 'string' === typeof resp.fossil, 'Response contains fossil property.');
    assert( !!resp.resultCode, 'resp.resultCode='+resp.resultCode);
    if(expectCode){
        assert( 'FOSSIL-'+expectCode == resp.resultCode, 'Expecting result code '+expectCode );
    }
}

function testHAI(){
    var rs;
    TestApp.fossil.HAI({
        onResponse:function(resp,req){
            rs = resp;
        }
    });
    assertResponseOK(rs);
    TestApp.serverVersion = rs.fossil;
    assert( 'string' === typeof TestApp.serverVersion, 'server version = '+TestApp.serverVersion);
}
testHAI.description = 'Get server version info.';

function testIAmNobody(){
    TestApp.fossil.whoami('/json/whoami');
    assert('nobody' === TestApp.fossil.auth.name, 'User == nobody.' );
    assert(!TestApp.fossil.auth.authToken, 'authToken is not set.' );

}
testIAmNobody.description = 'Ensure that current user is "nobody".';


function testAnonymousLogin(){
    TestApp.fossil.login();
    assert('string' === typeof TestApp.fossil.auth.authToken, 'authToken = '+TestApp.fossil.auth.authToken);
    assert( 'string' === typeof TestApp.fossil.auth.name, 'User name = '+TestApp.fossil.auth.name);
    TestApp.fossil.userName = null;
    TestApp.fossil.whoami('/json/whoami');
    assert( 'string' === typeof TestApp.fossil.auth.name, 'User name = '+TestApp.fossil.auth.name);
}
testAnonymousLogin.description = 'Perform anonymous login.';

function testAnonWiki(){
    var rs;
    TestApp.fossil.sendCommand('/json/wiki/list',undefined,{
        beforeSend:function(req,opt){
            assert( req && (req.authToken==TestApp.fossil.auth.authToken), 'Request envelope contains expected authToken.'  );
        },
        onResponse:function(resp,req){
            rs = resp;
        }
    });
    assertResponseOK(rs);
    assert( (typeof [] === typeof rs.payload) && rs.payload.length,
        "Wiki list seems to be okay.");
    TestApp.wiki.list = rs.payload;

    TestApp.fossil.sendCommand('/json/wiki/get',{
        name:TestApp.wiki.list[0]
    },{
        onResponse:function(resp,req){
            rs = resp;
        }
    });
    assertResponseOK(rs);
    assert(rs.payload.name == TestApp.wiki.list[0], "Fetched page name matches expectations.");
    print("Got first wiki page: "+WhAjaj.stringify(rs.payload));

}
testAnonWiki.description = 'Fetch wiki list as anonymous user.';

function testFetchCheckinArtifact(){
    var art = '18dd383e5e7684ece';
    var rs;
    TestApp.fossil.sendCommand('/json/artifact',{
        'name': art
        },
        {
            onResponse:function(resp,req){
                rs = resp;
            }
        });
    assertResponseOK(rs);
    assert(3 == rs.payload.parents.length, 'Got 3 parent artifacts.');
}
testFetchCheckinArtifact.description = '/json/artifact/CHECKIN';

function testAnonLogout(){
    var rs;
    TestApp.fossil.logout({
        onResponse:function(resp,req){
            rs = resp;
        }
    });
    assertResponseOK(rs);
    print("Ensure that second logout attempt fails...");
    TestApp.fossil.logout({
        onResponse:function(resp,req){
            rs = resp;
        }
    });
    assertResponseError(rs);
}
testAnonLogout.description = 'Log out anonymous user.';

function testExternalProcess(){

    var req = { command:"HAI", requestId:'testExternalProcess()' };
    var args = [TestApp.fossilBinary, 'json', '--json-input', '-'];
    var p = java.lang.Runtime.getRuntime().exec(args);
    var outs = p.getOutputStream();
    var osr = new java.io.OutputStreamWriter(outs);
    var osb = new java.io.BufferedWriter(osr);
    var json = JSON.stringify(req);
    osb.write(json,0, json.length);
    osb.close();
    req = json = outs = osr = osb = undefined;
    var ins = p.getInputStream();
    var isr = new java.io.InputStreamReader(ins);
    var br = new java.io.BufferedReader(isr);
    var line;

    while( null !== (line=br.readLine())){
        print(line);
    }
    br.close();
    isr.close();
    ins.close();
    p.waitFor();
}
testExternalProcess.description = 'Run fossil as external process.';

function testExternalProcessHandler(){
    var aj = TestApp.fossil.ajaj;
    var oldImpl = aj.sendImpl;
    aj.sendImpl = FossilAjaj.rhinoLocalBinarySendImpl;
    var rs;
    TestApp.fossil.sendCommand('/json/HAI',undefined,{
        onResponse:function(resp,opt){
            rs = resp;
        }
    });
    aj.sendImpl = oldImpl;
    assertResponseOK(rs);
    print("Using local fossil binary via AJAX interface, we fetched: "+
        WhAjaj.stringify(rs));
}
testExternalProcessHandler.description = 'Try local fossil binary via AJAX interface.';

(function runAllTests(){
    var testList = [
        testHAI,
        testIAmNobody,
        testAnonymousLogin,
        testAnonWiki,
        testFetchCheckinArtifact,
        testAnonLogout,
        testExternalProcess,
        testExternalProcessHandler
    ];
    var i, f;
    for( i = 0; i < testList.length; ++i ){
        f = testList[i];
        try{
            print("Running test #"+(i+1)+": "+(f.description || "no description."));
            f();
        }catch(e){
            print("Test #"+(i+1)+" failed: "+e);
            throw e;
        }
    }

})();

print("Done! If you don't see an exception message in the last few lines, you win!");

Deleted ajax/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
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332












































































































































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

<head>
    <title>Fossil/JSON raw request sending</title>
    <meta http-equiv="content-type" content="text/html;charset=utf-8" />
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script>
    <script type="text/javascript" src="js/whajaj.js"></script>
    <script type="text/javascript" src="js/fossil-ajaj.js"></script>

<style type='text/css'>
th {
  text-align: left;
  background-color: #ececec;
}

.dangerWillRobinson {
    background-color: yellow;
}
</style>

<script type='text/javascript'>
WhAjaj.Connector.options.ajax.url =
/*
    Change this to your CGI/server root path:
*/
     //'http://fjson/cgi-bin/fossil.cgi'
     //'/repos/fossil-sgb/json.cgi'
    '/cgi-bin/fossil-json.cgi'
     ;
var TheApp = {
      response:null,
      sessionID:null,
      jqe:{}/*jqe==jQuery Elements*/,
      ajaxCount:0,
      cgi: new FossilAjaj()
};


TheApp.startAjaxNotif = function()
{
    ++this.ajaxCount;
    TheApp.jqe.responseContainer.removeClass('dangerWillRobinson');
    this.jqe.ajaxNotification.attr( 'title', this.ajaxCount+" pending AJAX operation(s)..." );
    if( 1 == this.ajaxCount ) this.jqe.ajaxNotification.fadeIn();
};

TheApp.endAjaxNotif = function()
{
    --this.ajaxCount;
    this.jqe.ajaxNotification.attr( 'title', this.ajaxCount+" pending AJAX operation(s)..." );
    if( 0 == this.ajaxCount ) this.jqe.ajaxNotification.fadeOut();
};

TheApp.responseContainsError = function(resp) {
    if( resp && resp.resultCode ) {
        //alert("Error response:\n"+JSON.stringify(resp,0,4));
        TheApp.jqe.taResponse.val( "RESPONSE CONTAINS ERROR INFO:\n"+WhAjaj.stringify(resp) );
        //TheApp.jqe.responseContainer.css({backgroundColor:'yellow'});
        //TheApp.jqe.responseContainer.addClass('dangerWillRobinson');
        TheApp.jqe.responseContainer.flash( '255,0,0', 1500 );
        return true;
    }
    return false;
};


TheApp.sendRequest = function() {
    var path = this.jqe.textPath.val();
    var self = this;
    var data = this.jqe.taRequest.val();
    var doPost = (data && data.length);
    var req;
    if( doPost ) try {
        req = JSON.parse(data);
    }
    catch(e) {
        TheApp.jqe.taResponse.val("Request is not valid JSON.\n"+e);
        return;
    }
    if( req ) {
        req.requestId = this.cgi.generateRequestId();
    }
    var self = this;
    var opt = {
        url: WhAjaj.Connector.options.ajax.url + path,
        method: doPost ? 'POST' : 'GET'
    };
    this.cgi.sendRequest( req, opt );
};
jQuery.fn.animateHighlight = function(highlightColor, duration) {
    var highlightBg = highlightColor || "#FFFF9C";
    var animateMs = duration || 1500;
    var originalBg = this.css("backgroundColor");
    this.stop().css("background-color", highlightBg).animate({backgroundColor: originalBg}, animateMs);
};
jQuery.fn.flash = function( color, duration )
{
    var current = this.css( 'color' );
    this.animate( { color: 'rgb(' + color + ')' }, duration / 2);
    this.animate( { color: current }, duration / 2 );
};

function myJsonPCallback(obj){
    alert("JSONP callback got:\n"+WhAjaj.stringify(obj));
}

jQuery(document).ready(function(){
    var ids = [// list of HTML element IDs we use often.
        'btnSend',
        'ajaxNotification',
        'currentAuthToken',
        'responseContainer',
        'taRequest',
        'taRequestOpt',
        'taResponse',
        'textPath',
        'timer'
    ];
    var i, k;
    for( i = 0; i < ids.length; ++i ) {
        k = ids[i];
        TheApp.jqe[k] = jQuery('#'+k);
    }
    TheApp.jqe.textPath.
        keyup(function(event){
            if(event.keyCode == 13){
                TheApp.sendRequest();
            }
        });
    TheApp.timer = {
        _tstart:0,_tend:0,duration:0,
        start:function(){
            this._tstart = (new Date()).getTime();
        },
        end:function(){
            this._tend = (new Date()).getTime();
            return this.duration = this._tend - this._tstart;
        }
    };

    var ajcb = TheApp.cgi.ajaj.callbacks;
    ajcb.beforeSend = function(req,opt) {
        TheApp.timer.start();
        var val =
            req ?
            (('string'===typeof req) ? req : WhAjaj.stringify(req))
            : '';
        TheApp.jqe.taResponse.val('');
        TheApp.jqe.taRequest.val( val );
        TheApp.jqe.taRequestOpt.val( opt ? WhAjaj.stringify(opt) : '' );
        TheApp.startAjaxNotif();
    };
    ajcb.afterSend = function(req,opt) {
        TheApp.timer.end();
        TheApp.endAjaxNotif();
        TheApp.jqe.timer.text( "(Round-trip time (incl. JS overhead): "+TheApp.timer.duration+'ms)' );
    };
    ajcb.onResponse = function(resp,req, opt) {
        var val;
        if(this.jsonp) return /*was already handled*/;
        try {
            val = WhAjaj.stringify(resp);
        }
        catch(e) {
            val = WhAjaj.stringify(e)
        }
        //alert("onResponse this:"+WhAjaj.stringify(this));
        //alert("val="+val);
        // FIXME: this.url is hosed for login because of how i overload onResponse()
        if( opt.url ) TheApp.jqe.textPath.val(opt.url.replace(WhAjaj.Connector.options.ajax.url,''));
        TheApp.jqe.taResponse.val( val );
    };
    ajcb.onError = function(req,opt) {
        TheApp.jqe.taResponse.val( "ERROR SENDING REQUEST:\n"+WhAjaj.stringify(opt) );
    };

    TheApp.cgi.onLogin = function(){
      TheApp.jqe.taResponse.val( "Logged in:\n"+WhAjaj.stringify(this.auth));
      TheApp.jqe.currentAuthToken.html("Logged in: "+WhAjaj.stringify(this.auth));
    };
    TheApp.cgi.onLogout = function(){
      TheApp.jqe.taResponse.val( "Logged out!" );
      TheApp.jqe.currentAuthToken.text("Not logged in");
    };
    TheApp.cgi.whoami();
    jQuery('#headerArea').click(function(){
        jQuery(this).slideUp('fast',function(){
            jQuery(this).remove();
        });
    });
});

</script>

</head>

<body>
<span id='ajaxNotification'></span>
<div id='headerArea'>
<h1>You know, for sending raw JSON requests to Fossil...</h1>

If you're actually using this page, then you know what you're doing and don't
need help text, hoverhelp, and a snazzy interface.

<br><br>


JSON API docs: <a href='https://docs.google.com/document/d/1fXViveNhDbiXgCuE7QDXQOKeFzf2qNUkBEgiUvoqFN4/edit'>https://docs.google.com/document/d/1fXViveNhDbiXgCuE7QDXQOKeFzf2qNUkBEgiUvoqFN4/edit</a>

</div><!-- #headerArea -->
See also: <a href='wiki-editor.html'>prototype wiki editor</a>.

<h2>Request...</h2>

Path: <input type='text' size='40' id='textPath' value='/json/HAI'/>
<input type='button' value='Send...' id='btnSend' onclick='TheApp.sendRequest()' /><br/>
If the POST textarea is not empty then it will be posted with the request.
<hr/>
<strong>Quick-posts:</strong><br/>
<input type='button' value='HAI' onclick='TheApp.cgi.HAI()' />
<input type='button' value='HAI JSONP' onclick='TheApp.cgi.sendCommand("/json/HAI",undefined,{jsonp:"myJsonPCallback"});' />
<input type='button' value='version' onclick='TheApp.cgi.sendCommand("/json/version")' />
<input type='button' value='stat' onclick='TheApp.cgi.sendCommand("/json/stat?full=0")' />
<input type='button' value='whoami' onclick='TheApp.cgi.whoami()' />
<input type='button' value='cap' onclick='TheApp.cgi.sendCommand("/json/cap")' />
<input type='button' value='resultCodes' onclick='TheApp.cgi.sendCommand("/json/resultCodes")' />
<input type='button' value='g' onclick='TheApp.cgi.sendCommand("/json/g")' />

<br/>

<input type='button' value='branch/list' onclick='TheApp.cgi.sendCommand("/json/branch/list")' />
<input type='button' value='timeline/ci' onclick='TheApp.cgi.sendCommand("/json/timeline/ci?files=true")' />
<input type='button' value='timeline/wiki' onclick='TheApp.cgi.sendCommand("/json/timeline/wiki")' />
<input type='button' value='timeline/ticket' onclick='TheApp.cgi.sendCommand("/json/timeline/ticket")' />
<input type='button' value='timeline/branch' onclick='TheApp.cgi.sendCommand("/json/timeline/branch")' />

<br/>

<input type='button' value='wiki/list' onclick='TheApp.cgi.sendCommand("/json/wiki/list")' />
<input type='button' value='wiki/list verbose' onclick='TheApp.cgi.sendCommand("/json/wiki/list",{verbose:1})' />
<input type='button' value='wiki/get Fossil' onclick='TheApp.cgi.sendCommand("/json/wiki/get",{name:"Fossil"})' />
<input type='button' value='wiki/get/Fossil' onclick='TheApp.cgi.sendCommand("/json/wiki/get/Fossil")' />
<input type='button' value='wiki/diff' onclick='TheApp.cgi.sendCommand("/json/wiki/diff/e32ccdcda59e930c77c/e15992f475760cdf3a9")' />

<br/>

<input type='button' value='user/list' onclick='TheApp.cgi.sendCommand("/json/user/list")' />
<input type='button' value='user/get' onclick='TheApp.cgi.sendCommand("/json/user/get?name=anonymous")' />
<input type='button' value='tag/list' onclick='TheApp.cgi.sendCommand("/json/tag/list?includeTickets=false&raw=false")' />
<input type='button' value='tag/list/json' onclick='TheApp.cgi.sendCommand("/json/tag/list/json?raw=false")' />
<input type='button' value='tag/add'
    onclick='TheApp.cgi.sendCommand("/json/tag/add",{name:"json-add-tag-test",checkin:"json",value:"tag test",propagate:false,raw:false})' />
<input type='button' value='tag/cancel'
    onclick='TheApp.cgi.sendCommand("/json/tag/cancel",{name:"json-add-tag-test",checkin:"json",raw:false})' />
<input type='button' value='tag/find'
    onclick='TheApp.cgi.sendCommand("/json/tag/find",{name:"json",type:"*",raw:false,limit:5})' />

<br/>

<input type='button' value='diff'
    onclick='TheApp.cgi.sendCommand("/json/diff",{v1:"b0e9b45baed6f885",v2:"5f225e261d836287",context:2})' />
<input type='button' value='diff/A/B'
    onclick='TheApp.cgi.sendCommand("/json/diff/b0e9b45baed6f885/5f225e261d836287?context=2")' />

<input type='button' value='query'
    onclick='TheApp.cgi.sendCommand("/json/query?format=o","SELECT * from user")' />

<input type='button' value='report list'
    onclick='TheApp.cgi.sendCommand("/json/report/list")' />
<input type='button' value='report get'
    onclick='TheApp.cgi.sendCommand("/json/report/get",2)' />

<input type='button' value='report run'
    onclick='TheApp.cgi.sendCommand("/json/report/run",{"report":2,"format":"o"})' />

<input type='button' value='config/get' onclick='TheApp.cgi.sendCommand("/json/config/get")' />

<!-- not yet ready...
<input type='button' value='artifact/XYZ' onclick='TheApp.cgi.sendCommand("/json/artifact?uuid=json")' />
-->

<!--
<input type='button' value='get whiki' onclick='TheApp.cgi.getPages("whiki")' />
<input type='button' value='get more' onclick='TheApp.cgi.getPages("HelloWorld/WhikiNews")' />
<input type='button' value='get client data' onclick='TheApp.cgi.getPageClientData("HelloWorld/whiki/WhikiCommands")' />
<input type='button' value='save client data' onclick='TheApp.cgi.savePageClientData({"HelloWorld":[1,3,5]})' />
-->
<hr/>
<b>Login:</b>
<br/>
<input type='button' value='Anon. PW' onclick='TheApp.cgi.sendCommand("/json/anonymousPassword")' />
<input type='button' value='Anon. PW+Login' onclick='TheApp.cgi.login()' />
<br/>
name:<input type='text' id='textUser' value='json-demo' size='12'/>
pw:<input type='password' id='textPassword' value='json-demo' size='12'/>
<input type='button' value='login' onclick='TheApp.cgi.login(jQuery("#textUser").val(),jQuery("#textPassword").val(),{onResponse:TheApp.onLogin})' />
<input type='button' value='logout' onclick='TheApp.cgi.logout()' />
<br/>
<span id='currentAuthToken' style='font-family:monospaced'></span>

<br/>

<hr/>

<table>
    <tr>
        <th>POST data</th>
        <th>Request AJAJ options</th>
    </tr>
    <tr>
        <td width='50%' valign='top'>
            <textarea id='taRequest' rows='10' cols='50'></textarea>
        </td>
        <td width='50%' valign='top'>
            <textarea id='taRequestOpt' rows='10' cols='40' readonly></textarea>
        </td>
    </tr>
    <tr>
        <th colspan='2'>Response <span id='timer'></span></th>
    </tr>
    <tr>
        <td colspan='2' id='responseContainer' valign='top'>
            <textarea id='taResponse' rows='20' cols='80' readonly></textarea>
        </td>
    </tr>
</table>
<div></div>
<div></div>
<div></div>

</body></html>

Deleted ajax/js/fossil-ajaj.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


















































































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
/**
    This file contains a WhAjaj extension for use with Fossil/JSON.

    Author: Stephan Beal (sgbeal@googlemail.com)

    License: Public Domain
*/

/**
    Constructor for a new Fossil AJAJ client. ajajOpt may be an optional
    object suitable for passing to the WhAjaj.Connector() constructor.

    On returning, this.ajaj is-a WhAjaj.Connector instance which can
    be used to send requests to the back-end (though the convenience
    functions of this class are the preferred way to do it). Clients
    are encouraged to use FossilAjaj.sendCommand() (and friends) instead
    of the underlying WhAjaj.Connector API, since this class' API
    contains Fossil-specific request-calling handling (e.g. of authentication
    info) whereas WhAjaj is more generic.
*/
function FossilAjaj(ajajOpt)
{
    this.ajaj = new WhAjaj.Connector(ajajOpt);
    return this;
}

FossilAjaj.prototype.generateRequestId = function() {
    return this.ajaj.generateRequestId();
};

/**
   Proxy for this.ajaj.sendRequest().
*/
FossilAjaj.prototype.sendRequest = function(req,opt) {
    return this.ajaj.sendRequest(req,opt);
};

/**
    Sends a command to the fossil back-end. Command should be the
    path part of the URL, e.g. /json/stat, payload is a request-specific
    value type (may often be null/undefined). ajajOpt is an optional object
    holding WhAjaj.sendRequest()-compatible options.

    This function constructs a Fossil/JSON request envelope based
    on the given arguments and adds this.auth.authToken and a requestId
    to it.
*/
FossilAjaj.prototype.sendCommand = function(command, payload, ajajOpt) {
    var req;
    ajajOpt = ajajOpt || {};
    if(payload || (this.auth && this.auth.authToken) || ajajOpt.jsonp) {
        req = {
            payload:payload,
            requestId:('function' === typeof this.generateRequestId) ? this.generateRequestId() : undefined,
            authToken:(this.auth ? this.auth.authToken : undefined),
            jsonp:('string' === typeof ajajOpt.jsonp) ? ajajOpt.jsonp : undefined
        };
    }
    ajajOpt.method = req ? 'POST' : 'GET';
    // just for debuggering: ajajOpt.method = 'POST'; if(!req) req={};
    if(command) ajajOpt.url = this.ajaj.derivedOption('url',ajajOpt) + command;
    this.ajaj.sendRequest(req,ajajOpt);
};

/**
    Sends a login request to the back-end.

    ajajOpt is an optional configuration object suitable for passing
    to sendCommand().

    After the response returns, this.auth will be
    set to the response payload.

    If name === 'anonymous' (the default if none is passed in) then this
    function ignores the pw argument and must make two requests - the first
    one gets the captcha code and the second one submits it.
    ajajOpt.onResponse() (if set) is only called for the actual login
    response (the 2nd one), as opposed to being called for both requests.
    However, this.ajaj.callbacks.onResponse() _is_ called for both (because
    it happens at a lower level).

    If this object has an onLogin() function it is called (with
    no arguments) before the onResponse() handler of the login is called
    (that is the 2nd request for anonymous logins) and any exceptions
    it throws are ignored.

*/
FossilAjaj.prototype.login = function(name,pw,ajajOpt) {
    name = name || 'anonymous';
    var self = this;
    var loginReq = {
        name:name,
        password:pw
    };
    ajajOpt = this.ajaj.normalizeAjaxParameters( ajajOpt || {} );
    var oldOnResponse = ajajOpt.onResponse;
    ajajOpt.onResponse = function(resp,req) {
        var thisOpt = this;
        //alert('login response:\n'+WhAjaj.stringify(resp));
        if( resp && resp.payload ) {
            //self.userName = resp.payload.name;
            //self.capabilities = resp.payload.capabilities;
            self.auth = resp.payload;
        }
        if( WhAjaj.isFunction( self.onLogin ) ){
            try{ self.onLogin(); }
            catch(e){}
        }
        if( WhAjaj.isFunction(oldOnResponse) ) {
            oldOnResponse.apply(thisOpt,[resp,req]);
        }
    };
    function doLogin(){
        //alert("Sending login request..."+WhAjaj.stringify(loginReq));
        self.sendCommand('/json/login', loginReq, ajajOpt);
    }
    if( 'anonymous' === name ){
      this.sendCommand('/json/anonymousPassword',undefined,{
          onResponse:function(resp,req){
/*
            if( WhAjaj.isFunction(oldOnResponse) ){
                oldOnResponse.apply(this, [resp,req]);
            };
*/
            if(resp && !resp.resultCode){
                //alert("Got PW. Trying to log in..."+WhAjaj.stringify(resp));
                loginReq.anonymousSeed = resp.payload.seed;
                loginReq.password = resp.payload.password;
                doLogin();
            }
          }
      });
    }
    else doLogin();
};

/**
    Logs out of fossil, invaliding this login token.

    ajajOpt is an optional configuration object suitable for passing
    to sendCommand().

    If this object has an onLogout() function it is called (with
    no arguments) before the onResponse() handler is called.
    IFF the response succeeds then this.auth is unset.
*/
FossilAjaj.prototype.logout = function(ajajOpt) {
    var self = this;
    ajajOpt = this.ajaj.normalizeAjaxParameters( ajajOpt || {} );
    var oldOnResponse = ajajOpt.onResponse;
    ajajOpt.onResponse = function(resp,req) {
        var thisOpt = this;
        self.auth = undefined;
        if( WhAjaj.isFunction( self.onLogout ) ){
            try{ self.onLogout(); }
            catch(e){}
        }
        if( WhAjaj.isFunction(oldOnResponse) ) {
            oldOnResponse.apply(thisOpt,[resp,req]);
        }
    };
    this.sendCommand('/json/logout', undefined, ajajOpt );
};

/**
    Sends a HAI request to the server. /json/HAI is an alias /json/version.

    ajajOpt is an optional configuration object suitable for passing
    to sendCommand().
*/
FossilAjaj.prototype.HAI = function(ajajOpt) {
    this.sendCommand('/json/HAI', undefined, ajajOpt);
};


/**
    Sends a /json/whoami request. Updates this.auth to contain
    the login info, removing them if the response does not contain
    that data.
*/
FossilAjaj.prototype.whoami = function(ajajOpt) {
    var self = this;
    ajajOpt = this.ajaj.normalizeAjaxParameters( ajajOpt || {} );
    var oldOnResponse = ajajOpt.onResponse;
    ajajOpt.onResponse = function(resp,req) {
        var thisOpt = this;
        if( resp && resp.payload ){
            if(!self.auth || (self.auth.authToken!==resp.payload.authToken)){
                self.auth = resp.payload;
                if( WhAjaj.isFunction(self.onLogin) ){
                    self.onLogin();
                }
            }
        }
        else { delete self.auth; }
        if( WhAjaj.isFunction(oldOnResponse) ) {
            oldOnResponse.apply(thisOpt,[resp,req]);
        }
    };
    self.sendCommand('/json/whoami', undefined, ajajOpt);
};

/**
    EXPERIMENTAL concrete WhAjaj.Connector.sendImpl() implementation which
    uses Rhino to connect to a local fossil binary for input and output. Its
    signature and semantics are as described for
    WhAjaj.Connector.prototype.sendImpl(), with a few exceptions and
    additions:

    - It does not support timeouts or asynchronous mode.

    - The args.fossilBinary property must point to the local fossil binary
    (it need not be a complete path if fossil is in the $PATH). This
    function throws (without calling any request callbacks) if
    args.fossilBinary is not set. fossilBinary may be set on
    WhAjaj.Connector.options.ajax, in the FossilAjaj constructor call, as
    the ajax options parameter to any of the FossilAjaj.sendCommand() family
    of functions, or by setting
    aFossilAjajInstance.ajaj.options.fossilBinary on a specific
    FossilAjaj instance.

    - It uses the args.url field to create the "command" property of the
    request, constructs a request envelope, spawns a fossil process in JSON
    mode, feeds it the request envelope, and returns the response envelope
    via the same mechanisms defined for the HTTP-based implementations.

    The interface is otherwise compatible with the "normal"
    FossilAjaj.sendCommand() front-end (it is, however, fossil-specific, and
    not back-end agnostic like the WhAjaj.sendImpl() interface intends).


*/
FossilAjaj.rhinoLocalBinarySendImpl = function(request,args){
    var self = this;
    request = request || {};
    if(!args.fossilBinary){
        throw new Error("fossilBinary is not set on AJAX options!");
    }
    var url = args.url.split('?')[0].split(/\/+/);
    if(url.length>1){
        // 3x shift(): protocol, host, 'json' part of path
        request.command = (url.shift(),url.shift(),url.shift(), url.join('/'));
    }
    delete args.url;
    //print("rhinoLocalBinarySendImpl SENDING: "+WhAjaj.stringify(request));
    var json;
    try{
        var pargs = [args.fossilBinary, 'json', '--json-input', '-'];
        var p = java.lang.Runtime.getRuntime().exec(pargs);
        var outs = p.getOutputStream();
        var osr = new java.io.OutputStreamWriter(outs);
        var osb = new java.io.BufferedWriter(osr);

        json = JSON.stringify(request);
        osb.write(json,0, json.length);
        osb.close();
        var ins = p.getInputStream();
        var isr = new java.io.InputStreamReader(ins);
        var br = new java.io.BufferedReader(isr);
        var line;
        json = [];
        while( null !== (line=br.readLine())){
            json.push(line);
        }
        ins.close();
    }catch(e){
        args.errorMessage = e.toString();
        WhAjaj.Connector.sendHelper.onSendError.apply( self, [request, args] );
        return undefined;
    }
    json = json.join('');
    //print("READ IN JSON: "+json);
    WhAjaj.Connector.sendHelper.onSendSuccess.apply( self, [request, json, args] );
}/*rhinoLocalBinary*/

Deleted ajax/js/json2.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




























































































































































































































































































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
/*
    http://www.JSON.org/json2.js
    2009-06-29

    Public Domain.

    NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.

    See http://www.JSON.org/js.html

    This file creates a global JSON object containing two methods: stringify
    and parse.

        JSON.stringify(value, replacer, space)
            value       any JavaScript value, usually an object or array.

            replacer    an optional parameter that determines how object
                        values are stringified for objects. It can be a
                        function or an array of strings.

            space       an optional parameter that specifies the indentation
                        of nested structures. If it is omitted, the text will
                        be packed without extra whitespace. If it is a number,
                        it will specify the number of spaces to indent at each
                        level. If it is a string (such as '\t' or '&nbsp;'),
                        it contains the characters used to indent at each level.

            This method produces a JSON text from a JavaScript value.

            When an object value is found, if the object contains a toJSON
            method, its toJSON method will be called and the result will be
            stringified. A toJSON method does not serialize: it returns the
            value represented by the name/value pair that should be serialized,
            or undefined if nothing should be serialized. The toJSON method
            will be passed the key associated with the value, and this will be
            bound to the object holding the key.

            For example, this would serialize Dates as ISO strings.

                Date.prototype.toJSON = function (key) {
                    function f(n) {
                        // Format integers to have at least two digits.
                        return n < 10 ? '0' + n : n;
                    }

                    return this.getUTCFullYear()   + '-' +
                         f(this.getUTCMonth() + 1) + '-' +
                         f(this.getUTCDate())      + 'T' +
                         f(this.getUTCHours())     + ':' +
                         f(this.getUTCMinutes())   + ':' +
                         f(this.getUTCSeconds())   + 'Z';
                };

            You can provide an optional replacer method. It will be passed the
            key and value of each member, with this bound to the containing
            object. The value that is returned from your method will be
            serialized. If your method returns undefined, then the member will
            be excluded from the serialization.

            If the replacer parameter is an array of strings, then it will be
            used to select the members to be serialized. It filters the results
            such that only members with keys listed in the replacer array are
            stringified.

            Values that do not have JSON representations, such as undefined or
            functions, will not be serialized. Such values in objects will be
            dropped; in arrays they will be replaced with null. You can use
            a replacer function to replace those with JSON values.
            JSON.stringify(undefined) returns undefined.

            The optional space parameter produces a stringification of the
            value that is filled with line breaks and indentation to make it
            easier to read.

            If the space parameter is a non-empty string, then that string will
            be used for indentation. If the space parameter is a number, then
            the indentation will be that many spaces.

            Example:

            text = JSON.stringify(['e', {pluribus: 'unum'}]);
            // text is '["e",{"pluribus":"unum"}]'


            text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
            // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'

            text = JSON.stringify([new Date()], function (key, value) {
                return this[key] instanceof Date ?
                    'Date(' + this[key] + ')' : value;
            });
            // text is '["Date(---current time---)"]'


        JSON.parse(text, reviver)
            This method parses a JSON text to produce an object or array.
            It can throw a SyntaxError exception.

            The optional reviver parameter is a function that can filter and
            transform the results. It receives each of the keys and values,
            and its return value is used instead of the original value.
            If it returns what it received, then the structure is not modified.
            If it returns undefined then the member is deleted.

            Example:

            // Parse the text. Values that look like ISO date strings will
            // be converted to Date objects.

            myData = JSON.parse(text, function (key, value) {
                var a;
                if (typeof value === 'string') {
                    a =
/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
                    if (a) {
                        return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
                            +a[5], +a[6]));
                    }
                }
                return value;
            });

            myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
                var d;
                if (typeof value === 'string' &&
                        value.slice(0, 5) === 'Date(' &&
                        value.slice(-1) === ')') {
                    d = new Date(value.slice(5, -1));
                    if (d) {
                        return d;
                    }
                }
                return value;
            });


    This is a reference implementation. You are free to copy, modify, or
    redistribute.

    This code should be minified before deployment.
    See http://javascript.crockford.com/jsmin.html

    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
    NOT CONTROL.
*/

/*jslint evil: true */

/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
    call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
    getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
    lastIndex, length, parse, prototype, push, replace, slice, stringify,
    test, toJSON, toString, valueOf
*/

// Create a JSON object only if one does not already exist. We create the
// methods in a closure to avoid creating global variables.

var JSON = JSON || {};

(function () {

    function f(n) {
        // Format integers to have at least two digits.
        return n < 10 ? '0' + n : n;
    }

    if (typeof Date.prototype.toJSON !== 'function') {

        Date.prototype.toJSON = function (key) {

            return isFinite(this.valueOf()) ?
                   this.getUTCFullYear()   + '-' +
                 f(this.getUTCMonth() + 1) + '-' +
                 f(this.getUTCDate())      + 'T' +
                 f(this.getUTCHours())     + ':' +
                 f(this.getUTCMinutes())   + ':' +
                 f(this.getUTCSeconds())   + 'Z' : null;
        };

        String.prototype.toJSON =
        Number.prototype.toJSON =
        Boolean.prototype.toJSON = function (key) {
            return this.valueOf();
        };
    }

    var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
        escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
        gap,
        indent,
        meta = {    // table of character substitutions
            '\b': '\\b',
            '\t': '\\t',
            '\n': '\\n',
            '\f': '\\f',
            '\r': '\\r',
            '"' : '\\"',
            '\\': '\\\\'
        },
        rep;


    function quote(string) {

// If the string contains no control characters, no quote characters, and no
// backslash characters, then we can safely slap some quotes around it.
// Otherwise we must also replace the offending characters with safe escape
// sequences.

        escapable.lastIndex = 0;
        return escapable.test(string) ?
            '"' + string.replace(escapable, function (a) {
                var c = meta[a];
                return typeof c === 'string' ? c :
                    '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
            }) + '"' :
            '"' + string + '"';
    }


    function str(key, holder) {

// Produce a string from holder[key].

        var i,          // The loop counter.
            k,          // The member key.
            v,          // The member value.
            length,
            mind = gap,
            partial,
            value = holder[key];

// If the value has a toJSON method, call it to obtain a replacement value.

        if (value && typeof value === 'object' &&
                typeof value.toJSON === 'function') {
            value = value.toJSON(key);
        }

// If we were called with a replacer function, then call the replacer to
// obtain a replacement value.

        if (typeof rep === 'function') {
            value = rep.call(holder, key, value);
        }

// What happens next depends on the value's type.

        switch (typeof value) {
        case 'string':
            return quote(value);

        case 'number':

// JSON numbers must be finite. Encode non-finite numbers as null.

            return isFinite(value) ? String(value) : 'null';

        case 'boolean':
        case 'null':

// If the value is a boolean or null, convert it to a string. Note:
// typeof null does not produce 'null'. The case is included here in
// the remote chance that this gets fixed someday.

            return String(value);

// If the type is 'object', we might be dealing with an object or an array or
// null.

        case 'object':

// Due to a specification blunder in ECMAScript, typeof null is 'object',
// so watch out for that case.

            if (!value) {
                return 'null';
            }

// Make an array to hold the partial results of stringifying this object value.

            gap += indent;
            partial = [];

// Is the value an array?

            if (Object.prototype.toString.apply(value) === '[object Array]') {

// The value is an array. Stringify every element. Use null as a placeholder
// for non-JSON values.

                length = value.length;
                for (i = 0; i < length; i += 1) {
                    partial[i] = str(i, value) || 'null';
                }

// Join all of the elements together, separated with commas, and wrap them in
// brackets.

                v = partial.length === 0 ? '[]' :
                    gap ? '[\n' + gap +
                            partial.join(',\n' + gap) + '\n' +
                                mind + ']' :
                          '[' + partial.join(',') + ']';
                gap = mind;
                return v;
            }

// If the replacer is an array, use it to select the members to be stringified.

            if (rep && typeof rep === 'object') {
                length = rep.length;
                for (i = 0; i < length; i += 1) {
                    k = rep[i];
                    if (typeof k === 'string') {
                        v = str(k, value);
                        if (v) {
                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
                        }
                    }
                }
            } else {

// Otherwise, iterate through all of the keys in the object.

                for (k in value) {
                    if (Object.hasOwnProperty.call(value, k)) {
                        v = str(k, value);
                        if (v) {
                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
                        }
                    }
                }
            }

// Join all of the member texts together, separated with commas,
// and wrap them in braces.

            v = partial.length === 0 ? '{}' :
                gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
                        mind + '}' : '{' + partial.join(',') + '}';
            gap = mind;
            return v;
        }
    }

// If the JSON object does not yet have a stringify method, give it one.

    if (typeof JSON.stringify !== 'function') {
        JSON.stringify = function (value, replacer, space) {

// The stringify method takes a value and an optional replacer, and an optional
// space parameter, and returns a JSON text. The replacer can be a function
// that can replace values, or an array of strings that will select the keys.
// A default replacer method can be provided. Use of the space parameter can
// produce text that is more easily readable.

            var i;
            gap = '';
            indent = '';

// If the space parameter is a number, make an indent string containing that
// many spaces.

            if (typeof space === 'number') {
                for (i = 0; i < space; i += 1) {
                    indent += ' ';
                }

// If the space parameter is a string, it will be used as the indent string.

            } else if (typeof space === 'string') {
                indent = space;
            }

// If there is a replacer, it must be a function or an array.
// Otherwise, throw an error.

            rep = replacer;
            if (replacer && typeof replacer !== 'function' &&
                    (typeof replacer !== 'object' ||
                     typeof replacer.length !== 'number')) {
                throw new Error('JSON.stringify');
            }

// Make a fake root object containing our value under the key of ''.
// Return the result of stringifying the value.

            return str('', {'': value});
        };
    }


// If the JSON object does not yet have a parse method, give it one.

    if (typeof JSON.parse !== 'function') {
        JSON.parse = function (text, reviver) {

// The parse method takes a text and an optional reviver function, and returns
// a JavaScript value if the text is a valid JSON text.

            var j;

            function walk(holder, key) {

// The walk method is used to recursively walk the resulting structure so
// that modifications can be made.

                var k, v, value = holder[key];
                if (value && typeof value === 'object') {
                    for (k in value) {
                        if (Object.hasOwnProperty.call(value, k)) {
                            v = walk(value, k);
                            if (v !== undefined) {
                                value[k] = v;
                            } else {
                                delete value[k];
                            }
                        }
                    }
                }
                return reviver.call(holder, key, value);
            }


// Parsing happens in four stages. In the first stage, we replace certain
// Unicode characters with escape sequences. JavaScript handles many characters
// incorrectly, either silently deleting them, or treating them as line endings.

            cx.lastIndex = 0;
            if (cx.test(text)) {
                text = text.replace(cx, function (a) {
                    return '\\u' +
                        ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
                });
            }

// In the second stage, we run the text against regular expressions that look
// for non-JSON patterns. We are especially concerned with '()' and 'new'
// because they can cause invocation, and '=' because it can cause mutation.
// But just to be safe, we want to reject all unexpected forms.

// We split the second stage into 4 regexp operations in order to work around
// crippling inefficiencies in IE's and Safari's regexp engines. First we
// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
// replace all simple value tokens with ']' characters. Third, we delete all
// open brackets that follow a colon or comma or that begin the text. Finally,
// we look to see that the remaining characters are only whitespace or ']' or
// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.

            if (/^[\],:{}\s]*$/.
test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {

// In the third stage we use the eval function to compile the text into a
// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
// in JavaScript: it can begin a block or an object literal. We wrap the text
// in parens to eliminate the ambiguity.

                j = eval('(' + text + ')');

// In the optional fourth stage, we recursively walk the new structure, passing
// each name/value pair to a reviver function for possible transformation.

                return typeof reviver === 'function' ?
                    walk({'': j}, '') : j;
            }

// If the text is not JSON parseable, then a SyntaxError is thrown.

            throw new SyntaxError('JSON.parse');
        };
    }
}());

Deleted ajax/js/whajaj.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





































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
/**
    This file provides a JS interface into the core functionality of
    JSON-centric back-ends. It sends GET or JSON POST requests to
    a back-end and expects JSON responses. The exact semantics of
    the underlying back-end and overlying front-end are not its concern,
    and it leaves the interpretation of the data up to the client/server
    insofar as possible.

    All functionality is part of a class named WhAjaj, and that class
    acts as namespace for this framework.

    Author: Stephan Beal (http://wanderinghorse.net/home/stephan/)

    License: Public Domain

    This framework is directly derived from code originally found in
    http://code.google.com/p/jsonmessage, and later in
    http://whiki.wanderinghorse.net, where it contained quite a bit
    of application-specific logic. It was eventually (the 3rd time i
    needed it) split off into its own library to simplify inclusion
    into my many mini-projects.
*/


/**
    The WhAjaj function is primarily a namespace, and not intended
    to called or instantiated via the 'new' operator.
*/
function WhAjaj()
{
}

/** Returns a millisecond Unix Epoch timestamp. */
WhAjaj.msTimestamp = function()
{
    return (new Date()).getTime();
};

/** Returns a Unix Epoch timestamp (in seconds) in integer format.

    Reminder to self: (1.1 %1.2) evaluates to a floating-point value
    in JS, and thus this implementation is less than optimal.
*/
WhAjaj.unixTimestamp = function()
{
    var ts = (new Date()).getTime();
    return parseInt( ""+((ts / 1000) % ts) );
};

/**
    Returns true if v is-a Array instance.
*/
WhAjaj.isArray = function( v )
{
    return (v &&
            (v instanceof Array) ||
            (Object.prototype.toString.call(v) === "[object Array]")
            );
    /* Reminders to self:
        typeof [] == "object"
        toString.call([]) == "[object Array]"
        ([]).toString() == empty
    */
};

/**
    Returns true if v is-a Object instance.
*/
WhAjaj.isObject = function( v )
{
    return v &&
        (v instanceof Object) &&
        ('[object Object]' === Object.prototype.toString.apply(v) );
};

/**
    Returns true if v is-a Function instance.
*/
WhAjaj.isFunction = function(obj)
{
    return obj
    && (
    (obj instanceof Function)
    || ('function' === typeof obj)
    || ("[object Function]" === Object.prototype.toString.call(obj))
    )
    ;
};

/**
    Parses window.location.search-style string into an object
    containing key/value pairs of URL arguments (already urldecoded).

    If the str argument is not passed (arguments.length==0) then
    window.location.search.substring(1) is used by default. If
    neither str is passed in nor window exists then false is returned.

    On success it returns an Object containing the key/value pairs
    parsed from the string. Keys which have no value are treated
    has having the boolean true value.

    FIXME: for keys in the form "name[]", build an array of results,
    like PHP does.

*/
WhAjaj.processUrlArgs = function(str) {
    if( 0 === arguments.length ) {
        if( ('undefined' === typeof window) ||
            !window.location ||
            !window.location.search )  return false;
        else str = (''+window.location.search).substring(1);
    }
    if( ! str ) return false;
    str = (''+str).split(/#/,2)[0]; // remove #... to avoid it being added as part of the last value.
    var args = {};
    var sp = str.split(/&+/);
    var rx = /^([^=]+)(=(.+))?/;
    var i, m;
    for( i in sp ) {
        m = rx.exec( sp[i] );
        if( ! m ) continue;
        args[decodeURIComponent(m[1])] = (m[3] ? decodeURIComponent(m[3]) : true);
    }
    return args;
};

/**
    A simple wrapper around JSON.stringify(), using my own personal
    preferred values for the 2nd and 3rd parameters. To globally
    set its indentation level, assign WhAjaj.stringify.indent to
    an integer value (0 for no intendation).

    This function is intended only for human-readable output, not
    generic over-the-wire JSON output (where JSON.stringify(val) will
    produce smaller results).
*/
WhAjaj.stringify = function(val) {
    if( ! arguments.callee.indent ) arguments.callee.indent = 4;
    return JSON.stringify(val,0,arguments.callee.indent);
};

/**
    Each instance of this class holds state information for making
    AJAJ requests to a back-end system. While clients may use one
    "requester" object per connection attempt, for connections to the
    same back-end, using an instance configured for that back-end
    can simplify usage. This class is designed so that the actual
    connection-related details (i.e. _how_ it connects to the
    back-end) may be re-implemented to use a client's preferred
    connection mechanism (e.g. jQuery).

    The optional opt parameter may be an object with any (or all) of
    the properties documented for WhAjaj.Connector.options.ajax.
    Properties set here (or later via modification of the "options"
    property of this object) will be used in calls to
    WhAjaj.Connector.sendRequest(), and these override (normally) any
    options set in WhAjaj.Connector.options.ajax. Note that
    WhAjaj.Connector.sendRequest() _also_ takes an options object,
    and ones passed there will override, for purposes of that one
    request, any options passed in here or defined in
    WhAjaj.Connector.options.ajax. See WhAjaj.Connector.options.ajax
    and WhAjaj.Connector.prototype.sendRequest() for more details
    about the precedence of options.

    Sample usage:

    @code
    // Set up common connection-level options:
    var cgi = new WhAjaj.Connector({
        url: '/cgi-bin/my.cgi',
        timeout:10000,
        onResponse(resp,req) { alert(JSON.stringify(resp,0.4)); },
        onError(req,opt) {
            alert(opt.errorMessage);
        }
    });
    // Any of those options may optionally be set globally in
    // WhAjaj.Connector.options.ajax (onError(), beforeSend(), and afterSend()
    // are often easiest/most useful to set globally).

    // Get list of pages...
    cgi.sendRequest( null, {
        onResponse(resp,req){ alert(WhAjaj.stringify(resp)); }
    });
    @endcode

    For common request types, clients can add functions to this
    object which act as wrappers for backend-specific functionality. As
    a simple example:

    @code
    cgi.login = function(name,pw,ajajOpt) {
        this.sendRequest(
            {command:"json/login",
              name:name,
              password:pw
            }, ajajOpt );
    };
    @endcode

    TODOs:

    - Caching of page-load requests, with a configurable lifetime.

    - Use-cases like the above login() function are a tiny bit
    problematic to implement when each request has a different URL
    path (i know this from the whiki and fossil implementations).
    This is partly a side-effect of design descisions made back in
    the very first days of this code's life. i need to go through
    and see where i can bend those conventions a bit (where it won't
    break my other apps unduly).
*/
WhAjaj.Connector = function(opt)
{
    if(WhAjaj.isObject(opt)) this.options = opt;
    //TODO?: this.$cache = {};
};

/**
    The core options used by WhAjaj.Connector instances for performing
    network operations. These options can (and some _should_)
    be changed by a client application. They can also be changed
    on specific instances of WhAjaj.Connector, but for most applications
    it is simpler to set them here and not have to bother with configuring
    each WhAjaj.Connector instance. Apps which use multiple back-ends at one time,
    however, will need to customize each instance for a given back-end.
*/
WhAjaj.Connector.options = {
    /**
        A (meaningless) prefix to apply to WhAjaj.Connector-generated
        request IDs.
    */
    requestIdPrefix:'WhAjaj.Connector-',
    /**
        Default options for WhAjaj.Connector.sendRequest() connection
        parameters. This object holds only connection-related
        options and callbacks (all optional), and not options
        related to the required JSON structure of any given request.
        i.e. the page name used in a get-page request are not set
        here but are specified as part of the request object.

        These connection options are a "normalized form" of options
        often found in various AJAX libraries like jQuery,
        Prototype, dojo, etc. This approach allows us to swap out
        the real connection-related parts by writing a simple proxy
        which transforms our "normalized" form to the
        backend-specific form. For examples, see the various
        implementations stored in WhAjaj.Connector.sendImpls.

        The following callback options are, in practice, almost
        always set globally to some app-wide defaults:

        - onError() to report errors using a common mechanism.
        - beforeSend() to start a visual activity notification
        - afterSend() to disable the visual activity notification

        However, be aware that if any given WhAjaj.Connector instance is
        given its own before/afterSend callback then those will
        override these. Mixing shared/global and per-instance
        callbacks can potentially lead to confusing results if, e.g.,
        the beforeSend() and afterSend() functions have side-effects
        but are not used with their proper before/after partner.

        TODO: rename this to 'ajaj' (the name is historical). The
        problem with renaming it is is that the word 'ajax' is
        pretty prevelant in the source tree, so i can't globally
        swap it out.
    */
    ajax: {
        /**
            URL of the back-end server/CGI.
        */
        url: '/some/path',

        /**
            Connection method. Some connection-related functions might
            override any client-defined setting.

            Must be one of 'GET' or 'POST'. For custom connection
            implementation, it may optionally be some
            implementation-specified value.

            Normally the API can derive this value automatically - if the
            request uses JSON data it is POSTed, else it is GETted.
        */
        method:'GET',

        /**
            A hint whether to run the operation asynchronously or
            not. Not all concrete WhAjaj.Connector.sendImpl()
            implementations can support this. Interestingly, at
            least one popular AJAX toolkit does not document
            supporting _synchronous_ AJAX operations. All common
            browser-side implementations support async operation, but
            non-browser implementations might not.
        */
        asynchronous:true,

        /**
            A HTTP authentication login name for the AJAX
            connection. Not all concrete WhAjaj.Connector.sendImpl()
            implementations can support this.
        */
        loginName:undefined,

        /**
            An HTTP authentication login password for the AJAJ
            connection. Not all concrete WhAjaj.Connector.sendImpl()
            implementations can support this.
        */
        loginPassword:undefined,

        /**
            A connection timeout, in milliseconds, for establishing
            an AJAJ connection. Not all concrete
            WhAjaj.Connector.sendImpl() implementations can support this.
        */
        timeout:10000,

        /**
            If an AJAJ request receives JSON data from the back-end,
            that data is passed as a plain Object as the response
            parameter (exception: in jsonp mode it is passed a
            string (why???)). The initiating request object is
            passed as the second parameter, but clients can normally
            ignore it (only those which need a way to map specific
            requests to responses will need it). The 3rd parameter
            is the same as the 'this' object for the context of the
            callback, but is provided because the instance-level
            callbacks (set in (WhAjaj.Connector instance).callbacks,
            require it in some cases (because their 'this' is
            different!).

            Note that the response might contain error information
            which comes from the back-end. The difference between
            this error info and the info passed to the onError()
            callback is that this data indicates an
            application-level error, whereas onError() is used to
            report connection-level problems or when the backend
            produces non-JSON data (which, when not in jsonp mode,
            is unexpected and is as fatal to the request as a
            connection error).
        */
        onResponse: function(response, request, opt){},

        /**
            If an AJAX request fails to establish a connection or it
            receives non-JSON data from the back-end, this function
            is called (e.g. timeout error or host name not
            resolvable). It is passed the originating request and the
            "normalized" connection parameters used for that
            request. The connectOpt object "should" (or "might")
            have an "errorMessage" property which describes the
            nature of the problem.

            Clients will almost always want to replace the default
            implementation with something which integrates into
            their application.
        */
        onError: function(request, connectOpt)
        {
            alert('AJAJ request failed:\n'
                +'Connection information:\n'
                +JSON.stringify(connectOpt,0,4)
            );
        },

        /**
            Called before each connection attempt is made. Clients
            can use this to, e.g.,  enable a visual "network activity
            notification" for the user. It is passed the original
            request object and the normalized connection parameters
            for the request. If this function changes opt, those
            changes _are_ applied to the subsequent request. If this
            function throws, neither the onError() nor afterSend()
            callbacks are triggered and WhAjaj.Connector.sendImpl()
            propagates the exception back to the caller.
        */
        beforeSend: function(request,opt){},

        /**
            Called after an AJAJ connection attempt completes,
            regardless of success or failure. Passed the same
            parameters as beforeSend() (see that function for
            details).

            Here's an example of setting up a visual notification on
            ajax operations using jQuery (but it's also easy to do
            without jQuery as well):

            @code
            function startAjaxNotif(req,opt) {
                var me = arguments.callee;
                var c = ++me.ajaxCount;
                me.element.text( c + " pending AJAX operation(s)..." );
                if( 1 == c ) me.element.stop().fadeIn();
            }
            startAjaxNotif.ajaxCount = 0.
            startAjaxNotif.element = jQuery('#whikiAjaxNotification');

            function endAjaxNotif() {
                var c = --startAjaxNotif.ajaxCount;
                startAjaxNotif.element.text( c+" pending AJAX operation(s)..." );
                if( 0 == c ) startAjaxNotif.element.stop().fadeOut();
            }
            @endcode

            Set the beforeSend/afterSend properties to those
            functions to enable the notifications by default.
        */
        afterSend: function(request,opt){},

        /**
            If jsonp is a string then the WhAjaj-internal response
            handling code ASSUMES that the response contains a JSONP-style
            construct and eval()s it after afterSend() but before onResponse().
            In this case, onResponse() will get a string value for the response
            instead of a response object parsed from JSON.
        */
        jsonp:undefined,
        /**
            Don't use yet. Planned future option.
        */
        propagateExceptions:false
    }
};


/**
    WhAjaj.Connector.prototype.callbacks defines callbacks analog
    to the onXXX callbacks defined in WhAjaj.Connector.options.ajax,
    with two notable differences:

    1) these callbacks, if set, are called in addition to any
    request-specific callback. The intention is to allow a framework to set
    "framework-level" callbacks which should be called independently of the
    request-specific callbacks (without interfering with them, e.g.
    requiring special re-forwarding features).

    2) The 'this' object in these callbacks is the Connector instance
    associated with the callback, whereas the "other" onXXX form has its
    "ajax options" object as its this.

    When this API says that an onXXX callback will be called for a request,
    both the request's onXXX (if set) and this one (if set) will be called.
*/
WhAjaj.Connector.prototype.callbacks = {};
/**
    Instance-specific values for AJAJ-level properties (as opposed to
    application-level request properties). Options set here "override" those
    specified in WhAjaj.Connector.options.ajax and are "overridden" by
    options passed to sendRequest().
*/
WhAjaj.Connector.prototype.options = {};


/**
    Tries to find the given key in any of the following, returning
    the first match found: opt, this.options, WhAjaj.Connector.options.ajax.

    Returns undefined if key is not found.
*/
WhAjaj.Connector.prototype.derivedOption = function(key,opt) {
    var v = opt ? opt[key] : undefined;
    if( undefined !== v ) return v;
    else v = this.options[key];
    if( undefined !== v ) return v;
    else v = WhAjaj.Connector.options.ajax[key];
    return v;
};

/**
    Returns a unique string on each call containing a generic
    reandom request identifier string. This is not used by the core
    API but can be used by client code to generate unique IDs for
    each request (if needed).

    The exact format is unspecified and may change in the future.

    Request IDs can be used by clients to "match up" responses to
    specific requests if needed. In practice, however, they are
    seldom, if ever, needed. When passing several concurrent
    requests through the same response callback, it might be useful
    for some clients to be able to distinguish, possibly re-routing
    them through other handlers based on the originating request type.

    If this.options.requestIdPrefix or
    WhAjaj.Connector.options.requestIdPrefix is set then that text
    is prefixed to the returned string.
*/
WhAjaj.Connector.prototype.generateRequestId = function()
{
    if( undefined === arguments.callee.sequence )
    {
        arguments.callee.sequence = 0;
    }
    var pref = this.options.requestIdPrefix || WhAjaj.Connector.options.requestIdPrefix || '';
    return pref +
        WhAjaj.msTimestamp() +
        '/'+(Math.round( Math.random() * 100000000) )+
        ':'+(++arguments.callee.sequence);
};

/**
    Copies (SHALLOWLY) all properties in opt to this.options.
*/
WhAjaj.Connector.prototype.addOptions = function(opt) {
    var k, v;
    for( k in opt ) {
        if( ! opt.hasOwnProperty(k) ) continue /* proactive Prototype kludge! */;
        this.options[k] = opt[k];
    }
    return this.options;
};

/**
    An internal helper object which holds several functions intended
    to simplify the creation of concrete communication channel
    implementations for WhAjaj.Connector.sendImpl(). These operations
    take care of some of the more error-prone parts of ensuring that
    onResponse(), onError(), etc. callbacks are called consistently
    using the same rules.
*/
WhAjaj.Connector.sendHelper = {
    /**
        opt is assumed to be a normalized set of
        WhAjaj.Connector.sendRequest() options. This function
        creates a url by concatenating opt.url and some form of
        opt.urlParam.

        If opt.urlParam is an object or string then it is appended
        to the url. An object is assumed to be a one-dimensional set
        of simple (urlencodable) key/value pairs, and not larger
        data structures. A string value is assumed to be a
        well-formed, urlencoded set of key/value pairs separated by
        '&' characters.

        The new/normalized URL is returned (opt is not modified). If
        opt.urlParam is not set then opt.url is returned (or an
        empty string if opt.url is itself a false value).

        TODO: if opt is-a Object and any key points to an array,
        build up a list of keys in the form "keyname[]". We could
        arguably encode sub-objects like "keyname[subkey]=...", but
        i don't know if that's conventions-compatible with other
        frameworks.
    */
    normalizeURL: function(opt) {
        var u = opt.url || '';
        if( opt.urlParam ) {
            var addQ = (u.indexOf('?') >= 0) ? false : true;
            var addA = addQ ? false : ((u.indexOf('&')>=0) ? true : false);
            var tail = '';
            if( WhAjaj.isObject(opt.urlParam) ) {
                var li = [], k;
                for( k in opt.urlParam) {
                    li.push( k+'='+encodeURIComponent( opt.urlParam[k] ) );
                }
                tail = li.join('&');
            }
            else if( 'string' === typeof opt.urlParam ) {
                tail = opt.urlParam;
            }
            u = u + (addQ ? '?' : '') + (addA ? '&' : '') + tail;
        }
        return u;
    },
    /**
        Should be called by WhAjaj.Connector.sendImpl()
        implementations after a response has come back. This
        function takes care of most of ensuring that framework-level
        conventions involving WhAjaj.Connector.options.ajax
        properties are followed.

        The request argument must be the original request passed to
        the sendImpl() function. It may legally be null for GET requests.

        The opt object should be the normalized AJAX options used
        for the connection.

        The resp argument may be either a plain Object or a string
        (in which case it is assumed to be JSON).

        The 'this' object for this call MUST be a WhAjaj.Connector
        instance in order for callback processing to work properly.

        This function takes care of the following:

        - Calling opt.afterSend()

        - If resp is a string, de-JSON-izing it to an object.

        - Calling opt.onResponse()

        - Calling opt.onError() in several common (potential) error
        cases.

        - If resp is-a String and opt.jsonp then resp is assumed to be
        a JSONP-form construct and is eval()d BEFORE opt.onResponse()
        is called. It is arguable to eval() it first, but the logic
        integrates better with the non-jsonp handler.

        The sendImpl() should return immediately after calling this.

        The sendImpl() must call only one of onSendSuccess() or
        onSendError(). It must call one of them or it must implement
        its own response/error handling, which is not recommended
        because getting the documented semantics of the
        onError/onResponse/afterSend handling correct can be tedious.
    */
    onSendSuccess:function(request,resp,opt) {
        var cb = this.callbacks || {};
        if( WhAjaj.isFunction(cb.afterSend) ) {
            try {cb.afterSend( request, opt );}
            catch(e){}
        }
        if( WhAjaj.isFunction(opt.afterSend) ) {
            try {opt.afterSend( request, opt );}
            catch(e){}
        }
        function doErr(){
            if( WhAjaj.isFunction(cb.onError) ) {
                try {cb.onError( request, opt );}
                catch(e){}
            }
            if( WhAjaj.isFunction(opt.onError) ) {
                try {opt.onError( request, opt );}
                catch(e){}
            }
        }
        if( ! resp ) {
            opt.errorMessage = "Sending of request succeeded but returned no data!";
            doErr();
            return false;
        }

        if( 'string' === typeof resp ) {
            try {
                resp = opt.jsonp ? eval(resp) : JSON.parse(resp);
            } catch(e) {
                opt.errorMessage = e.toString();
                doErr();
                return;
            }
        }
        try {
            if( WhAjaj.isFunction( cb.onResponse  ) ) {
                cb.onResponse( resp, request, opt );
            }
            if( WhAjaj.isFunction( opt.onResponse  ) ) {
                opt.onResponse( resp, request, opt );
            }
            return true;
        }
        catch(e) {
            opt.errorMessage = "Exception while handling inbound JSON response:\n"
                + e
                +"\nOriginal response data:\n"+JSON.stringify(resp,0,2)
                ;
            ;
            doErr();
            return false;
        }
    },
   /**
        Should be called by sendImpl() implementations after a response
        has failed to connect (e.g. could not resolve host or timeout
        reached). This function takes care of most of ensuring that
        framework-level conventions involving WhAjaj.Connector.options.ajax
        properties are followed.

        The request argument must be the original request passed to
        the sendImpl() function. It may legally be null for GET
        requests.

        The 'this' object for this call MUST be a WhAjaj.Connector
        instance in order for callback processing to work properly.

        The opt object should be the normalized AJAX options used
        for the connection. By convention, the caller of this
        function "should" set opt.errorMessage to contain a
        human-readable description of the error.

        The sendImpl() should return immediately after calling this. The
        return value from this function is unspecified.
    */
    onSendError: function(request,opt) {
        var cb = this.callbacks || {};
        if( WhAjaj.isFunction(cb.afterSend) ) {
            try {cb.afterSend( request, opt );}
            catch(e){}
        }
        if( WhAjaj.isFunction(opt.afterSend) ) {
            try {opt.afterSend( request, opt );}
            catch(e){}
        }
        if( WhAjaj.isFunction( cb.onError ) ) {
            try {cb.onError( request, opt );}
            catch(e) {/*ignore*/}
        }
        if( WhAjaj.isFunction( opt.onError ) ) {
            try {opt.onError( request, opt );}
            catch(e) {/*ignore*/}
        }
    }
};

/**
    WhAjaj.Connector.sendImpls holds several concrete
    implementations of WhAjaj.Connector.prototype.sendImpl(). To use
    a specific implementation by default assign
    WhAjaj.Connector.prototype.sendImpl to one of these functions.

    The functions defined here require that the 'this' object be-a
    WhAjaj.Connector instance.

    Historical notes:

    a) We once had an implementation based on Prototype, but that
    library just pisses me off (they change base-most types'
    prototypes, introducing side-effects in client code which
    doesn't even use Prototype). The Prototype version at the time
    had a serious toJSON() bug which caused empty arrays to
    serialize as the string "[]", which broke a bunch of my code.
    (That has been fixed in the mean time, but i don't use
    Prototype.)

    b) We once had an implementation for the dojo library,

    If/when the time comes to add Prototype/dojo support, we simply
    need to port:

    http://code.google.com/p/jsonmessage/source/browse/trunk/lib/JSONMessage/JSONMessage.inc.js

    (search that file for "dojo" and "Prototype") to this tree. That
    code is this code's generic grandfather and they are still very
    similar, so a port is trivial.

*/
WhAjaj.Connector.sendImpls = {
    /**
        This is a concrete implementation of
        WhAjaj.Connector.prototype.sendImpl() which uses the
        environment's native XMLHttpRequest class to send whiki
        requests and fetch the responses.

        The only argument must be a connection properties object, as
        constructed by WhAjaj.Connector.normalizeAjaxParameters().

        If window.firebug is set then window.firebug.watchXHR() is
        called to enable monitoring of the XMLHttpRequest object.

        This implementation honors the loginName and loginPassword
        connection parameters.

        Returns the XMLHttpRequest object.

        This implementation requires that the 'this' object be-a
        WhAjaj.Connector.

        This implementation uses setTimeout() to implement the
        timeout support, and thus the JS engine must provide that
        functionality.
    */
    XMLHttpRequest: function(request, args)
    {
        var json = WhAjaj.isObject(request) ? JSON.stringify(request) : request;
        var xhr = new XMLHttpRequest();
        var startTime = (new Date()).getTime();
        var timeout = args.timeout || 10000/*arbitrary!*/;
        var hitTimeout = false;
        var done = false;
        var tmid /* setTimeout() ID */;
        var whself = this;
        function handleTimeout()
        {
            hitTimeout = true;
            if( ! done )
            {
                var now = (new Date()).getTime();
                try { xhr.abort(); } catch(e) {/*ignore*/}
                // see: http://www.w3.org/TR/XMLHttpRequest/#the-abort-method
                args.errorMessage = "Timeout of "+timeout+"ms reached after "+(now-startTime)+"ms during AJAX request.";
                WhAjaj.Connector.sendHelper.onSendError.apply( whself, [request, args] );
            }
            return;
        }
        function onStateChange()
        { // reminder to self: apparently 'this' is-not-a XHR :/
            if( hitTimeout )
            { /* we're too late - the error was already triggered. */
                return;
            }

            if( 4 == xhr.readyState )
            {
                done = true;
                if( tmid )
                {
                    clearTimeout( tmid );
                    tmid = null;
                }
                if( (xhr.status >= 200) && (xhr.status < 300) )
                {
                    WhAjaj.Connector.sendHelper.onSendSuccess.apply( whself, [request, xhr.responseText, args] );
                    return;
                }
                else
                {
                    if( undefined === args.errorMessage )
                    {
                        args.errorMessage = "Error sending a '"+args.method+"' AJAX request to "
                                +"["+args.url+"]: "
                                +"Status text=["+xhr.statusText+"]"
                            ;
                        WhAjaj.Connector.sendHelper.onSendError.apply( whself, [request, args] );
                    }
                    else { /*maybe it was was set by the timeout handler. */ }
                    return;
                }
            }
        };

        xhr.onreadystatechange = onStateChange;
        if( ('undefined'!==(typeof window)) && ('firebug' in window) && ('watchXHR' in window.firebug) )
        { /* plug in to firebug lite's XHR monitor... */
            window.firebug.watchXHR( xhr );
        }
        try
        {
            //alert( JSON.stringify( args  ));
            function xhrOpen()
            {
                if( ('loginName' in args) && args.loginName )
                {
                    xhr.open( args.method, args.url, args.asynchronous, args.loginName, args.loginPassword );
                }
                else
                {
                    xhr.open( args.method, args.url, args.asynchronous  );
                }
            }
            if( json && ('POST' ===  args.method.toUpperCase()) )
            {
                xhrOpen();
                xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8");
                // Google Chrome warns that it refuses to set these
                // "unsafe" headers (his words, not mine):
                // xhr.setRequestHeader("Content-length", json.length);
                // xhr.setRequestHeader("Connection", "close");
                xhr.send( json );
            }
            else /* assume GET */
            {
                xhrOpen();
                xhr.send(null);
            }
            tmid = setTimeout( handleTimeout, timeout );
            return xhr;
        }
        catch(e)
        {
            args.errorMessage = e.toString();
            WhAjaj.Connector.sendHelper.onSendError.apply( whself, [request, args] );
            return undefined;
        }
    }/*XMLHttpRequest()*/,
    /**
        This is a concrete implementation of
        WhAjaj.Connector.prototype.sendImpl() which uses the jQuery
        AJAX API to send requests and fetch the responses.

        The first argument may be either null/false, an Object
        containing toJSON-able data to post to the back-end, or such an
        object in JSON string form.

        The second argument must be a connection properties object, as
        constructed by WhAjaj.Connector.normalizeAjaxParameters().

        If window.firebug is set then window.firebug.watchXHR() is
        called to enable monitoring of the XMLHttpRequest object.

        This implementation honors the loginName and loginPassword
        connection parameters.

        Returns the XMLHttpRequest object.

        This implementation requires that the 'this' object be-a
        WhAjaj.Connector.
    */
    jQuery:function(request,args)
    {
        var data = request || undefined;
        var whself = this;
        if( data ) {
            if('string'!==typeof data) {
                try {
                    data = JSON.stringify(data);
                }
                catch(e) {
                    WhAjaj.Connector.sendHelper.onSendError.apply( whself, [request, args] );
                    return;
                }
            }
        }
        var ajopt = {
            url: args.url,
            data: data,
            type: args.method,
            async: args.asynchronous,
            password: (undefined !== args.loginPassword) ? args.loginPassword : undefined,
            username: (undefined !== args.loginName) ? args.loginName : undefined,
            contentType: 'application/json; charset=utf-8',
            error: function(xhr, textStatus, errorThrown)
            {
                //this === the options for this ajax request
                args.errorMessage = "Error sending a '"+ajopt.type+"' request to ["+ajopt.url+"]: "
                        +"Status text=["+textStatus+"]"
                        +(errorThrown ? ("Error=["+errorThrown+"]") : "")
                    ;
                WhAjaj.Connector.sendHelper.onSendError.apply( whself, [request, args] );
            },
            success: function(data)
            {
                WhAjaj.Connector.sendHelper.onSendSuccess.apply( whself, [request, data, args] );
            },
            /* Set dataType=text instead of json to keep jQuery from doing our carefully
                written response handling for us.
            */
            dataType: 'text'
        };
        if( undefined !== args.timeout )
        {
            ajopt.timeout = args.timeout;
        }
        try
        {
            return jQuery.ajax(ajopt);
        }
        catch(e)
        {
            args.errorMessage = e.toString();
            WhAjaj.Connector.sendHelper.onSendError.apply( whself, [request, args] );
            return undefined;
        }
    }/*jQuery()*/,
    /**
        This is a concrete implementation of
        WhAjaj.Connector.prototype.sendImpl() which uses the rhino
        Java API to send requests and fetch the responses.

        Limitations vis-a-vis the interface:

        - timeouts are not supported.

        - asynchronous mode is not supported because implementing it
        requires the ability to kill a running thread (which is deprecated
        in the Java API).

        TODOs:

        - add socket timeouts.

        - support HTTP proxy.

        The Java APIs support this, it just hasn't been added here yet.
    */
    rhino:function(request,args)
    {
        var self = this;
        var data = request || undefined;
        if( data ) {
            if('string'!==typeof data) {
                try {
                    data = JSON.stringify(data);
                }
                catch(e) {
                    WhAjaj.Connector.sendHelper.onSendError.apply( self, [request, args] );
                    return;
                }
            }
        }
        var url;
        var con;
        var IO = new JavaImporter(java.io);
        var wr;
        var rd, ln, json = [];
        function setIncomingCookies(list){
            if(!list || !list.length) return;
            if( !self.cookies ) self.cookies = {};
            var k, v, i;
            for( i = 0; i < list.length; ++i ){
                v = list[i].split('=',2);
                k = decodeURIComponent(v[0])
                v = v[0] ? decodeURIComponent(v[0].split(';',2)[0]) : null;
                //print("RECEIVED COOKIE: "+k+"="+v);
                if(!v) {
                    delete self.cookies[k];
                    continue;
                }else{
                    self.cookies[k] = v;
                }
            }
        };
        function setOutboundCookies(conn){
            if(!self.cookies) return;
            var k, v;
            for( k in self.cookies ){
                if(!self.cookies.hasOwnProperty(k)) continue /*kludge for broken JS libs*/;
                v = self.cookies[k];
                conn.addRequestProperty("Cookie", encodeURIComponent(k)+'='+encodeURIComponent(v));
                //print("SENDING COOKIE: "+k+"="+v);
            }
        };
        try{
            url = new java.net.URL( args.url )
            con = url.openConnection(/*FIXME: add proxy support!*/);
            con.setRequestProperty("Accept-Charset","utf-8");
            setOutboundCookies(con);
            if(data){
                con.setRequestProperty("Content-Type","application/json; charset=utf-8");
                con.setDoOutput( true );
                wr = new IO.OutputStreamWriter(con.getOutputStream())
                wr.write(data);
                wr.flush();
                wr.close();
                wr = null;
                //print("POSTED: "+data);
            }
            rd = new IO.BufferedReader(new IO.InputStreamReader(con.getInputStream()));
            //var skippedHeaders = false;
            while ((line = rd.readLine()) !== null) {
                //print("LINE: "+line);
                //if(!line.length && !skippedHeaders){
                //    skippedHeaders = true;
                // json = [];
                //    continue;
                //}
                json.push(line);
            }
            setIncomingCookies(con.getHeaderFields().get("Set-Cookie"));
        }catch(e){
            args.errorMessage = e.toString();
            WhAjaj.Connector.sendHelper.onSendError.apply( self, [request, args] );
            return undefined;
        }
        try { if(wr) wr.close(); } catch(e) { /*ignore*/}
        try { if(rd) rd.close(); } catch(e) { /*ignore*/}
        json = json.join('');
        //print("READ IN JSON: "+json);
        WhAjaj.Connector.sendHelper.onSendSuccess.apply( self, [request, json, args] );
    }/*rhino*/
};

/**
    An internal function which takes an object containing properties
    for a WhAjaj.Connector network request. This function creates a new
    object containing a superset of the properties from:

    a) opt
    b) this.options
    c) WhAjaj.Connector.options.ajax

    in that order, using the first one it finds.

    All non-function properties are _deeply_ copied via JSON cloning
    in order to prevent accidental "cross-request pollenation" (been
    there, done that). Functions cannot be cloned and are simply
    copied by reference.

    This function throws if JSON-copying one of the options fails
    (e.g. due to cyclic data structures).

    Reminder to self: this function does not "normalize" opt.urlParam
    by encoding it into opt.url, mainly for historical reasons, but
    also because that behaviour was specifically undesirable in this
    code's genetic father.
*/
WhAjaj.Connector.prototype.normalizeAjaxParameters = function (opt)
{
    var rc = {};
    function merge(k,v)
    {
        if( rc.hasOwnProperty(k) ) return;
        else if( WhAjaj.isFunction(v) ) {}
        else if( WhAjaj.isObject(v) ) v = JSON.parse( JSON.stringify(v) );
        rc[k]=v;
    }
    function cp(obj) {
        if( ! WhAjaj.isObject(obj) ) return;
        var k;
        for( k in obj ) {
            if( ! obj.hasOwnProperty(k) ) continue /* i will always hate the Prototype designers for this. */;
            merge(k, obj[k]);
        }
    }
    cp( opt );
    cp( this.options );
    cp( WhAjaj.Connector.options.ajax );
    // no, not here: rc.url = WhAjaj.Connector.sendHelper.normalizeURL(rc);
    return rc;
};

/**
    This is the generic interface for making calls to a back-end
    JSON-producing request handler. It is a simple wrapper around
    WhAjaj.Connector.prototype.sendImpl(), which just normalizes the
    connection options for sendImpl() and makes sure that
    opt.beforeSend() is (possibly) called.

    The request parameter must either be false/null/empty or a
    fully-populated JSON-able request object (which will be sent as
    unencoded application/json text), depending on the type of
    request being made. It is never semantically legal (in this API)
    for request to be a string/number/true/array value. As a rule,
    only POST requests use the request data. GET requests should
    encode their data in opt.url or opt.urlParam (see below).

    opt must contain the network-related parameters for the request.
    Paramters _not_ set in opt are pulled from this.options or
    WhAjaj.Connector.options.ajax (in that order, using the first
    value it finds). Thus the set of connection-level options used
    for the request are a superset of those various sources.

    The "normalized" (or "superimposed") opt object's URL may be
    modified before the request is sent, as follows:

    if opt.urlParam is a string then it is assumed to be properly
    URL-encoded parameters and is appended to the opt.url. If it is
    an Object then it is assumed to be a one-dimensional set of
    key/value pairs with simple values (numbers, strings, booleans,
    null, and NOT objects/arrays). The keys/values are URL-encoded
    and appended to the URL.

    The beforeSend() callback (see below) can modify the options
    object before the request attempt is made.

    The callbacks in the normalized opt object will be triggered as
    follows (if they are set to Function values):

    - beforeSend(request,opt) will be called before any network
    processing starts. If beforeSend() throws then no other
    callbacks are triggered and this function propagates the
    exception. This function is passed normalized connection options
    as its second parameter, and changes this function makes to that
    object _will_ be used for the pending connection attempt.

    - onError(request,opt) will be called if a connection to the
    back-end cannot be established. It will be passed the original
    request object (which might be null, depending on the request
    type) and the normalized options object. In the error case, the
    opt object passed to onError() "should" have a property called
    "errorMessage" which contains a description of the problem.

    - onError(request,opt) will also be called if connection
    succeeds but the response is not JSON data.

    - onResponse(response,request) will be called if the response
    returns JSON data. That data might hold an error response code -
    clients need to check for that. It is passed the response object
    (a plain object) and the original request object.

    - afterSend(request,opt) will be called directly after the
    AJAX request is finished, before onError() or onResonse() are
    called. Possible TODO: we explicitly do NOT pass the response to
    this function in order to keep the line between the responsibilities
    of the various callback clear (otherwise this could be used the same
    as onResponse()). In practice it would sometimes be useful have the
    response passed to this function, mainly for logging/debugging
    purposes.

    The return value from this function is meaningless because
    AJAX operations tend to take place asynchronously.

*/
WhAjaj.Connector.prototype.sendRequest = function(request,opt)
{
    if( !WhAjaj.isFunction(this.sendImpl) )
    {
        throw new Error("This object has no sendImpl() member function! I don't know how to send the request!");
    }
    var ex = false;
    var av = Array.prototype.slice.apply( arguments, [0] );

    /**
        FIXME: how to handle the error, vis-a-vis- the callbacks, if
        normalizeAjaxParameters() throws? It can throw if
        (de)JSON-izing fails.
    */
    var norm = this.normalizeAjaxParameters( WhAjaj.isObject(opt) ? opt : {} );
    norm.url = WhAjaj.Connector.sendHelper.normalizeURL(norm);
    if( ! request ) norm.method = 'GET';
    var cb = this.callbacks || {};
    if( this.callbacks && WhAjaj.isFunction(this.callbacks.beforeSend) ) {
        this.callbacks.beforeSend( request, norm );
    }
    if( WhAjaj.isFunction(norm.beforeSend) ){
        norm.beforeSend( request, norm );
    }
    //alert( WhAjaj.stringify(request)+'\n'+WhAjaj.stringify(norm));
    try { this.sendImpl( request, norm ); }
    catch(e) { ex = e; }
    if(ex) throw ex;
};

/**
    sendImpl() holds a concrete back-end connection implementation. It
    can be replaced with a custom implementation if one follows the rules
    described throughout this API. See WhAjaj.Connector.sendImpls for
    the concrete implementations included with this API.
*/
//WhAjaj.Connector.prototype.sendImpl = WhAjaj.Connector.sendImpls.XMLHttpRequest;
//WhAjaj.Connector.prototype.sendImpl = WhAjaj.Connector.sendImpls.rhino;
//WhAjaj.Connector.prototype.sendImpl = WhAjaj.Connector.sendImpls.jQuery;

if( 'undefined' !== typeof jQuery ){
    WhAjaj.Connector.prototype.sendImpl = WhAjaj.Connector.sendImpls.jQuery;
}
else {
    WhAjaj.Connector.prototype.sendImpl = WhAjaj.Connector.sendImpls.XMLHttpRequest;
}

Deleted ajax/wiki-editor.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





























































































































































































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

<head>
    <title>Fossil/JSON Wiki Editor Prototype</title>
    <meta http-equiv="content-type" content="text/html;charset=utf-8" />
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
    <script type="text/javascript" src="js/whajaj.js"></script>
    <script type="text/javascript" src="js/fossil-ajaj.js"></script>

<style type='text/css'>
th {
  text-align: left;
  background-color: #ececec;
}

.dangerWillRobinson {
    background-color: yellow;
}

.wikiPageLink {
    text-decoration: underline;
}
</style>

<script type='text/javascript'>
WhAjaj.Connector.options.ajax.url =
/*
    Change this to your CGI/server root path:
*/
     //'http://fjson/cgi-bin/fossil.cgi'
     //'/repos/fossil-sgb/json.cgi'
    '/cgi-bin/fossil-json.cgi'
     ;
var TheApp = {
      response:null,
      sessionID:null,
      jqe:{}/*jqe==jQuery Elements*/,
      ajaxCount:0,
      cgi: new FossilAjaj(),
      pages:{}
};


TheApp.startAjaxNotif = function()
{
    ++this.ajaxCount;
    TheApp.jqe.responseContainer.removeClass('dangerWillRobinson');
    this.jqe.ajaxNotification.attr( 'title', this.ajaxCount+" pending AJAX operation(s)..." );
    if( 1 == this.ajaxCount ) this.jqe.ajaxNotification.fadeIn();
};

TheApp.endAjaxNotif = function()
{
    --this.ajaxCount;
    this.jqe.ajaxNotification.attr( 'title', this.ajaxCount+" pending AJAX operation(s)..." );
    if( 0 == this.ajaxCount ) this.jqe.ajaxNotification.fadeOut();
};

TheApp.responseContainsError = function(resp) {
    if( !resp || resp.resultCode ) {
        //alert("Error response:\n"+JSON.stringify(resp,0,4));
        TheApp.jqe.taResponse.val( "RESPONSE CONTAINS ERROR INFO:\n"+WhAjaj.stringify(resp) );
        //TheApp.jqe.responseContainer.css({backgroundColor:'yellow'});
        //TheApp.jqe.responseContainer.addClass('dangerWillRobinson');
        TheApp.jqe.responseContainer.flash( '255,0,0', 1500 );
        return true;
    }
    return false;
};


TheApp.sendRequest = function() {
    var path = this.jqe.textPath.val();
    var self = this;
    var data = this.jqe.pageListArea.val();
    var doPost = (data && data.length);
    var req;
    if( doPost ) try {
        req = JSON.parse(data);
    }
    catch(e) {
        TheApp.jqe.taResponse.val("Request is not valid JSON.\n"+e);
        return;
    }
    if( req ) {
        req.requestId = this.cgi.generateRequestId();
    }
    var self = this;
    var opt = {
        url: WhAjaj.Connector.options.ajax.url + path,
        method: doPost ? 'POST' : 'GET'
    };
    this.cgi.sendRequest( req, opt );
};
jQuery.fn.animateHighlight = function(highlightColor, duration) {
    var highlightBg = highlightColor || "#FFFF9C";
    var animateMs = duration || 1500;
    var originalBg = this.css("backgroundColor");
    this.stop().css("background-color", highlightBg).animate({backgroundColor: originalBg}, animateMs);
};
jQuery.fn.flash = function( color, duration )
{
    var current = this.css( 'color' );
    this.animate( { color: 'rgb(' + color + ')' }, duration / 2);
    this.animate( { color: current }, duration / 2 );
};

jQuery(document).ready(function(){
    var ids = [
        'btnSend',
        'ajaxNotification',
        'currentAuthToken',
        'responseContainer',
        'spanPageName',
        'pageListArea',
        'taPageContent',
        'taResponse',
        'textPath', // list of HTML element IDs we use often.
        'timer'
    ];
    var i, k;
    for( i = 0; i < ids.length; ++i ) {
        k = ids[i];
        TheApp.jqe[k] = jQuery('#'+k);
    }
    TheApp.jqe.textPath.
        keyup(function(event){
            if(event.keyCode == 13){
                TheApp.sendRequest();
            }
        });
    TheApp.timer = {
        _tstart:0,_tend:0,duration:0,
        start:function(){
            this._tstart = (new Date()).getTime();
        },
        end:function(){
            this._tend = (new Date()).getTime();
            return this.duration = this._tend - this._tstart;
        }
    };
    var ajcb = TheApp.cgi.ajaj.callbacks;
    ajcb.beforeSend = TheApp.beforeSend = function(req,opt) {
        TheApp.timer.start();
        var val =
            req ?
            (('string'===typeof req) ? req : WhAjaj.stringify(req))
            : '';
        TheApp.jqe.taResponse.val('');
        TheApp.startAjaxNotif();
    };
    ajcb.afterSend = TheApp.afterSend = function(req,opt) {
        TheApp.timer.end();
        TheApp.endAjaxNotif();
        TheApp.jqe.timer.text( "(Round-trip time: "+TheApp.timer.duration+'ms)' );
    };
    ajcb.onResponse = TheApp.onResponse = function(resp,req) {
        var val;
        try {
            val = WhAjaj.stringify(resp);
        }
        catch(e) {
            val = WhAjaj.stringify(e)
        }
        if(resp.resultCode){
            alert("Response contains error info:\n"+val);
        }
        TheApp.jqe.taResponse.val( val );
    };
    ajcb.onError = function(req,opt) {
        TheApp.jqe.taResponse.val( "ERROR SENDING REQUEST:\n"+WhAjaj.stringify(opt) );
    };

    TheApp.jqe.taPageContent.blur(function(){
        var p = TheApp.currentPage;
        if(! p ) return;
        p.content = TheApp.jqe.taPageContent.val();
    });

    TheApp.cgi.onLogin = function(){
      TheApp.jqe.taResponse.val( "Logged in: "+WhAjaj.stringify(this.auth));
      TheApp.jqe.currentAuthToken.text("Logged in: "+WhAjaj.stringify(this.auth));
    };
    TheApp.cgi.onLogout = function(){
      TheApp.jqe.taResponse.val( "Logged out!" );
      TheApp.jqe.currentAuthToken.text("");
    };

    TheApp.showPage = function(name){
        function doShow(page){
            TheApp.currentPage = page;
            TheApp.jqe.spanPageName.text('('+page.name+')');
            TheApp.jqe.taPageContent.val(page.content);
        }
        var p = ('object' === typeof name) ? name : TheApp.pages[name];
        if(('object' === typeof p) && p.content) {
            doShow(p);
            return;
        }
        TheApp.cgi.sendCommand('/json/wiki/get',{
            name:name
        },{
            onResponse:function(resp,req){
                TheApp.onResponse(resp,req);
                if(resp.resultCode) return;
                var p = resp.payload;
                doShow( TheApp.pages[p.name] = p );
            }
        });
    };
    TheApp.refreshPageListView = function(){
        var list = (function(){
            var k, v, li = [];
            for( k in TheApp.pages ){
                if(!TheApp.pages.hasOwnProperty(k)) continue;
                li.push(k);
            }
            return li;
        })();
        var i, p, a, tgt = TheApp.jqe.pageListArea;
        tgt.text('');
        function makeLink(name){
            var link = jQuery('<span></span>');
            link.text(name);
            link.addClass('wikiPageLink');
            link.click(function(e){
                TheApp.showPage(name);
                e.preventDefault();
                return false;
            });
            return link;
        }
        list.sort();
        for( i = 0; i < list.length; ++i ){
            tgt.append(makeLink(list[i]));
            tgt.append('<br/>');
        }
    };

    TheApp.loadPageList = function(){
        TheApp.cgi.sendCommand('/json/wiki/list',null,{
            onResponse:function(resp,req){
                TheApp.onResponse(resp,req);
                if(resp.resultCode) return;
                var i, v, p, ar = resp.payload;
                for( i = 0; i < ar.length; ++i ){
                    v = ar[i];
                    p = TheApp.pages[v];
                    if( !p ) TheApp.pages[v] = {name:v};
                }
                TheApp.refreshPageListView();
            }
        });
        return false /*for click handlers*/;
    }

    TheApp.savePage = function(p){
        p = p || TheApp.currentPage;
        if( 'object' !== typeof p ){
            p = TheApp.pages[p];
        }
        if('object' !== typeof p){
            alert("savePage() argument is not a page object or known page name.");
        }
        TheApp.pages[p.name] = p;
        p.content = TheApp.jqe.taPageContent.val();
        var req = {
            name:p.name,
            content:p.content
        };
        if(! confirm("Really save wiki page ["+p.name+"]?") ) return;
        TheApp.cgi.sendCommand('/json/wiki/'+(p.isNew?'create':'save'),req,{
            onResponse:function(resp,req){
                TheApp.onResponse(resp,req);
                if(resp.resultCode) return;
                delete p.isNew;
                p.timestamp = resp.payload.timestamp;
            }
        });

    };

    TheApp.createNewPage = function(){
        var name = prompt("New page name?");
        if(!name) return;
        var p = {
            name:name,
            content:"New, empty page.",
            isNew:true
        };
        TheApp.pages[name] = p;
        TheApp.refreshPageListView();
        TheApp.showPage(p);
/*
        if(! confirm("Really create new wiki page ["+name+"]?") ) return;
        TheApp.cgi.sendCommand('/json/wiki/create',req,{
            onResponse:function(resp,req){
                TheApp.onResponse(resp,req);
                if(resp.resultCode) return;
                TheApp.pages[p.name] = p;
                TheApp.refreshPageListView();
            }
        });
*/
    };

    TheApp.cgi.whoami();

});

</script>

</head>

<body>
<span id='ajaxNotification'></span>
<h1>PROTOTYPE JSON-based Fossil Wiki Editor</h1>

See also: <a href='index.html'>main test page</a>.

<br>
<b>Login:</b>
<br/>
<input type='button' value='Anon. Login' onclick='TheApp.cgi.login()' />
or:
name:<input type='text' id='textUser' value='json-demo' size='12'/>
pw:<input type='password' id='textPassword' value='json-demo' size='12'/>
<input type='button' value='login' onclick='TheApp.cgi.login(jQuery("#textUser").val(),jQuery("#textPassword").val(),{onResponse:TheApp.onLogin})' />
<input type='button' value='logout' onclick='TheApp.cgi.logout()' />

<br/>
<span id='currentAuthToken' style='font-family:monospaced'></span>

<hr/>
<strong>Quick-posts:</strong><br/>
<input type='button' value='HAI' onclick='TheApp.cgi.HAI()' />
<input type='button' value='stat' onclick='TheApp.cgi.sendCommand("/json/stat")' />
<input type='button' value='whoami' onclick='TheApp.cgi.whoami()' />
<input type='button' value='wiki/list' onclick='TheApp.loadPageList()' />
<!--
<input type='button' value='timeline/ci' onclick='TheApp.cgi.sendCommand("/json/timeline/ci")' />
-->

<!--
<input type='button' value='get whiki' onclick='TheApp.cgi.getPages("whiki")' />
<input type='button' value='get more' onclick='TheApp.cgi.getPages("HelloWorld/WhikiNews")' />
<input type='button' value='get client data' onclick='TheApp.cgi.getPageClientData("HelloWorld/whiki/WhikiCommands")' />
<input type='button' value='save client data' onclick='TheApp.cgi.savePageClientData({"HelloWorld":[1,3,5]})' />
-->
<hr/>

<table>
    <tr>
        <th>Page List</th>
        <th>Content <span id='spanPageName'></span></th>
    </tr>
    <tr>
        <td width='25%' valign='top'>
            <input type='button' value='Create new...' onclick='TheApp.createNewPage()' /><br/>
            <div id='pageListArea'></div>
        </td>
        <td width='75%' valign='top'>
            <input type='button' value='Save' onclick='TheApp.savePage()' /><br/>
            <textarea id='taPageContent' rows='20' cols='60'></textarea>
        </td>
    </tr>
    <tr>
        <th colspan='2'>Response <span id='timer'></span></th>
    </tr>
    <tr>
        <td colspan='2' id='responseContainer'>
            <textarea id='taResponse' rows='20' cols='80' readonly></textarea>
        </td>
    </tr>
</table>
<div></div>
<div></div>
<div></div>

</body></html>

Deleted art/CollRev1.dia.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
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

























































































































































































































































































































































































































































































































































































































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
<?xml version="1.0" encoding="UTF-8"?>
<dia:diagram xmlns:dia="http://www.lysator.liu.se/~alla/dia/">
  <dia:diagramdata>
    <dia:attribute name="background">
      <dia:color val="#ffffff"/>
    </dia:attribute>
    <dia:attribute name="pagebreak">
      <dia:color val="#000099"/>
    </dia:attribute>
    <dia:attribute name="paper">
      <dia:composite type="paper">
        <dia:attribute name="name">
          <dia:string>#Letter#</dia:string>
        </dia:attribute>
        <dia:attribute name="tmargin">
          <dia:real val="2.5399999618530273"/>
        </dia:attribute>
        <dia:attribute name="bmargin">
          <dia:real val="2.5399999618530273"/>
        </dia:attribute>
        <dia:attribute name="lmargin">
          <dia:real val="2.5399999618530273"/>
        </dia:attribute>
        <dia:attribute name="rmargin">
          <dia:real val="2.5399999618530273"/>
        </dia:attribute>
        <dia:attribute name="is_portrait">
          <dia:boolean val="true"/>
        </dia:attribute>
        <dia:attribute name="scaling">
          <dia:real val="1"/>
        </dia:attribute>
        <dia:attribute name="fitto">
          <dia:boolean val="false"/>
        </dia:attribute>
      </dia:composite>
    </dia:attribute>
    <dia:attribute name="grid">
      <dia:composite type="grid">
        <dia:attribute name="width_x">
          <dia:real val="1"/>
        </dia:attribute>
        <dia:attribute name="width_y">
          <dia:real val="1"/>
        </dia:attribute>
        <dia:attribute name="visible_x">
          <dia:int val="1"/>
        </dia:attribute>
        <dia:attribute name="visible_y">
          <dia:int val="1"/>
        </dia:attribute>
        <dia:composite type="color"/>
      </dia:composite>
    </dia:attribute>
    <dia:attribute name="color">
      <dia:color val="#d8e5e5"/>
    </dia:attribute>
    <dia:attribute name="guides">
      <dia:composite type="guides">
        <dia:attribute name="hguides"/>
        <dia:attribute name="vguides"/>
      </dia:composite>
    </dia:attribute>
  </dia:diagramdata>
  <dia:layer name="Background" visible="true">
    <dia:group>
      <dia:group>
        <dia:object type="Standard - Box" version="0" id="O0">
          <dia:attribute name="obj_pos">
            <dia:point val="15,13"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="14.95,12.95;20.05,15.05"/>
          </dia:attribute>
          <dia:attribute name="elem_corner">
            <dia:point val="15,13"/>
          </dia:attribute>
          <dia:attribute name="elem_width">
            <dia:real val="5"/>
          </dia:attribute>
          <dia:attribute name="elem_height">
            <dia:real val="2"/>
          </dia:attribute>
          <dia:attribute name="show_background">
            <dia:boolean val="true"/>
          </dia:attribute>
        </dia:object>
        <dia:object type="Standard - Text" version="1" id="O1">
          <dia:attribute name="obj_pos">
            <dia:point val="16.2733,14.064"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="16.2733,13.5215;18.7096,14.4615"/>
          </dia:attribute>
          <dia:attribute name="text">
            <dia:composite type="text">
              <dia:attribute name="string">
                <dia:string>#Revision#</dia:string>
              </dia:attribute>
              <dia:attribute name="font">
                <dia:font family="sans" style="0" name="Helvetica"/>
              </dia:attribute>
              <dia:attribute name="height">
                <dia:real val="0.80000000000000004"/>
              </dia:attribute>
              <dia:attribute name="pos">
                <dia:point val="16.2733,14.064"/>
              </dia:attribute>
              <dia:attribute name="color">
                <dia:color val="#000000"/>
              </dia:attribute>
              <dia:attribute name="alignment">
                <dia:enum val="0"/>
              </dia:attribute>
            </dia:composite>
          </dia:attribute>
          <dia:attribute name="valign">
            <dia:enum val="3"/>
          </dia:attribute>
        </dia:object>
      </dia:group>
      <dia:group>
        <dia:object type="Standard - Box" version="0" id="O2">
          <dia:attribute name="obj_pos">
            <dia:point val="15,7"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="14.95,6.95;20.05,9.05"/>
          </dia:attribute>
          <dia:attribute name="elem_corner">
            <dia:point val="15,7"/>
          </dia:attribute>
          <dia:attribute name="elem_width">
            <dia:real val="5"/>
          </dia:attribute>
          <dia:attribute name="elem_height">
            <dia:real val="2"/>
          </dia:attribute>
          <dia:attribute name="show_background">
            <dia:boolean val="true"/>
          </dia:attribute>
        </dia:object>
        <dia:object type="Standard - Text" version="1" id="O3">
          <dia:attribute name="obj_pos">
            <dia:point val="17.0039,8.06397"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="17.0039,7.52147;17.979,8.46147"/>
          </dia:attribute>
          <dia:attribute name="text">
            <dia:composite type="text">
              <dia:attribute name="string">
                <dia:string>#File#</dia:string>
              </dia:attribute>
              <dia:attribute name="font">
                <dia:font family="sans" style="0" name="Helvetica"/>
              </dia:attribute>
              <dia:attribute name="height">
                <dia:real val="0.80000000000000004"/>
              </dia:attribute>
              <dia:attribute name="pos">
                <dia:point val="17.0039,8.06397"/>
              </dia:attribute>
              <dia:attribute name="color">
                <dia:color val="#000000"/>
              </dia:attribute>
              <dia:attribute name="alignment">
                <dia:enum val="0"/>
              </dia:attribute>
            </dia:composite>
          </dia:attribute>
          <dia:attribute name="valign">
            <dia:enum val="3"/>
          </dia:attribute>
        </dia:object>
      </dia:group>
      <dia:group>
        <dia:object type="Standard - Box" version="0" id="O4">
          <dia:attribute name="obj_pos">
            <dia:point val="15,1"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="14.95,0.95;20.05,3.05"/>
          </dia:attribute>
          <dia:attribute name="elem_corner">
            <dia:point val="15,1"/>
          </dia:attribute>
          <dia:attribute name="elem_width">
            <dia:real val="5"/>
          </dia:attribute>
          <dia:attribute name="elem_height">
            <dia:real val="2"/>
          </dia:attribute>
          <dia:attribute name="show_background">
            <dia:boolean val="true"/>
          </dia:attribute>
        </dia:object>
        <dia:object type="Standard - Text" version="1" id="O5">
          <dia:attribute name="obj_pos">
            <dia:point val="16.4942,2.06397"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="16.4942,1.52147;18.4887,2.46147"/>
          </dia:attribute>
          <dia:attribute name="text">
            <dia:composite type="text">
              <dia:attribute name="string">
                <dia:string>#Project#</dia:string>
              </dia:attribute>
              <dia:attribute name="font">
                <dia:font family="sans" style="0" name="Helvetica"/>
              </dia:attribute>
              <dia:attribute name="height">
                <dia:real val="0.80000000000000004"/>
              </dia:attribute>
              <dia:attribute name="pos">
                <dia:point val="16.4942,2.06397"/>
              </dia:attribute>
              <dia:attribute name="color">
                <dia:color val="#000000"/>
              </dia:attribute>
              <dia:attribute name="alignment">
                <dia:enum val="0"/>
              </dia:attribute>
            </dia:composite>
          </dia:attribute>
          <dia:attribute name="valign">
            <dia:enum val="3"/>
          </dia:attribute>
        </dia:object>
      </dia:group>
      <dia:object type="Standard - ZigZagLine" version="1" id="O6">
        <dia:attribute name="obj_pos">
          <dia:point val="17.5,13"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="17.45,8.92929;17.55,13"/>
        </dia:attribute>
        <dia:attribute name="orth_points">
          <dia:point val="17.5,13"/>
          <dia:point val="17.5,13"/>
          <dia:point val="17.5,9"/>
          <dia:point val="17.5,9"/>
        </dia:attribute>
        <dia:attribute name="orth_orient">
          <dia:enum val="0"/>
          <dia:enum val="1"/>
          <dia:enum val="0"/>
        </dia:attribute>
        <dia:attribute name="autorouting">
          <dia:boolean val="true"/>
        </dia:attribute>
        <dia:attribute name="end_arrow">
          <dia:enum val="22"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_length">
          <dia:real val="0.5"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_width">
          <dia:real val="0.5"/>
        </dia:attribute>
      </dia:object>
      <dia:object type="Standard - ZigZagLine" version="1" id="O7">
        <dia:attribute name="obj_pos">
          <dia:point val="17.5,7"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="17.45,2.92929;17.55,7"/>
        </dia:attribute>
        <dia:attribute name="orth_points">
          <dia:point val="17.5,7"/>
          <dia:point val="17.5,7"/>
          <dia:point val="17.5,3"/>
          <dia:point val="17.5,3"/>
        </dia:attribute>
        <dia:attribute name="orth_orient">
          <dia:enum val="0"/>
          <dia:enum val="1"/>
          <dia:enum val="0"/>
        </dia:attribute>
        <dia:attribute name="autorouting">
          <dia:boolean val="true"/>
        </dia:attribute>
        <dia:attribute name="end_arrow">
          <dia:enum val="22"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_length">
          <dia:real val="0.5"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_width">
          <dia:real val="0.5"/>
        </dia:attribute>
      </dia:object>
      <dia:group>
        <dia:object type="Standard - Box" version="0" id="O8">
          <dia:attribute name="obj_pos">
            <dia:point val="15,19"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="14.95,18.95;20.05,21.05"/>
          </dia:attribute>
          <dia:attribute name="elem_corner">
            <dia:point val="15,19"/>
          </dia:attribute>
          <dia:attribute name="elem_width">
            <dia:real val="5"/>
          </dia:attribute>
          <dia:attribute name="elem_height">
            <dia:real val="2"/>
          </dia:attribute>
          <dia:attribute name="show_background">
            <dia:boolean val="true"/>
          </dia:attribute>
        </dia:object>
        <dia:object type="Standard - Text" version="1" id="O9">
          <dia:attribute name="obj_pos">
            <dia:point val="16.7775,20.064"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="16.7775,19.5215;18.2225,20.4615"/>
          </dia:attribute>
          <dia:attribute name="text">
            <dia:composite type="text">
              <dia:attribute name="string">
                <dia:string>#Meta#</dia:string>
              </dia:attribute>
              <dia:attribute name="font">
                <dia:font family="sans" style="0" name="Helvetica"/>
              </dia:attribute>
              <dia:attribute name="height">
                <dia:real val="0.80000000000000004"/>
              </dia:attribute>
              <dia:attribute name="pos">
                <dia:point val="16.7775,20.064"/>
              </dia:attribute>
              <dia:attribute name="color">
                <dia:color val="#000000"/>
              </dia:attribute>
              <dia:attribute name="alignment">
                <dia:enum val="0"/>
              </dia:attribute>
            </dia:composite>
          </dia:attribute>
          <dia:attribute name="valign">
            <dia:enum val="3"/>
          </dia:attribute>
        </dia:object>
      </dia:group>
      <dia:object type="Standard - PolyLine" version="0" id="O10">
        <dia:attribute name="obj_pos">
          <dia:point val="15,20"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="7.5,14.95;15.05,20.05"/>
        </dia:attribute>
        <dia:attribute name="poly_points">
          <dia:point val="15,20"/>
          <dia:point val="8,20"/>
          <dia:point val="8,15"/>
        </dia:attribute>
        <dia:attribute name="end_arrow">
          <dia:enum val="22"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_length">
          <dia:real val="0.5"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_width">
          <dia:real val="0.5"/>
        </dia:attribute>
      </dia:object>
      <dia:object type="Standard - Line" version="0" id="O11">
        <dia:attribute name="obj_pos">
          <dia:point val="15,14"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="10.95,13.5;15.05,14.5"/>
        </dia:attribute>
        <dia:attribute name="conn_endpoints">
          <dia:point val="15,14"/>
          <dia:point val="11,14"/>
        </dia:attribute>
        <dia:attribute name="numcp">
          <dia:int val="1"/>
        </dia:attribute>
        <dia:attribute name="end_arrow">
          <dia:enum val="22"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_length">
          <dia:real val="0.5"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_width">
          <dia:real val="0.5"/>
        </dia:attribute>
      </dia:object>
      <dia:object type="Standard - ZigZagLine" version="1" id="O12">
        <dia:attribute name="obj_pos">
          <dia:point val="20,20"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="19.95,1.5;27.05,20.05"/>
        </dia:attribute>
        <dia:attribute name="orth_points">
          <dia:point val="20,20"/>
          <dia:point val="27,20"/>
          <dia:point val="27,2"/>
          <dia:point val="20,2"/>
        </dia:attribute>
        <dia:attribute name="orth_orient">
          <dia:enum val="0"/>
          <dia:enum val="1"/>
          <dia:enum val="0"/>
        </dia:attribute>
        <dia:attribute name="autorouting">
          <dia:boolean val="false"/>
        </dia:attribute>
        <dia:attribute name="end_arrow">
          <dia:enum val="22"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_length">
          <dia:real val="0.5"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_width">
          <dia:real val="0.5"/>
        </dia:attribute>
      </dia:object>
      <dia:object type="Standard - Line" version="0" id="O13">
        <dia:attribute name="obj_pos">
          <dia:point val="17.5,15"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="17,14.95;18,19.05"/>
        </dia:attribute>
        <dia:attribute name="conn_endpoints">
          <dia:point val="17.5,15"/>
          <dia:point val="17.5,19"/>
        </dia:attribute>
        <dia:attribute name="numcp">
          <dia:int val="1"/>
        </dia:attribute>
        <dia:attribute name="end_arrow">
          <dia:enum val="22"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_length">
          <dia:real val="0.5"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_width">
          <dia:real val="0.5"/>
        </dia:attribute>
      </dia:object>
      <dia:object type="Standard - PolyLine" version="0" id="O14">
        <dia:attribute name="obj_pos">
          <dia:point val="8,13"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="7.95,1.5;15.05,13.05"/>
        </dia:attribute>
        <dia:attribute name="poly_points">
          <dia:point val="8,13"/>
          <dia:point val="8,2"/>
          <dia:point val="15,2"/>
        </dia:attribute>
        <dia:attribute name="end_arrow">
          <dia:enum val="22"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_length">
          <dia:real val="0.5"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_width">
          <dia:real val="0.5"/>
        </dia:attribute>
      </dia:object>
      <dia:group>
        <dia:object type="Standard - Box" version="0" id="O15">
          <dia:attribute name="obj_pos">
            <dia:point val="5,13"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="4.95,12.95;11.05,15.05"/>
          </dia:attribute>
          <dia:attribute name="elem_corner">
            <dia:point val="5,13"/>
          </dia:attribute>
          <dia:attribute name="elem_width">
            <dia:real val="6"/>
          </dia:attribute>
          <dia:attribute name="elem_height">
            <dia:real val="2"/>
          </dia:attribute>
          <dia:attribute name="show_background">
            <dia:boolean val="true"/>
          </dia:attribute>
        </dia:object>
        <dia:object type="Standard - Text" version="1" id="O16">
          <dia:attribute name="obj_pos">
            <dia:point val="6.91375,14.0725"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="6.91375,13.53;9.08625,14.47"/>
          </dia:attribute>
          <dia:attribute name="text">
            <dia:composite type="text">
              <dia:attribute name="string">
                <dia:string>#Symbol#</dia:string>
              </dia:attribute>
              <dia:attribute name="font">
                <dia:font family="sans" style="0" name="Helvetica"/>
              </dia:attribute>
              <dia:attribute name="height">
                <dia:real val="0.80000000000000004"/>
              </dia:attribute>
              <dia:attribute name="pos">
                <dia:point val="6.91375,14.0725"/>
              </dia:attribute>
              <dia:attribute name="color">
                <dia:color val="#000000"/>
              </dia:attribute>
              <dia:attribute name="alignment">
                <dia:enum val="0"/>
              </dia:attribute>
            </dia:composite>
          </dia:attribute>
          <dia:attribute name="valign">
            <dia:enum val="3"/>
          </dia:attribute>
        </dia:object>
      </dia:group>
      <dia:object type="Standard - Text" version="1" id="O17">
        <dia:attribute name="obj_pos">
          <dia:point val="18,12"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="18,11.4575;21.06,12.3975"/>
        </dia:attribute>
        <dia:attribute name="text">
          <dia:composite type="text">
            <dia:attribute name="string">
              <dia:string>#belongs to#</dia:string>
            </dia:attribute>
            <dia:attribute name="font">
              <dia:font family="sans" style="0" name="Helvetica"/>
            </dia:attribute>
            <dia:attribute name="height">
              <dia:real val="0.80000000000000004"/>
            </dia:attribute>
            <dia:attribute name="pos">
              <dia:point val="18,12"/>
            </dia:attribute>
            <dia:attribute name="color">
              <dia:color val="#000000"/>
            </dia:attribute>
            <dia:attribute name="alignment">
              <dia:enum val="0"/>
            </dia:attribute>
          </dia:composite>
        </dia:attribute>
        <dia:attribute name="valign">
          <dia:enum val="3"/>
        </dia:attribute>
      </dia:object>
      <dia:object type="Standard - Text" version="1" id="O18">
        <dia:attribute name="obj_pos">
          <dia:point val="18,6"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="18,5.4575;21.06,6.3975"/>
        </dia:attribute>
        <dia:attribute name="text">
          <dia:composite type="text">
            <dia:attribute name="string">
              <dia:string>#belongs to#</dia:string>
            </dia:attribute>
            <dia:attribute name="font">
              <dia:font family="sans" style="0" name="Helvetica"/>
            </dia:attribute>
            <dia:attribute name="height">
              <dia:real val="0.80000000000000004"/>
            </dia:attribute>
            <dia:attribute name="pos">
              <dia:point val="18,6"/>
            </dia:attribute>
            <dia:attribute name="color">
              <dia:color val="#000000"/>
            </dia:attribute>
            <dia:attribute name="alignment">
              <dia:enum val="0"/>
            </dia:attribute>
          </dia:composite>
        </dia:attribute>
        <dia:attribute name="valign">
          <dia:enum val="3"/>
        </dia:attribute>
      </dia:object>
      <dia:object type="Standard - Text" version="1" id="O19">
        <dia:attribute name="obj_pos">
          <dia:point val="18,16"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="18,15.4575;19.03,16.3975"/>
        </dia:attribute>
        <dia:attribute name="text">
          <dia:composite type="text">
            <dia:attribute name="string">
              <dia:string>#has#</dia:string>
            </dia:attribute>
            <dia:attribute name="font">
              <dia:font family="sans" style="0" name="Helvetica"/>
            </dia:attribute>
            <dia:attribute name="height">
              <dia:real val="0.80000000000000004"/>
            </dia:attribute>
            <dia:attribute name="pos">
              <dia:point val="18,16"/>
            </dia:attribute>
            <dia:attribute name="color">
              <dia:color val="#000000"/>
            </dia:attribute>
            <dia:attribute name="alignment">
              <dia:enum val="0"/>
            </dia:attribute>
          </dia:composite>
        </dia:attribute>
        <dia:attribute name="valign">
          <dia:enum val="3"/>
        </dia:attribute>
      </dia:object>
      <dia:object type="Standard - Text" version="1" id="O20">
        <dia:attribute name="obj_pos">
          <dia:point val="21,20"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="21,19.4575;24.06,20.3975"/>
        </dia:attribute>
        <dia:attribute name="text">
          <dia:composite type="text">
            <dia:attribute name="string">
              <dia:string>#belongs to#</dia:string>
            </dia:attribute>
            <dia:attribute name="font">
              <dia:font family="sans" style="0" name="Helvetica"/>
            </dia:attribute>
            <dia:attribute name="height">
              <dia:real val="0.80000000000000004"/>
            </dia:attribute>
            <dia:attribute name="pos">
              <dia:point val="21,20"/>
            </dia:attribute>
            <dia:attribute name="color">
              <dia:color val="#000000"/>
            </dia:attribute>
            <dia:attribute name="alignment">
              <dia:enum val="0"/>
            </dia:attribute>
          </dia:composite>
        </dia:attribute>
        <dia:attribute name="valign">
          <dia:enum val="3"/>
        </dia:attribute>
      </dia:object>
      <dia:object type="Standard - Text" version="1" id="O21">
        <dia:attribute name="obj_pos">
          <dia:point val="11,20"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="11,19.4575;14.06,20.3975"/>
        </dia:attribute>
        <dia:attribute name="text">
          <dia:composite type="text">
            <dia:attribute name="string">
              <dia:string>#belongs to#</dia:string>
            </dia:attribute>
            <dia:attribute name="font">
              <dia:font family="sans" style="0" name="Helvetica"/>
            </dia:attribute>
            <dia:attribute name="height">
              <dia:real val="0.80000000000000004"/>
            </dia:attribute>
            <dia:attribute name="pos">
              <dia:point val="11,20"/>
            </dia:attribute>
            <dia:attribute name="color">
              <dia:color val="#000000"/>
            </dia:attribute>
            <dia:attribute name="alignment">
              <dia:enum val="0"/>
            </dia:attribute>
          </dia:composite>
        </dia:attribute>
        <dia:attribute name="valign">
          <dia:enum val="3"/>
        </dia:attribute>
      </dia:object>
      <dia:object type="Standard - Text" version="1" id="O22">
        <dia:attribute name="obj_pos">
          <dia:point val="12,15"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="12,14.4575;15.06,15.3975"/>
        </dia:attribute>
        <dia:attribute name="text">
          <dia:composite type="text">
            <dia:attribute name="string">
              <dia:string>#belongs to#</dia:string>
            </dia:attribute>
            <dia:attribute name="font">
              <dia:font family="sans" style="0" name="Helvetica"/>
            </dia:attribute>
            <dia:attribute name="height">
              <dia:real val="0.80000000000000004"/>
            </dia:attribute>
            <dia:attribute name="pos">
              <dia:point val="12,15"/>
            </dia:attribute>
            <dia:attribute name="color">
              <dia:color val="#000000"/>
            </dia:attribute>
            <dia:attribute name="alignment">
              <dia:enum val="0"/>
            </dia:attribute>
          </dia:composite>
        </dia:attribute>
        <dia:attribute name="valign">
          <dia:enum val="3"/>
        </dia:attribute>
      </dia:object>
      <dia:object type="Standard - Text" version="1" id="O23">
        <dia:attribute name="obj_pos">
          <dia:point val="9,12"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="9,11.4575;12.06,12.3975"/>
        </dia:attribute>
        <dia:attribute name="text">
          <dia:composite type="text">
            <dia:attribute name="string">
              <dia:string>#belongs to#</dia:string>
            </dia:attribute>
            <dia:attribute name="font">
              <dia:font family="sans" style="0" name="Helvetica"/>
            </dia:attribute>
            <dia:attribute name="height">
              <dia:real val="0.80000000000000004"/>
            </dia:attribute>
            <dia:attribute name="pos">
              <dia:point val="9,12"/>
            </dia:attribute>
            <dia:attribute name="color">
              <dia:color val="#000000"/>
            </dia:attribute>
            <dia:attribute name="alignment">
              <dia:enum val="0"/>
            </dia:attribute>
          </dia:composite>
        </dia:attribute>
        <dia:attribute name="valign">
          <dia:enum val="3"/>
        </dia:attribute>
      </dia:object>
      <dia:object type="Standard - Text" version="1" id="O24">
        <dia:attribute name="obj_pos">
          <dia:point val="1,13"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="1,12.4575;10.5175,13.3975"/>
        </dia:attribute>
        <dia:attribute name="text">
          <dia:composite type="text">
            <dia:attribute name="string">
              <dia:string>#(Line of Development  /  Branch)#</dia:string>
            </dia:attribute>
            <dia:attribute name="font">
              <dia:font family="sans" style="0" name="Helvetica"/>
            </dia:attribute>
            <dia:attribute name="height">
              <dia:real val="0.80000000000000004"/>
            </dia:attribute>
            <dia:attribute name="pos">
              <dia:point val="1,13"/>
            </dia:attribute>
            <dia:attribute name="color">
              <dia:color val="#000000"/>
            </dia:attribute>
            <dia:attribute name="alignment">
              <dia:enum val="0"/>
            </dia:attribute>
          </dia:composite>
        </dia:attribute>
        <dia:attribute name="valign">
          <dia:enum val="3"/>
        </dia:attribute>
      </dia:object>
    </dia:group>
  </dia:layer>
</dia:diagram>

Deleted art/CollRev2.dia.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
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
































































































































































































































































































































































































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
<?xml version="1.0" encoding="UTF-8"?>
<dia:diagram xmlns:dia="http://www.lysator.liu.se/~alla/dia/">
  <dia:diagramdata>
    <dia:attribute name="background">
      <dia:color val="#ffffff"/>
    </dia:attribute>
    <dia:attribute name="pagebreak">
      <dia:color val="#000099"/>
    </dia:attribute>
    <dia:attribute name="paper">
      <dia:composite type="paper">
        <dia:attribute name="name">
          <dia:string>#Letter#</dia:string>
        </dia:attribute>
        <dia:attribute name="tmargin">
          <dia:real val="2.5399999618530273"/>
        </dia:attribute>
        <dia:attribute name="bmargin">
          <dia:real val="2.5399999618530273"/>
        </dia:attribute>
        <dia:attribute name="lmargin">
          <dia:real val="2.5399999618530273"/>
        </dia:attribute>
        <dia:attribute name="rmargin">
          <dia:real val="2.5399999618530273"/>
        </dia:attribute>
        <dia:attribute name="is_portrait">
          <dia:boolean val="true"/>
        </dia:attribute>
        <dia:attribute name="scaling">
          <dia:real val="1"/>
        </dia:attribute>
        <dia:attribute name="fitto">
          <dia:boolean val="false"/>
        </dia:attribute>
      </dia:composite>
    </dia:attribute>
    <dia:attribute name="grid">
      <dia:composite type="grid">
        <dia:attribute name="width_x">
          <dia:real val="1"/>
        </dia:attribute>
        <dia:attribute name="width_y">
          <dia:real val="1"/>
        </dia:attribute>
        <dia:attribute name="visible_x">
          <dia:int val="1"/>
        </dia:attribute>
        <dia:attribute name="visible_y">
          <dia:int val="1"/>
        </dia:attribute>
        <dia:composite type="color"/>
      </dia:composite>
    </dia:attribute>
    <dia:attribute name="color">
      <dia:color val="#d8e5e5"/>
    </dia:attribute>
    <dia:attribute name="guides">
      <dia:composite type="guides">
        <dia:attribute name="hguides"/>
        <dia:attribute name="vguides"/>
      </dia:composite>
    </dia:attribute>
  </dia:diagramdata>
  <dia:layer name="Background" visible="true">
    <dia:group>
      <dia:group>
        <dia:object type="Standard - Box" version="0" id="O0">
          <dia:attribute name="obj_pos">
            <dia:point val="13,1"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="12.95,0.95;20.05,3.05"/>
          </dia:attribute>
          <dia:attribute name="elem_corner">
            <dia:point val="13,1"/>
          </dia:attribute>
          <dia:attribute name="elem_width">
            <dia:real val="7"/>
          </dia:attribute>
          <dia:attribute name="elem_height">
            <dia:real val="2"/>
          </dia:attribute>
          <dia:attribute name="show_background">
            <dia:boolean val="true"/>
          </dia:attribute>
        </dia:object>
        <dia:object type="Standard - Text" version="1" id="O1">
          <dia:attribute name="obj_pos">
            <dia:point val="15.2818,2.06397"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="15.2818,1.52147;17.7182,2.46147"/>
          </dia:attribute>
          <dia:attribute name="text">
            <dia:composite type="text">
              <dia:attribute name="string">
                <dia:string>#Revision#</dia:string>
              </dia:attribute>
              <dia:attribute name="font">
                <dia:font family="sans" style="0" name="Helvetica"/>
              </dia:attribute>
              <dia:attribute name="height">
                <dia:real val="0.80000000000000004"/>
              </dia:attribute>
              <dia:attribute name="pos">
                <dia:point val="15.2818,2.06397"/>
              </dia:attribute>
              <dia:attribute name="color">
                <dia:color val="#000000"/>
              </dia:attribute>
              <dia:attribute name="alignment">
                <dia:enum val="0"/>
              </dia:attribute>
            </dia:composite>
          </dia:attribute>
          <dia:attribute name="valign">
            <dia:enum val="3"/>
          </dia:attribute>
        </dia:object>
      </dia:group>
      <dia:group>
        <dia:object type="Standard - Box" version="0" id="O2">
          <dia:attribute name="obj_pos">
            <dia:point val="13,7"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="12.95,6.95;20.05,9.05"/>
          </dia:attribute>
          <dia:attribute name="elem_corner">
            <dia:point val="13,7"/>
          </dia:attribute>
          <dia:attribute name="elem_width">
            <dia:real val="7"/>
          </dia:attribute>
          <dia:attribute name="elem_height">
            <dia:real val="2"/>
          </dia:attribute>
          <dia:attribute name="show_background">
            <dia:boolean val="true"/>
          </dia:attribute>
        </dia:object>
        <dia:object type="Standard - Text" version="1" id="O3">
          <dia:attribute name="obj_pos">
            <dia:point val="14.1456,8.06397"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="14.1456,7.52147;18.8544,8.46147"/>
          </dia:attribute>
          <dia:attribute name="text">
            <dia:composite type="text">
              <dia:attribute name="string">
                <dia:string>#Revision' (Child)#</dia:string>
              </dia:attribute>
              <dia:attribute name="font">
                <dia:font family="sans" style="0" name="Helvetica"/>
              </dia:attribute>
              <dia:attribute name="height">
                <dia:real val="0.80000000000000004"/>
              </dia:attribute>
              <dia:attribute name="pos">
                <dia:point val="14.1456,8.06397"/>
              </dia:attribute>
              <dia:attribute name="color">
                <dia:color val="#000000"/>
              </dia:attribute>
              <dia:attribute name="alignment">
                <dia:enum val="0"/>
              </dia:attribute>
            </dia:composite>
          </dia:attribute>
          <dia:attribute name="valign">
            <dia:enum val="3"/>
          </dia:attribute>
        </dia:object>
      </dia:group>
      <dia:group>
        <dia:object type="Standard - Box" version="0" id="O4">
          <dia:attribute name="obj_pos">
            <dia:point val="2,2"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="1.95,1.95;9.05,4.05"/>
          </dia:attribute>
          <dia:attribute name="elem_corner">
            <dia:point val="2,2"/>
          </dia:attribute>
          <dia:attribute name="elem_width">
            <dia:real val="7"/>
          </dia:attribute>
          <dia:attribute name="elem_height">
            <dia:real val="2"/>
          </dia:attribute>
          <dia:attribute name="show_background">
            <dia:boolean val="true"/>
          </dia:attribute>
        </dia:object>
        <dia:object type="Standard - Text" version="1" id="O5">
          <dia:attribute name="obj_pos">
            <dia:point val="5.00393,3.06397"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="5.00393,2.52147;5.97901,3.46147"/>
          </dia:attribute>
          <dia:attribute name="text">
            <dia:composite type="text">
              <dia:attribute name="string">
                <dia:string>#File#</dia:string>
              </dia:attribute>
              <dia:attribute name="font">
                <dia:font family="sans" style="0" name="Helvetica"/>
              </dia:attribute>
              <dia:attribute name="height">
                <dia:real val="0.80000000000000004"/>
              </dia:attribute>
              <dia:attribute name="pos">
                <dia:point val="5.00393,3.06397"/>
              </dia:attribute>
              <dia:attribute name="color">
                <dia:color val="#000000"/>
              </dia:attribute>
              <dia:attribute name="alignment">
                <dia:enum val="0"/>
              </dia:attribute>
            </dia:composite>
          </dia:attribute>
          <dia:attribute name="valign">
            <dia:enum val="3"/>
          </dia:attribute>
        </dia:object>
      </dia:group>
      <dia:object type="Standard - Line" version="0" id="O6">
        <dia:attribute name="obj_pos">
          <dia:point val="13,8"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="8.83023,6.5028;13.0606,8.06063"/>
        </dia:attribute>
        <dia:attribute name="conn_endpoints">
          <dia:point val="13,8"/>
          <dia:point val="9,7"/>
        </dia:attribute>
        <dia:attribute name="numcp">
          <dia:int val="1"/>
        </dia:attribute>
        <dia:attribute name="end_arrow">
          <dia:enum val="22"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_length">
          <dia:real val="0.5"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_width">
          <dia:real val="0.5"/>
        </dia:attribute>
      </dia:object>
      <dia:object type="Standard - Line" version="0" id="O7">
        <dia:attribute name="obj_pos">
          <dia:point val="13,2"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="8.83023,1.93937;13.0606,3.4972"/>
        </dia:attribute>
        <dia:attribute name="conn_endpoints">
          <dia:point val="13,2"/>
          <dia:point val="9,3"/>
        </dia:attribute>
        <dia:attribute name="numcp">
          <dia:int val="1"/>
        </dia:attribute>
        <dia:attribute name="end_arrow">
          <dia:enum val="22"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_length">
          <dia:real val="0.5"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_width">
          <dia:real val="0.5"/>
        </dia:attribute>
      </dia:object>
      <dia:object type="Standard - Line" version="0" id="O8">
        <dia:attribute name="obj_pos">
          <dia:point val="13,2"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="8.57833,1.92972;13.0703,7.35139"/>
        </dia:attribute>
        <dia:attribute name="conn_endpoints">
          <dia:point val="13,2"/>
          <dia:point val="9,7"/>
        </dia:attribute>
        <dia:attribute name="numcp">
          <dia:int val="1"/>
        </dia:attribute>
        <dia:attribute name="end_arrow">
          <dia:enum val="22"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_length">
          <dia:real val="0.5"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_width">
          <dia:real val="0.5"/>
        </dia:attribute>
      </dia:object>
      <dia:object type="Standard - Line" version="0" id="O9">
        <dia:attribute name="obj_pos">
          <dia:point val="13,8"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="8.57833,2.64861;13.0703,8.07028"/>
        </dia:attribute>
        <dia:attribute name="conn_endpoints">
          <dia:point val="13,8"/>
          <dia:point val="9,3"/>
        </dia:attribute>
        <dia:attribute name="numcp">
          <dia:int val="1"/>
        </dia:attribute>
        <dia:attribute name="end_arrow">
          <dia:enum val="22"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_length">
          <dia:real val="0.5"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_width">
          <dia:real val="0.5"/>
        </dia:attribute>
      </dia:object>
      <dia:group>
        <dia:object type="Standard - Box" version="0" id="O10">
          <dia:attribute name="obj_pos">
            <dia:point val="2,6"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="1.95,5.95;9.05,8.05"/>
          </dia:attribute>
          <dia:attribute name="elem_corner">
            <dia:point val="2,6"/>
          </dia:attribute>
          <dia:attribute name="elem_width">
            <dia:real val="7"/>
          </dia:attribute>
          <dia:attribute name="elem_height">
            <dia:real val="2"/>
          </dia:attribute>
          <dia:attribute name="show_background">
            <dia:boolean val="true"/>
          </dia:attribute>
        </dia:object>
        <dia:object type="Standard - Text" version="1" id="O11">
          <dia:attribute name="obj_pos">
            <dia:point val="3.53147,6.6725"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="3.53147,6.13;7.45147,7.87"/>
          </dia:attribute>
          <dia:attribute name="text">
            <dia:composite type="text">
              <dia:attribute name="string">
                <dia:string>#Line of
Development#</dia:string>
              </dia:attribute>
              <dia:attribute name="font">
                <dia:font family="sans" style="0" name="Helvetica"/>
              </dia:attribute>
              <dia:attribute name="height">
                <dia:real val="0.80000000000000004"/>
              </dia:attribute>
              <dia:attribute name="pos">
                <dia:point val="3.53147,6.6725"/>
              </dia:attribute>
              <dia:attribute name="color">
                <dia:color val="#000000"/>
              </dia:attribute>
              <dia:attribute name="alignment">
                <dia:enum val="0"/>
              </dia:attribute>
            </dia:composite>
          </dia:attribute>
          <dia:attribute name="valign">
            <dia:enum val="3"/>
          </dia:attribute>
        </dia:object>
      </dia:group>
      <dia:object type="Standard - Text" version="1" id="O12">
        <dia:attribute name="obj_pos">
          <dia:point val="18,6"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="18,5.4575;20.0947,6.3975"/>
        </dia:attribute>
        <dia:attribute name="text">
          <dia:composite type="text">
            <dia:attribute name="string">
              <dia:string># parent#</dia:string>
            </dia:attribute>
            <dia:attribute name="font">
              <dia:font family="sans" style="0" name="Helvetica"/>
            </dia:attribute>
            <dia:attribute name="height">
              <dia:real val="0.80000000000000004"/>
            </dia:attribute>
            <dia:attribute name="pos">
              <dia:point val="18,6"/>
            </dia:attribute>
            <dia:attribute name="color">
              <dia:color val="#000000"/>
            </dia:attribute>
            <dia:attribute name="alignment">
              <dia:enum val="0"/>
            </dia:attribute>
          </dia:composite>
        </dia:attribute>
        <dia:attribute name="valign">
          <dia:enum val="3"/>
        </dia:attribute>
      </dia:object>
      <dia:object type="Standard - Line" version="0" id="O13">
        <dia:attribute name="obj_pos">
          <dia:point val="18,7"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="17.5,2.95;18.5,7.05"/>
        </dia:attribute>
        <dia:attribute name="conn_endpoints">
          <dia:point val="18,7"/>
          <dia:point val="18,3"/>
        </dia:attribute>
        <dia:attribute name="numcp">
          <dia:int val="1"/>
        </dia:attribute>
        <dia:attribute name="end_arrow">
          <dia:enum val="22"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_length">
          <dia:real val="0.5"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_width">
          <dia:real val="0.5"/>
        </dia:attribute>
      </dia:object>
      <dia:group>
        <dia:object type="Standard - Text" version="1" id="O14">
          <dia:attribute name="obj_pos">
            <dia:point val="22,7"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="22,6.4575;28.4122,8.1975"/>
          </dia:attribute>
          <dia:attribute name="text">
            <dia:composite type="text">
              <dia:attribute name="string">
                <dia:string>#branch parent symbol
       (NULL)#</dia:string>
              </dia:attribute>
              <dia:attribute name="font">
                <dia:font family="sans" style="0" name="Helvetica"/>
              </dia:attribute>
              <dia:attribute name="height">
                <dia:real val="0.80000000000000004"/>
              </dia:attribute>
              <dia:attribute name="pos">
                <dia:point val="22,7"/>
              </dia:attribute>
              <dia:attribute name="color">
                <dia:color val="#000000"/>
              </dia:attribute>
              <dia:attribute name="alignment">
                <dia:enum val="0"/>
              </dia:attribute>
            </dia:composite>
          </dia:attribute>
          <dia:attribute name="valign">
            <dia:enum val="3"/>
          </dia:attribute>
        </dia:object>
        <dia:object type="Standard - Line" version="0" id="O15">
          <dia:attribute name="obj_pos">
            <dia:point val="20,8"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="19.95,7.5;22.05,8.5"/>
          </dia:attribute>
          <dia:attribute name="conn_endpoints">
            <dia:point val="20,8"/>
            <dia:point val="22,8"/>
          </dia:attribute>
          <dia:attribute name="numcp">
            <dia:int val="1"/>
          </dia:attribute>
          <dia:attribute name="end_arrow">
            <dia:enum val="22"/>
          </dia:attribute>
          <dia:attribute name="end_arrow_length">
            <dia:real val="0.5"/>
          </dia:attribute>
          <dia:attribute name="end_arrow_width">
            <dia:real val="0.5"/>
          </dia:attribute>
        </dia:object>
        <dia:object type="Standard - Polygon" version="0" id="O16">
          <dia:attribute name="obj_pos">
            <dia:point val="23,7"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="21.9293,6.87929;23.05,9.12071"/>
          </dia:attribute>
          <dia:attribute name="poly_points">
            <dia:point val="23,7"/>
            <dia:point val="22,8"/>
            <dia:point val="23,9"/>
          </dia:attribute>
          <dia:attribute name="show_background">
            <dia:boolean val="true"/>
          </dia:attribute>
        </dia:object>
      </dia:group>
      <dia:object type="Standard - Text" version="1" id="O17">
        <dia:attribute name="obj_pos">
          <dia:point val="15,4"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="15,3.4575;16.57,4.3975"/>
        </dia:attribute>
        <dia:attribute name="text">
          <dia:composite type="text">
            <dia:attribute name="string">
              <dia:string># child#</dia:string>
            </dia:attribute>
            <dia:attribute name="font">
              <dia:font family="sans" style="0" name="Helvetica"/>
            </dia:attribute>
            <dia:attribute name="height">
              <dia:real val="0.80000000000000004"/>
            </dia:attribute>
            <dia:attribute name="pos">
              <dia:point val="15,4"/>
            </dia:attribute>
            <dia:attribute name="color">
              <dia:color val="#000000"/>
            </dia:attribute>
            <dia:attribute name="alignment">
              <dia:enum val="0"/>
            </dia:attribute>
          </dia:composite>
        </dia:attribute>
        <dia:attribute name="valign">
          <dia:enum val="3"/>
        </dia:attribute>
      </dia:object>
      <dia:object type="Standard - Line" version="0" id="O18">
        <dia:attribute name="obj_pos">
          <dia:point val="15,3"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="14.5,2.95;15.5,7.05"/>
        </dia:attribute>
        <dia:attribute name="conn_endpoints">
          <dia:point val="15,3"/>
          <dia:point val="15,7"/>
        </dia:attribute>
        <dia:attribute name="numcp">
          <dia:int val="1"/>
        </dia:attribute>
        <dia:attribute name="end_arrow">
          <dia:enum val="22"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_length">
          <dia:real val="0.5"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_width">
          <dia:real val="0.5"/>
        </dia:attribute>
      </dia:object>
    </dia:group>
  </dia:layer>
</dia:diagram>

Deleted art/CollRev3.dia.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
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




















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
<?xml version="1.0" encoding="UTF-8"?>
<dia:diagram xmlns:dia="http://www.lysator.liu.se/~alla/dia/">
  <dia:diagramdata>
    <dia:attribute name="background">
      <dia:color val="#ffffff"/>
    </dia:attribute>
    <dia:attribute name="pagebreak">
      <dia:color val="#000099"/>
    </dia:attribute>
    <dia:attribute name="paper">
      <dia:composite type="paper">
        <dia:attribute name="name">
          <dia:string>#Letter#</dia:string>
        </dia:attribute>
        <dia:attribute name="tmargin">
          <dia:real val="2.5399999618530273"/>
        </dia:attribute>
        <dia:attribute name="bmargin">
          <dia:real val="2.5399999618530273"/>
        </dia:attribute>
        <dia:attribute name="lmargin">
          <dia:real val="2.5399999618530273"/>
        </dia:attribute>
        <dia:attribute name="rmargin">
          <dia:real val="2.5399999618530273"/>
        </dia:attribute>
        <dia:attribute name="is_portrait">
          <dia:boolean val="true"/>
        </dia:attribute>
        <dia:attribute name="scaling">
          <dia:real val="1"/>
        </dia:attribute>
        <dia:attribute name="fitto">
          <dia:boolean val="false"/>
        </dia:attribute>
      </dia:composite>
    </dia:attribute>
    <dia:attribute name="grid">
      <dia:composite type="grid">
        <dia:attribute name="width_x">
          <dia:real val="1"/>
        </dia:attribute>
        <dia:attribute name="width_y">
          <dia:real val="1"/>
        </dia:attribute>
        <dia:attribute name="visible_x">
          <dia:int val="1"/>
        </dia:attribute>
        <dia:attribute name="visible_y">
          <dia:int val="1"/>
        </dia:attribute>
        <dia:composite type="color"/>
      </dia:composite>
    </dia:attribute>
    <dia:attribute name="color">
      <dia:color val="#d8e5e5"/>
    </dia:attribute>
    <dia:attribute name="guides">
      <dia:composite type="guides">
        <dia:attribute name="hguides"/>
        <dia:attribute name="vguides"/>
      </dia:composite>
    </dia:attribute>
  </dia:diagramdata>
  <dia:layer name="Background" visible="true">
    <dia:group>
      <dia:object type="Standard - Text" version="1" id="O0">
        <dia:attribute name="obj_pos">
          <dia:point val="7,15"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="7,14.6;7,15.8"/>
        </dia:attribute>
        <dia:attribute name="text">
          <dia:composite type="text">
            <dia:attribute name="string">
              <dia:string>##</dia:string>
            </dia:attribute>
            <dia:attribute name="font">
              <dia:font family="sans" style="0" name="Helvetica"/>
            </dia:attribute>
            <dia:attribute name="height">
              <dia:real val="0.80000000000000004"/>
            </dia:attribute>
            <dia:attribute name="pos">
              <dia:point val="7,15"/>
            </dia:attribute>
            <dia:attribute name="color">
              <dia:color val="#000000"/>
            </dia:attribute>
            <dia:attribute name="alignment">
              <dia:enum val="0"/>
            </dia:attribute>
          </dia:composite>
        </dia:attribute>
        <dia:attribute name="valign">
          <dia:enum val="3"/>
        </dia:attribute>
      </dia:object>
      <dia:group>
        <dia:object type="Standard - Box" version="0" id="O1">
          <dia:attribute name="obj_pos">
            <dia:point val="13,1"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="12.95,0.95;19.05,3.05"/>
          </dia:attribute>
          <dia:attribute name="elem_corner">
            <dia:point val="13,1"/>
          </dia:attribute>
          <dia:attribute name="elem_width">
            <dia:real val="6"/>
          </dia:attribute>
          <dia:attribute name="elem_height">
            <dia:real val="2"/>
          </dia:attribute>
          <dia:attribute name="show_background">
            <dia:boolean val="true"/>
          </dia:attribute>
        </dia:object>
        <dia:object type="Standard - Text" version="1" id="O2">
          <dia:attribute name="obj_pos">
            <dia:point val="14.04,1.66397"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="14.04,1.12147;17.96,2.86147"/>
          </dia:attribute>
          <dia:attribute name="text">
            <dia:composite type="text">
              <dia:attribute name="string">
                <dia:string>#Line of
Development#</dia:string>
              </dia:attribute>
              <dia:attribute name="font">
                <dia:font family="sans" style="0" name="Helvetica"/>
              </dia:attribute>
              <dia:attribute name="height">
                <dia:real val="0.80000000000000004"/>
              </dia:attribute>
              <dia:attribute name="pos">
                <dia:point val="14.04,1.66397"/>
              </dia:attribute>
              <dia:attribute name="color">
                <dia:color val="#000000"/>
              </dia:attribute>
              <dia:attribute name="alignment">
                <dia:enum val="0"/>
              </dia:attribute>
            </dia:composite>
          </dia:attribute>
          <dia:attribute name="valign">
            <dia:enum val="3"/>
          </dia:attribute>
        </dia:object>
      </dia:group>
      <dia:group>
        <dia:object type="Standard - Box" version="0" id="O3">
          <dia:attribute name="obj_pos">
            <dia:point val="3,1"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="2.95,0.95;9.05,3.05"/>
          </dia:attribute>
          <dia:attribute name="elem_corner">
            <dia:point val="3,1"/>
          </dia:attribute>
          <dia:attribute name="elem_width">
            <dia:real val="6"/>
          </dia:attribute>
          <dia:attribute name="elem_height">
            <dia:real val="2"/>
          </dia:attribute>
          <dia:attribute name="show_background">
            <dia:boolean val="true"/>
          </dia:attribute>
        </dia:object>
        <dia:object type="Standard - Text" version="1" id="O4">
          <dia:attribute name="obj_pos">
            <dia:point val="4.04,1.66397"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="4.04,1.12147;7.96,2.86147"/>
          </dia:attribute>
          <dia:attribute name="text">
            <dia:composite type="text">
              <dia:attribute name="string">
                <dia:string>#Line' of
Development#</dia:string>
              </dia:attribute>
              <dia:attribute name="font">
                <dia:font family="sans" style="0" name="Helvetica"/>
              </dia:attribute>
              <dia:attribute name="height">
                <dia:real val="0.80000000000000004"/>
              </dia:attribute>
              <dia:attribute name="pos">
                <dia:point val="4.04,1.66397"/>
              </dia:attribute>
              <dia:attribute name="color">
                <dia:color val="#000000"/>
              </dia:attribute>
              <dia:attribute name="alignment">
                <dia:enum val="0"/>
              </dia:attribute>
            </dia:composite>
          </dia:attribute>
          <dia:attribute name="valign">
            <dia:enum val="3"/>
          </dia:attribute>
        </dia:object>
      </dia:group>
      <dia:group>
        <dia:object type="Standard - Box" version="0" id="O5">
          <dia:attribute name="obj_pos">
            <dia:point val="3,13"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="2.95,12.95;9.05,15.05"/>
          </dia:attribute>
          <dia:attribute name="elem_corner">
            <dia:point val="3,13"/>
          </dia:attribute>
          <dia:attribute name="elem_width">
            <dia:real val="6"/>
          </dia:attribute>
          <dia:attribute name="elem_height">
            <dia:real val="2"/>
          </dia:attribute>
          <dia:attribute name="show_background">
            <dia:boolean val="true"/>
          </dia:attribute>
        </dia:object>
        <dia:object type="Standard - Text" version="1" id="O6">
          <dia:attribute name="obj_pos">
            <dia:point val="3.94,13.664"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="3.94,13.1215;8.06,14.8615"/>
          </dia:attribute>
          <dia:attribute name="text">
            <dia:composite type="text">
              <dia:attribute name="string">
                <dia:string>#Revision''
(Branch Start)#</dia:string>
              </dia:attribute>
              <dia:attribute name="font">
                <dia:font family="sans" style="0" name="Helvetica"/>
              </dia:attribute>
              <dia:attribute name="height">
                <dia:real val="0.80000000000000004"/>
              </dia:attribute>
              <dia:attribute name="pos">
                <dia:point val="3.94,13.664"/>
              </dia:attribute>
              <dia:attribute name="color">
                <dia:color val="#000000"/>
              </dia:attribute>
              <dia:attribute name="alignment">
                <dia:enum val="0"/>
              </dia:attribute>
            </dia:composite>
          </dia:attribute>
          <dia:attribute name="valign">
            <dia:enum val="3"/>
          </dia:attribute>
        </dia:object>
      </dia:group>
      <dia:group>
        <dia:object type="Standard - Box" version="0" id="O7">
          <dia:attribute name="obj_pos">
            <dia:point val="13,7"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="12.95,6.95;19.05,9.05"/>
          </dia:attribute>
          <dia:attribute name="elem_corner">
            <dia:point val="13,7"/>
          </dia:attribute>
          <dia:attribute name="elem_width">
            <dia:real val="6"/>
          </dia:attribute>
          <dia:attribute name="elem_height">
            <dia:real val="2"/>
          </dia:attribute>
          <dia:attribute name="show_background">
            <dia:boolean val="true"/>
          </dia:attribute>
        </dia:object>
        <dia:object type="Standard - Text" version="1" id="O8">
          <dia:attribute name="obj_pos">
            <dia:point val="14.7733,8.0725"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="14.7733,7.53;17.2096,8.47"/>
          </dia:attribute>
          <dia:attribute name="text">
            <dia:composite type="text">
              <dia:attribute name="string">
                <dia:string>#Revision#</dia:string>
              </dia:attribute>
              <dia:attribute name="font">
                <dia:font family="sans" style="0" name="Helvetica"/>
              </dia:attribute>
              <dia:attribute name="height">
                <dia:real val="0.80000000000000004"/>
              </dia:attribute>
              <dia:attribute name="pos">
                <dia:point val="14.7733,8.0725"/>
              </dia:attribute>
              <dia:attribute name="color">
                <dia:color val="#000000"/>
              </dia:attribute>
              <dia:attribute name="alignment">
                <dia:enum val="0"/>
              </dia:attribute>
            </dia:composite>
          </dia:attribute>
          <dia:attribute name="valign">
            <dia:enum val="3"/>
          </dia:attribute>
        </dia:object>
      </dia:group>
      <dia:group>
        <dia:object type="Standard - Box" version="0" id="O9">
          <dia:attribute name="obj_pos">
            <dia:point val="23,13"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="22.95,12.95;29.05,15.05"/>
          </dia:attribute>
          <dia:attribute name="elem_corner">
            <dia:point val="23,13"/>
          </dia:attribute>
          <dia:attribute name="elem_width">
            <dia:real val="6"/>
          </dia:attribute>
          <dia:attribute name="elem_height">
            <dia:real val="2"/>
          </dia:attribute>
          <dia:attribute name="show_background">
            <dia:boolean val="true"/>
          </dia:attribute>
        </dia:object>
        <dia:object type="Standard - Text" version="1" id="O10">
          <dia:attribute name="obj_pos">
            <dia:point val="25.5039,14.0725"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="25.5039,13.53;26.479,14.47"/>
          </dia:attribute>
          <dia:attribute name="text">
            <dia:composite type="text">
              <dia:attribute name="string">
                <dia:string>#File#</dia:string>
              </dia:attribute>
              <dia:attribute name="font">
                <dia:font family="sans" style="0" name="Helvetica"/>
              </dia:attribute>
              <dia:attribute name="height">
                <dia:real val="0.80000000000000004"/>
              </dia:attribute>
              <dia:attribute name="pos">
                <dia:point val="25.5039,14.0725"/>
              </dia:attribute>
              <dia:attribute name="color">
                <dia:color val="#000000"/>
              </dia:attribute>
              <dia:attribute name="alignment">
                <dia:enum val="0"/>
              </dia:attribute>
            </dia:composite>
          </dia:attribute>
          <dia:attribute name="valign">
            <dia:enum val="3"/>
          </dia:attribute>
        </dia:object>
      </dia:group>
      <dia:group>
        <dia:object type="Standard - Box" version="0" id="O11">
          <dia:attribute name="obj_pos">
            <dia:point val="13,13"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="12.95,12.95;19.05,15.05"/>
          </dia:attribute>
          <dia:attribute name="elem_corner">
            <dia:point val="13,13"/>
          </dia:attribute>
          <dia:attribute name="elem_width">
            <dia:real val="6"/>
          </dia:attribute>
          <dia:attribute name="elem_height">
            <dia:real val="2"/>
          </dia:attribute>
          <dia:attribute name="show_background">
            <dia:boolean val="true"/>
          </dia:attribute>
        </dia:object>
        <dia:object type="Standard - Text" version="1" id="O12">
          <dia:attribute name="obj_pos">
            <dia:point val="14.6933,13.6725"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="14.6933,13.13;17.2896,14.87"/>
          </dia:attribute>
          <dia:attribute name="text">
            <dia:composite type="text">
              <dia:attribute name="string">
                <dia:string>#Revision'
(Child)#</dia:string>
              </dia:attribute>
              <dia:attribute name="font">
                <dia:font family="sans" style="0" name="Helvetica"/>
              </dia:attribute>
              <dia:attribute name="height">
                <dia:real val="0.80000000000000004"/>
              </dia:attribute>
              <dia:attribute name="pos">
                <dia:point val="14.6933,13.6725"/>
              </dia:attribute>
              <dia:attribute name="color">
                <dia:color val="#000000"/>
              </dia:attribute>
              <dia:attribute name="alignment">
                <dia:enum val="0"/>
              </dia:attribute>
            </dia:composite>
          </dia:attribute>
          <dia:attribute name="valign">
            <dia:enum val="3"/>
          </dia:attribute>
        </dia:object>
      </dia:group>
      <dia:object type="Standard - Line" version="0" id="O13">
        <dia:attribute name="obj_pos">
          <dia:point val="19,14"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="18.95,13.5;23.05,14.5"/>
        </dia:attribute>
        <dia:attribute name="conn_endpoints">
          <dia:point val="19,14"/>
          <dia:point val="23,14"/>
        </dia:attribute>
        <dia:attribute name="numcp">
          <dia:int val="1"/>
        </dia:attribute>
        <dia:attribute name="end_arrow">
          <dia:enum val="22"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_length">
          <dia:real val="0.5"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_width">
          <dia:real val="0.5"/>
        </dia:attribute>
      </dia:object>
      <dia:object type="Standard - Line" version="0" id="O14">
        <dia:attribute name="obj_pos">
          <dia:point val="6,13"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="5.5,2.95;6.5,13.05"/>
        </dia:attribute>
        <dia:attribute name="conn_endpoints">
          <dia:point val="6,13"/>
          <dia:point val="6,3"/>
        </dia:attribute>
        <dia:attribute name="numcp">
          <dia:int val="1"/>
        </dia:attribute>
        <dia:attribute name="end_arrow">
          <dia:enum val="22"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_length">
          <dia:real val="0.5"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_width">
          <dia:real val="0.5"/>
        </dia:attribute>
      </dia:object>
      <dia:object type="Standard - Line" version="0" id="O15">
        <dia:attribute name="obj_pos">
          <dia:point val="18,9"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="17.5,8.95;18.5,13.05"/>
        </dia:attribute>
        <dia:attribute name="conn_endpoints">
          <dia:point val="18,9"/>
          <dia:point val="18,13"/>
        </dia:attribute>
        <dia:attribute name="numcp">
          <dia:int val="1"/>
        </dia:attribute>
        <dia:attribute name="end_arrow">
          <dia:enum val="22"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_length">
          <dia:real val="0.5"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_width">
          <dia:real val="0.5"/>
        </dia:attribute>
      </dia:object>
      <dia:object type="Standard - Line" version="0" id="O16">
        <dia:attribute name="obj_pos">
          <dia:point val="16,7"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="15.5,2.95;16.5,7.05"/>
        </dia:attribute>
        <dia:attribute name="conn_endpoints">
          <dia:point val="16,7"/>
          <dia:point val="16,3"/>
        </dia:attribute>
        <dia:attribute name="numcp">
          <dia:int val="1"/>
        </dia:attribute>
        <dia:attribute name="end_arrow">
          <dia:enum val="22"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_length">
          <dia:real val="0.5"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_width">
          <dia:real val="0.5"/>
        </dia:attribute>
      </dia:object>
      <dia:object type="Standard - Line" version="0" id="O17">
        <dia:attribute name="obj_pos">
          <dia:point val="14,13"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="13.5,8.95;14.5,13.05"/>
        </dia:attribute>
        <dia:attribute name="conn_endpoints">
          <dia:point val="14,13"/>
          <dia:point val="14,9"/>
        </dia:attribute>
        <dia:attribute name="numcp">
          <dia:int val="1"/>
        </dia:attribute>
        <dia:attribute name="end_arrow">
          <dia:enum val="22"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_length">
          <dia:real val="0.5"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_width">
          <dia:real val="0.5"/>
        </dia:attribute>
      </dia:object>
      <dia:object type="Standard - ZigZagLine" version="1" id="O18">
        <dia:attribute name="obj_pos">
          <dia:point val="9,14"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="8.95,7.5;13.05,14.05"/>
        </dia:attribute>
        <dia:attribute name="orth_points">
          <dia:point val="9,14"/>
          <dia:point val="11,14"/>
          <dia:point val="11,8"/>
          <dia:point val="13,8"/>
        </dia:attribute>
        <dia:attribute name="orth_orient">
          <dia:enum val="0"/>
          <dia:enum val="1"/>
          <dia:enum val="0"/>
        </dia:attribute>
        <dia:attribute name="autorouting">
          <dia:boolean val="true"/>
        </dia:attribute>
        <dia:attribute name="end_arrow">
          <dia:enum val="22"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_length">
          <dia:real val="0.5"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_width">
          <dia:real val="0.5"/>
        </dia:attribute>
      </dia:object>
      <dia:object type="Standard - ZigZagLine" version="1" id="O19">
        <dia:attribute name="obj_pos">
          <dia:point val="6,15"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="5.95,14.95;26.5,17.05"/>
        </dia:attribute>
        <dia:attribute name="orth_points">
          <dia:point val="6,15"/>
          <dia:point val="6,17"/>
          <dia:point val="26,17"/>
          <dia:point val="26,15"/>
        </dia:attribute>
        <dia:attribute name="orth_orient">
          <dia:enum val="1"/>
          <dia:enum val="0"/>
          <dia:enum val="1"/>
        </dia:attribute>
        <dia:attribute name="autorouting">
          <dia:boolean val="false"/>
        </dia:attribute>
        <dia:attribute name="end_arrow">
          <dia:enum val="22"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_length">
          <dia:real val="0.5"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_width">
          <dia:real val="0.5"/>
        </dia:attribute>
      </dia:object>
      <dia:object type="Standard - ZigZagLine" version="1" id="O20">
        <dia:attribute name="obj_pos">
          <dia:point val="19,8"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="18.95,7.95;26.5,13.05"/>
        </dia:attribute>
        <dia:attribute name="orth_points">
          <dia:point val="19,8"/>
          <dia:point val="26,8"/>
          <dia:point val="26,13"/>
        </dia:attribute>
        <dia:attribute name="orth_orient">
          <dia:enum val="0"/>
          <dia:enum val="1"/>
        </dia:attribute>
        <dia:attribute name="autorouting">
          <dia:boolean val="true"/>
        </dia:attribute>
        <dia:attribute name="end_arrow">
          <dia:enum val="22"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_length">
          <dia:real val="0.5"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_width">
          <dia:real val="0.5"/>
        </dia:attribute>
      </dia:object>
      <dia:object type="Standard - Text" version="1" id="O21">
        <dia:attribute name="obj_pos">
          <dia:point val="6,12"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="6,11.4575;8.2125,13.1975"/>
        </dia:attribute>
        <dia:attribute name="text">
          <dia:composite type="text">
            <dia:attribute name="string">
              <dia:string># branch
 parent#</dia:string>
            </dia:attribute>
            <dia:attribute name="font">
              <dia:font family="sans" style="0" name="Helvetica"/>
            </dia:attribute>
            <dia:attribute name="height">
              <dia:real val="0.80000000000000004"/>
            </dia:attribute>
            <dia:attribute name="pos">
              <dia:point val="6,12"/>
            </dia:attribute>
            <dia:attribute name="color">
              <dia:color val="#000000"/>
            </dia:attribute>
            <dia:attribute name="alignment">
              <dia:enum val="0"/>
            </dia:attribute>
          </dia:composite>
        </dia:attribute>
        <dia:attribute name="valign">
          <dia:enum val="3"/>
        </dia:attribute>
      </dia:object>
      <dia:object type="Standard - Text" version="1" id="O22">
        <dia:attribute name="obj_pos">
          <dia:point val="14,12"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="14,11.4575;16.0947,12.3975"/>
        </dia:attribute>
        <dia:attribute name="text">
          <dia:composite type="text">
            <dia:attribute name="string">
              <dia:string># parent#</dia:string>
            </dia:attribute>
            <dia:attribute name="font">
              <dia:font family="sans" style="0" name="Helvetica"/>
            </dia:attribute>
            <dia:attribute name="height">
              <dia:real val="0.80000000000000004"/>
            </dia:attribute>
            <dia:attribute name="pos">
              <dia:point val="14,12"/>
            </dia:attribute>
            <dia:attribute name="color">
              <dia:color val="#000000"/>
            </dia:attribute>
            <dia:attribute name="alignment">
              <dia:enum val="0"/>
            </dia:attribute>
          </dia:composite>
        </dia:attribute>
        <dia:attribute name="valign">
          <dia:enum val="3"/>
        </dia:attribute>
      </dia:object>
      <dia:object type="Standard - Text" version="1" id="O23">
        <dia:attribute name="obj_pos">
          <dia:point val="18,10"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="18,9.4575;19.57,10.3975"/>
        </dia:attribute>
        <dia:attribute name="text">
          <dia:composite type="text">
            <dia:attribute name="string">
              <dia:string># child#</dia:string>
            </dia:attribute>
            <dia:attribute name="font">
              <dia:font family="sans" style="0" name="Helvetica"/>
            </dia:attribute>
            <dia:attribute name="height">
              <dia:real val="0.80000000000000004"/>
            </dia:attribute>
            <dia:attribute name="pos">
              <dia:point val="18,10"/>
            </dia:attribute>
            <dia:attribute name="color">
              <dia:color val="#000000"/>
            </dia:attribute>
            <dia:attribute name="alignment">
              <dia:enum val="0"/>
            </dia:attribute>
          </dia:composite>
        </dia:attribute>
        <dia:attribute name="valign">
          <dia:enum val="3"/>
        </dia:attribute>
      </dia:object>
      <dia:object type="Standard - Text" version="1" id="O24">
        <dia:attribute name="obj_pos">
          <dia:point val="19,7"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="19,6.52875;22.245,8.39875"/>
        </dia:attribute>
        <dia:attribute name="text">
          <dia:composite type="text">
            <dia:attribute name="string">
              <dia:string>#
 belongs to#</dia:string>
            </dia:attribute>
            <dia:attribute name="font">
              <dia:font family="sans" style="0" name="Helvetica"/>
            </dia:attribute>
            <dia:attribute name="height">
              <dia:real val="0.80000000000000004"/>
            </dia:attribute>
            <dia:attribute name="pos">
              <dia:point val="19,7"/>
            </dia:attribute>
            <dia:attribute name="color">
              <dia:color val="#000000"/>
            </dia:attribute>
            <dia:attribute name="alignment">
              <dia:enum val="0"/>
            </dia:attribute>
          </dia:composite>
        </dia:attribute>
        <dia:attribute name="valign">
          <dia:enum val="3"/>
        </dia:attribute>
      </dia:object>
      <dia:object type="Standard - Text" version="1" id="O25">
        <dia:attribute name="obj_pos">
          <dia:point val="6,15"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="6,14.4575;9.245,16.1975"/>
        </dia:attribute>
        <dia:attribute name="text">
          <dia:composite type="text">
            <dia:attribute name="string">
              <dia:string>#
 belongs to#</dia:string>
            </dia:attribute>
            <dia:attribute name="font">
              <dia:font family="sans" style="0" name="Helvetica"/>
            </dia:attribute>
            <dia:attribute name="height">
              <dia:real val="0.80000000000000004"/>
            </dia:attribute>
            <dia:attribute name="pos">
              <dia:point val="6,15"/>
            </dia:attribute>
            <dia:attribute name="color">
              <dia:color val="#000000"/>
            </dia:attribute>
            <dia:attribute name="alignment">
              <dia:enum val="0"/>
            </dia:attribute>
          </dia:composite>
        </dia:attribute>
        <dia:attribute name="valign">
          <dia:enum val="3"/>
        </dia:attribute>
      </dia:object>
      <dia:object type="Standard - Text" version="1" id="O26">
        <dia:attribute name="obj_pos">
          <dia:point val="19,13"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="19,12.5288;22.245,14.3987"/>
        </dia:attribute>
        <dia:attribute name="text">
          <dia:composite type="text">
            <dia:attribute name="string">
              <dia:string>#
 belongs to#</dia:string>
            </dia:attribute>
            <dia:attribute name="font">
              <dia:font family="sans" style="0" name="Helvetica"/>
            </dia:attribute>
            <dia:attribute name="height">
              <dia:real val="0.80000000000000004"/>
            </dia:attribute>
            <dia:attribute name="pos">
              <dia:point val="19,13"/>
            </dia:attribute>
            <dia:attribute name="color">
              <dia:color val="#000000"/>
            </dia:attribute>
            <dia:attribute name="alignment">
              <dia:enum val="0"/>
            </dia:attribute>
          </dia:composite>
        </dia:attribute>
        <dia:attribute name="valign">
          <dia:enum val="3"/>
        </dia:attribute>
      </dia:object>
      <dia:object type="Standard - Text" version="1" id="O27">
        <dia:attribute name="obj_pos">
          <dia:point val="9,14"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="9,13.5288;11.0947,15.3987"/>
        </dia:attribute>
        <dia:attribute name="text">
          <dia:composite type="text">
            <dia:attribute name="string">
              <dia:string>#
 parent#</dia:string>
            </dia:attribute>
            <dia:attribute name="font">
              <dia:font family="sans" style="0" name="Helvetica"/>
            </dia:attribute>
            <dia:attribute name="height">
              <dia:real val="0.80000000000000004"/>
            </dia:attribute>
            <dia:attribute name="pos">
              <dia:point val="9,14"/>
            </dia:attribute>
            <dia:attribute name="color">
              <dia:color val="#000000"/>
            </dia:attribute>
            <dia:attribute name="alignment">
              <dia:enum val="0"/>
            </dia:attribute>
          </dia:composite>
        </dia:attribute>
        <dia:attribute name="valign">
          <dia:enum val="3"/>
        </dia:attribute>
      </dia:object>
      <dia:object type="Standard - ZigZagLine" version="1" id="O28">
        <dia:attribute name="obj_pos">
          <dia:point val="3,14"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="0.95,1.5;3.05,14.05"/>
        </dia:attribute>
        <dia:attribute name="orth_points">
          <dia:point val="3,14"/>
          <dia:point val="1,14"/>
          <dia:point val="1,2"/>
          <dia:point val="3,2"/>
        </dia:attribute>
        <dia:attribute name="orth_orient">
          <dia:enum val="0"/>
          <dia:enum val="1"/>
          <dia:enum val="0"/>
        </dia:attribute>
        <dia:attribute name="autorouting">
          <dia:boolean val="false"/>
        </dia:attribute>
        <dia:attribute name="end_arrow">
          <dia:enum val="22"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_length">
          <dia:real val="0.5"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_width">
          <dia:real val="0.5"/>
        </dia:attribute>
      </dia:object>
      <dia:object type="Standard - Text" version="1" id="O29">
        <dia:attribute name="obj_pos">
          <dia:point val="1,12"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="1,11.4575;4.245,12.3975"/>
        </dia:attribute>
        <dia:attribute name="text">
          <dia:composite type="text">
            <dia:attribute name="string">
              <dia:string># belongs to#</dia:string>
            </dia:attribute>
            <dia:attribute name="font">
              <dia:font family="sans" style="0" name="Helvetica"/>
            </dia:attribute>
            <dia:attribute name="height">
              <dia:real val="0.80000000000000004"/>
            </dia:attribute>
            <dia:attribute name="pos">
              <dia:point val="1,12"/>
            </dia:attribute>
            <dia:attribute name="color">
              <dia:color val="#000000"/>
            </dia:attribute>
            <dia:attribute name="alignment">
              <dia:enum val="0"/>
            </dia:attribute>
          </dia:composite>
        </dia:attribute>
        <dia:attribute name="valign">
          <dia:enum val="3"/>
        </dia:attribute>
      </dia:object>
      <dia:object type="Standard - Text" version="1" id="O30">
        <dia:attribute name="obj_pos">
          <dia:point val="10,2.12153"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="10,1.10403;12.205,2.86403"/>
        </dia:attribute>
        <dia:attribute name="text">
          <dia:composite type="text">
            <dia:attribute name="string">
              <dia:string>#=/=#</dia:string>
            </dia:attribute>
            <dia:attribute name="font">
              <dia:font family="sans" style="0" name="Helvetica"/>
            </dia:attribute>
            <dia:attribute name="height">
              <dia:real val="1.5"/>
            </dia:attribute>
            <dia:attribute name="pos">
              <dia:point val="10,2.12153"/>
            </dia:attribute>
            <dia:attribute name="color">
              <dia:color val="#000000"/>
            </dia:attribute>
            <dia:attribute name="alignment">
              <dia:enum val="0"/>
            </dia:attribute>
          </dia:composite>
        </dia:attribute>
        <dia:attribute name="valign">
          <dia:enum val="3"/>
        </dia:attribute>
      </dia:object>
    </dia:group>
  </dia:layer>
</dia:diagram>

Deleted art/CollRev4.dia.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
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
































































































































































































































































































































































































































































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
<?xml version="1.0" encoding="UTF-8"?>
<dia:diagram xmlns:dia="http://www.lysator.liu.se/~alla/dia/">
  <dia:diagramdata>
    <dia:attribute name="background">
      <dia:color val="#ffffff"/>
    </dia:attribute>
    <dia:attribute name="pagebreak">
      <dia:color val="#000099"/>
    </dia:attribute>
    <dia:attribute name="paper">
      <dia:composite type="paper">
        <dia:attribute name="name">
          <dia:string>#Letter#</dia:string>
        </dia:attribute>
        <dia:attribute name="tmargin">
          <dia:real val="2.5399999618530273"/>
        </dia:attribute>
        <dia:attribute name="bmargin">
          <dia:real val="2.5399999618530273"/>
        </dia:attribute>
        <dia:attribute name="lmargin">
          <dia:real val="2.5399999618530273"/>
        </dia:attribute>
        <dia:attribute name="rmargin">
          <dia:real val="2.5399999618530273"/>
        </dia:attribute>
        <dia:attribute name="is_portrait">
          <dia:boolean val="true"/>
        </dia:attribute>
        <dia:attribute name="scaling">
          <dia:real val="1"/>
        </dia:attribute>
        <dia:attribute name="fitto">
          <dia:boolean val="false"/>
        </dia:attribute>
      </dia:composite>
    </dia:attribute>
    <dia:attribute name="grid">
      <dia:composite type="grid">
        <dia:attribute name="width_x">
          <dia:real val="1"/>
        </dia:attribute>
        <dia:attribute name="width_y">
          <dia:real val="1"/>
        </dia:attribute>
        <dia:attribute name="visible_x">
          <dia:int val="1"/>
        </dia:attribute>
        <dia:attribute name="visible_y">
          <dia:int val="1"/>
        </dia:attribute>
        <dia:composite type="color"/>
      </dia:composite>
    </dia:attribute>
    <dia:attribute name="color">
      <dia:color val="#d8e5e5"/>
    </dia:attribute>
    <dia:attribute name="guides">
      <dia:composite type="guides">
        <dia:attribute name="hguides"/>
        <dia:attribute name="vguides"/>
      </dia:composite>
    </dia:attribute>
  </dia:diagramdata>
  <dia:layer name="Background" visible="true">
    <dia:group>
      <dia:group>
        <dia:object type="Standard - Box" version="0" id="O0">
          <dia:attribute name="obj_pos">
            <dia:point val="3,8"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="2.95,7.95;9.05,10.05"/>
          </dia:attribute>
          <dia:attribute name="elem_corner">
            <dia:point val="3,8"/>
          </dia:attribute>
          <dia:attribute name="elem_width">
            <dia:real val="6"/>
          </dia:attribute>
          <dia:attribute name="elem_height">
            <dia:real val="2"/>
          </dia:attribute>
          <dia:attribute name="show_background">
            <dia:boolean val="true"/>
          </dia:attribute>
        </dia:object>
        <dia:object type="Standard - Text" version="1" id="O1">
          <dia:attribute name="obj_pos">
            <dia:point val="4.11647,8.66397"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="4.11647,8.12147;7.86647,9.86147"/>
          </dia:attribute>
          <dia:attribute name="text">
            <dia:composite type="text">
              <dia:attribute name="string">
                <dia:string>#Revision'
(Child NTDB)#</dia:string>
              </dia:attribute>
              <dia:attribute name="font">
                <dia:font family="sans" style="0" name="Helvetica"/>
              </dia:attribute>
              <dia:attribute name="height">
                <dia:real val="0.80000000000000004"/>
              </dia:attribute>
              <dia:attribute name="pos">
                <dia:point val="4.11647,8.66397"/>
              </dia:attribute>
              <dia:attribute name="color">
                <dia:color val="#000000"/>
              </dia:attribute>
              <dia:attribute name="alignment">
                <dia:enum val="0"/>
              </dia:attribute>
            </dia:composite>
          </dia:attribute>
          <dia:attribute name="valign">
            <dia:enum val="3"/>
          </dia:attribute>
        </dia:object>
      </dia:group>
      <dia:group>
        <dia:object type="Standard - Box" version="0" id="O2">
          <dia:attribute name="obj_pos">
            <dia:point val="5,1"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="4.95,0.95;11.05,3.05"/>
          </dia:attribute>
          <dia:attribute name="elem_corner">
            <dia:point val="5,1"/>
          </dia:attribute>
          <dia:attribute name="elem_width">
            <dia:real val="6"/>
          </dia:attribute>
          <dia:attribute name="elem_height">
            <dia:real val="2"/>
          </dia:attribute>
          <dia:attribute name="show_background">
            <dia:boolean val="true"/>
          </dia:attribute>
        </dia:object>
        <dia:object type="Standard - Text" version="1" id="O3">
          <dia:attribute name="obj_pos">
            <dia:point val="6.78184,1.6725"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="6.78184,1.13;9.21816,2.87"/>
          </dia:attribute>
          <dia:attribute name="text">
            <dia:composite type="text">
              <dia:attribute name="string">
                <dia:string>#Revision
(NTDB)#</dia:string>
              </dia:attribute>
              <dia:attribute name="font">
                <dia:font family="sans" style="0" name="Helvetica"/>
              </dia:attribute>
              <dia:attribute name="height">
                <dia:real val="0.80000000000000004"/>
              </dia:attribute>
              <dia:attribute name="pos">
                <dia:point val="6.78184,1.6725"/>
              </dia:attribute>
              <dia:attribute name="color">
                <dia:color val="#000000"/>
              </dia:attribute>
              <dia:attribute name="alignment">
                <dia:enum val="0"/>
              </dia:attribute>
            </dia:composite>
          </dia:attribute>
          <dia:attribute name="valign">
            <dia:enum val="3"/>
          </dia:attribute>
        </dia:object>
      </dia:group>
      <dia:group>
        <dia:object type="Standard - Box" version="0" id="O4">
          <dia:attribute name="obj_pos">
            <dia:point val="1,15"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="0.95,14.95;7.05,17.05"/>
          </dia:attribute>
          <dia:attribute name="elem_corner">
            <dia:point val="1,15"/>
          </dia:attribute>
          <dia:attribute name="elem_width">
            <dia:real val="6"/>
          </dia:attribute>
          <dia:attribute name="elem_height">
            <dia:real val="2"/>
          </dia:attribute>
          <dia:attribute name="show_background">
            <dia:boolean val="true"/>
          </dia:attribute>
        </dia:object>
        <dia:object type="Standard - Text" version="1" id="O5">
          <dia:attribute name="obj_pos">
            <dia:point val="2.11647,15.664"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="2.11647,15.1215;5.86647,16.8615"/>
          </dia:attribute>
          <dia:attribute name="text">
            <dia:composite type="text">
              <dia:attribute name="string">
                <dia:string>#Revision''
(Child NTDB)#</dia:string>
              </dia:attribute>
              <dia:attribute name="font">
                <dia:font family="sans" style="0" name="Helvetica"/>
              </dia:attribute>
              <dia:attribute name="height">
                <dia:real val="0.80000000000000004"/>
              </dia:attribute>
              <dia:attribute name="pos">
                <dia:point val="2.11647,15.664"/>
              </dia:attribute>
              <dia:attribute name="color">
                <dia:color val="#000000"/>
              </dia:attribute>
              <dia:attribute name="alignment">
                <dia:enum val="0"/>
              </dia:attribute>
            </dia:composite>
          </dia:attribute>
          <dia:attribute name="valign">
            <dia:enum val="3"/>
          </dia:attribute>
        </dia:object>
      </dia:group>
      <dia:group>
        <dia:object type="Standard - Box" version="0" id="O6">
          <dia:attribute name="obj_pos">
            <dia:point val="13,13"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="12.95,12.95;19.05,15.05"/>
          </dia:attribute>
          <dia:attribute name="elem_corner">
            <dia:point val="13,13"/>
          </dia:attribute>
          <dia:attribute name="elem_width">
            <dia:real val="6"/>
          </dia:attribute>
          <dia:attribute name="elem_height">
            <dia:real val="2"/>
          </dia:attribute>
          <dia:attribute name="show_background">
            <dia:boolean val="true"/>
          </dia:attribute>
        </dia:object>
        <dia:object type="Standard - Text" version="1" id="O7">
          <dia:attribute name="obj_pos">
            <dia:point val="14.2915,13.664"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="14.2915,13.1215;17.6915,14.8615"/>
          </dia:attribute>
          <dia:attribute name="text">
            <dia:composite type="text">
              <dia:attribute name="string">
                <dia:string>#Revision""
(non-NTDB)#</dia:string>
              </dia:attribute>
              <dia:attribute name="font">
                <dia:font family="sans" style="0" name="Helvetica"/>
              </dia:attribute>
              <dia:attribute name="height">
                <dia:real val="0.80000000000000004"/>
              </dia:attribute>
              <dia:attribute name="pos">
                <dia:point val="14.2915,13.664"/>
              </dia:attribute>
              <dia:attribute name="color">
                <dia:color val="#000000"/>
              </dia:attribute>
              <dia:attribute name="alignment">
                <dia:enum val="0"/>
              </dia:attribute>
            </dia:composite>
          </dia:attribute>
          <dia:attribute name="valign">
            <dia:enum val="3"/>
          </dia:attribute>
        </dia:object>
      </dia:group>
      <dia:object type="Standard - Line" version="0" id="O8">
        <dia:attribute name="obj_pos">
          <dia:point val="8,8"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="7.93501,2.76788;10.4828,8.06499"/>
        </dia:attribute>
        <dia:attribute name="conn_endpoints">
          <dia:point val="8,8"/>
          <dia:point val="10,3"/>
        </dia:attribute>
        <dia:attribute name="numcp">
          <dia:int val="1"/>
        </dia:attribute>
        <dia:attribute name="end_arrow">
          <dia:enum val="22"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_length">
          <dia:real val="0.5"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_width">
          <dia:real val="0.5"/>
        </dia:attribute>
      </dia:object>
      <dia:object type="Standard - Line" version="0" id="O9">
        <dia:attribute name="obj_pos">
          <dia:point val="6,3"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="3.51719,2.93501;6.06499,8.23212"/>
        </dia:attribute>
        <dia:attribute name="conn_endpoints">
          <dia:point val="6,3"/>
          <dia:point val="4,8"/>
        </dia:attribute>
        <dia:attribute name="numcp">
          <dia:int val="1"/>
        </dia:attribute>
        <dia:attribute name="end_arrow">
          <dia:enum val="22"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_length">
          <dia:real val="0.5"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_width">
          <dia:real val="0.5"/>
        </dia:attribute>
      </dia:object>
      <dia:object type="Standard - Line" version="0" id="O10">
        <dia:attribute name="obj_pos">
          <dia:point val="4,10"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="1.51719,9.93501;4.06499,15.2321"/>
        </dia:attribute>
        <dia:attribute name="conn_endpoints">
          <dia:point val="4,10"/>
          <dia:point val="2,15"/>
        </dia:attribute>
        <dia:attribute name="numcp">
          <dia:int val="1"/>
        </dia:attribute>
        <dia:attribute name="end_arrow">
          <dia:enum val="22"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_length">
          <dia:real val="0.5"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_width">
          <dia:real val="0.5"/>
        </dia:attribute>
      </dia:object>
      <dia:object type="Standard - Line" version="0" id="O11">
        <dia:attribute name="obj_pos">
          <dia:point val="6,15"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="5.93501,9.76788;8.48281,15.065"/>
        </dia:attribute>
        <dia:attribute name="conn_endpoints">
          <dia:point val="6,15"/>
          <dia:point val="8,10"/>
        </dia:attribute>
        <dia:attribute name="numcp">
          <dia:int val="1"/>
        </dia:attribute>
        <dia:attribute name="end_arrow">
          <dia:enum val="22"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_length">
          <dia:real val="0.5"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_width">
          <dia:real val="0.5"/>
        </dia:attribute>
      </dia:object>
      <dia:object type="Standard - Line" version="0" id="O12">
        <dia:attribute name="obj_pos">
          <dia:point val="16,13"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="8.70852,8.54107;16.0682,13.0682"/>
        </dia:attribute>
        <dia:attribute name="conn_endpoints">
          <dia:point val="16,13"/>
          <dia:point val="9,9"/>
        </dia:attribute>
        <dia:attribute name="numcp">
          <dia:int val="1"/>
        </dia:attribute>
        <dia:attribute name="end_arrow">
          <dia:enum val="22"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_length">
          <dia:real val="0.5"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_width">
          <dia:real val="0.5"/>
        </dia:attribute>
      </dia:object>
      <dia:object type="Standard - Line" version="0" id="O13">
        <dia:attribute name="obj_pos">
          <dia:point val="6,10"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="5.93178,9.93178;13.2915,14.4589"/>
        </dia:attribute>
        <dia:attribute name="conn_endpoints">
          <dia:point val="6,10"/>
          <dia:point val="13,14"/>
        </dia:attribute>
        <dia:attribute name="numcp">
          <dia:int val="1"/>
        </dia:attribute>
        <dia:attribute name="end_arrow">
          <dia:enum val="22"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_length">
          <dia:real val="0.5"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_width">
          <dia:real val="0.5"/>
        </dia:attribute>
      </dia:object>
      <dia:object type="Standard - Text" version="1" id="O14">
        <dia:attribute name="obj_pos">
          <dia:point val="6,14"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="6,13.5288;8.27969,15.3987"/>
        </dia:attribute>
        <dia:attribute name="text">
          <dia:composite type="text">
            <dia:attribute name="string">
              <dia:string>#
  parent#</dia:string>
            </dia:attribute>
            <dia:attribute name="font">
              <dia:font family="sans" style="0" name="Helvetica"/>
            </dia:attribute>
            <dia:attribute name="height">
              <dia:real val="0.80000000000000004"/>
            </dia:attribute>
            <dia:attribute name="pos">
              <dia:point val="6,14"/>
            </dia:attribute>
            <dia:attribute name="color">
              <dia:color val="#000000"/>
            </dia:attribute>
            <dia:attribute name="alignment">
              <dia:enum val="0"/>
            </dia:attribute>
          </dia:composite>
        </dia:attribute>
        <dia:attribute name="valign">
          <dia:enum val="3"/>
        </dia:attribute>
      </dia:object>
      <dia:object type="Standard - Text" version="1" id="O15">
        <dia:attribute name="obj_pos">
          <dia:point val="6,3"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="6,2.4575;7.385,4.1975"/>
        </dia:attribute>
        <dia:attribute name="text">
          <dia:composite type="text">
            <dia:attribute name="string">
              <dia:string>#
child#</dia:string>
            </dia:attribute>
            <dia:attribute name="font">
              <dia:font family="sans" style="0" name="Helvetica"/>
            </dia:attribute>
            <dia:attribute name="height">
              <dia:real val="0.80000000000000004"/>
            </dia:attribute>
            <dia:attribute name="pos">
              <dia:point val="6,3"/>
            </dia:attribute>
            <dia:attribute name="color">
              <dia:color val="#000000"/>
            </dia:attribute>
            <dia:attribute name="alignment">
              <dia:enum val="0"/>
            </dia:attribute>
          </dia:composite>
        </dia:attribute>
        <dia:attribute name="valign">
          <dia:enum val="3"/>
        </dia:attribute>
      </dia:object>
      <dia:object type="Standard - Text" version="1" id="O16">
        <dia:attribute name="obj_pos">
          <dia:point val="4,10"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="4,9.4575;5.385,11.1975"/>
        </dia:attribute>
        <dia:attribute name="text">
          <dia:composite type="text">
            <dia:attribute name="string">
              <dia:string>#
child#</dia:string>
            </dia:attribute>
            <dia:attribute name="font">
              <dia:font family="sans" style="0" name="Helvetica"/>
            </dia:attribute>
            <dia:attribute name="height">
              <dia:real val="0.80000000000000004"/>
            </dia:attribute>
            <dia:attribute name="pos">
              <dia:point val="4,10"/>
            </dia:attribute>
            <dia:attribute name="color">
              <dia:color val="#000000"/>
            </dia:attribute>
            <dia:attribute name="alignment">
              <dia:enum val="0"/>
            </dia:attribute>
          </dia:composite>
        </dia:attribute>
        <dia:attribute name="valign">
          <dia:enum val="3"/>
        </dia:attribute>
      </dia:object>
      <dia:object type="Standard - Text" version="1" id="O17">
        <dia:attribute name="obj_pos">
          <dia:point val="15,12"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="15,11.5288;18.3897,13.3987"/>
        </dia:attribute>
        <dia:attribute name="text">
          <dia:composite type="text">
            <dia:attribute name="string">
              <dia:string>#
    dbparent#</dia:string>
            </dia:attribute>
            <dia:attribute name="font">
              <dia:font family="sans" style="0" name="Helvetica"/>
            </dia:attribute>
            <dia:attribute name="height">
              <dia:real val="0.80000000000000004"/>
            </dia:attribute>
            <dia:attribute name="pos">
              <dia:point val="15,12"/>
            </dia:attribute>
            <dia:attribute name="color">
              <dia:color val="#000000"/>
            </dia:attribute>
            <dia:attribute name="alignment">
              <dia:enum val="0"/>
            </dia:attribute>
          </dia:composite>
        </dia:attribute>
        <dia:attribute name="valign">
          <dia:enum val="3"/>
        </dia:attribute>
      </dia:object>
      <dia:object type="Standard - Text" version="1" id="O18">
        <dia:attribute name="obj_pos">
          <dia:point val="9,12"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="9,11.4575;11.865,12.3975"/>
        </dia:attribute>
        <dia:attribute name="text">
          <dia:composite type="text">
            <dia:attribute name="string">
              <dia:string>#    dbchild#</dia:string>
            </dia:attribute>
            <dia:attribute name="font">
              <dia:font family="sans" style="0" name="Helvetica"/>
            </dia:attribute>
            <dia:attribute name="height">
              <dia:real val="0.80000000000000004"/>
            </dia:attribute>
            <dia:attribute name="pos">
              <dia:point val="9,12"/>
            </dia:attribute>
            <dia:attribute name="color">
              <dia:color val="#000000"/>
            </dia:attribute>
            <dia:attribute name="alignment">
              <dia:enum val="0"/>
            </dia:attribute>
          </dia:composite>
        </dia:attribute>
        <dia:attribute name="valign">
          <dia:enum val="3"/>
        </dia:attribute>
      </dia:object>
      <dia:object type="Standard - Text" version="1" id="O19">
        <dia:attribute name="obj_pos">
          <dia:point val="8,7"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="8,6.4575;10.2797,8.1975"/>
        </dia:attribute>
        <dia:attribute name="text">
          <dia:composite type="text">
            <dia:attribute name="string">
              <dia:string>#
  parent#</dia:string>
            </dia:attribute>
            <dia:attribute name="font">
              <dia:font family="sans" style="0" name="Helvetica"/>
            </dia:attribute>
            <dia:attribute name="height">
              <dia:real val="0.80000000000000004"/>
            </dia:attribute>
            <dia:attribute name="pos">
              <dia:point val="8,7"/>
            </dia:attribute>
            <dia:attribute name="color">
              <dia:color val="#000000"/>
            </dia:attribute>
            <dia:attribute name="alignment">
              <dia:enum val="0"/>
            </dia:attribute>
          </dia:composite>
        </dia:attribute>
        <dia:attribute name="valign">
          <dia:enum val="3"/>
        </dia:attribute>
      </dia:object>
    </dia:group>
  </dia:layer>
</dia:diagram>

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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234










































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
<?xml version="1.0" encoding="UTF-8"?>
<dia:diagram xmlns:dia="http://www.lysator.liu.se/~alla/dia/">
  <dia:diagramdata>
    <dia:attribute name="background">
      <dia:color val="#ffffff"/>
    </dia:attribute>
    <dia:attribute name="pagebreak">
      <dia:color val="#000099"/>
    </dia:attribute>
    <dia:attribute name="paper">
      <dia:composite type="paper">
        <dia:attribute name="name">
          <dia:string>#Letter#</dia:string>
        </dia:attribute>
        <dia:attribute name="tmargin">
          <dia:real val="2.5399999618530273"/>
        </dia:attribute>
        <dia:attribute name="bmargin">
          <dia:real val="2.5399999618530273"/>
        </dia:attribute>
        <dia:attribute name="lmargin">
          <dia:real val="2.5399999618530273"/>
        </dia:attribute>
        <dia:attribute name="rmargin">
          <dia:real val="2.5399999618530273"/>
        </dia:attribute>
        <dia:attribute name="is_portrait">
          <dia:boolean val="true"/>
        </dia:attribute>
        <dia:attribute name="scaling">
          <dia:real val="1"/>
        </dia:attribute>
        <dia:attribute name="fitto">
          <dia:boolean val="false"/>
        </dia:attribute>
      </dia:composite>
    </dia:attribute>
    <dia:attribute name="grid">
      <dia:composite type="grid">
        <dia:attribute name="width_x">
          <dia:real val="1"/>
        </dia:attribute>
        <dia:attribute name="width_y">
          <dia:real val="1"/>
        </dia:attribute>
        <dia:attribute name="visible_x">
          <dia:int val="1"/>
        </dia:attribute>
        <dia:attribute name="visible_y">
          <dia:int val="1"/>
        </dia:attribute>
        <dia:composite type="color"/>
      </dia:composite>
    </dia:attribute>
    <dia:attribute name="color">
      <dia:color val="#d8e5e5"/>
    </dia:attribute>
    <dia:attribute name="guides">
      <dia:composite type="guides">
        <dia:attribute name="hguides"/>
        <dia:attribute name="vguides"/>
      </dia:composite>
    </dia:attribute>
  </dia:diagramdata>
  <dia:layer name="Background" visible="true">
    <dia:group>
      <dia:group>
        <dia:object type="Standard - Box" version="0" id="O0">
          <dia:attribute name="obj_pos">
            <dia:point val="1,1"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="0.95,0.95;5.05,3.05"/>
          </dia:attribute>
          <dia:attribute name="elem_corner">
            <dia:point val="1,1"/>
          </dia:attribute>
          <dia:attribute name="elem_width">
            <dia:real val="4"/>
          </dia:attribute>
          <dia:attribute name="elem_height">
            <dia:real val="2"/>
          </dia:attribute>
          <dia:attribute name="show_background">
            <dia:boolean val="true"/>
          </dia:attribute>
        </dia:object>
        <dia:object type="Standard - Text" version="1" id="O1">
          <dia:attribute name="obj_pos">
            <dia:point val="2,2"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="2,1.4575;4.12,2.3975"/>
          </dia:attribute>
          <dia:attribute name="text">
            <dia:composite type="text">
              <dia:attribute name="string">
                <dia:string>#Header#</dia:string>
              </dia:attribute>
              <dia:attribute name="font">
                <dia:font family="sans" style="0" name="Helvetica"/>
              </dia:attribute>
              <dia:attribute name="height">
                <dia:real val="0.80000000000000004"/>
              </dia:attribute>
              <dia:attribute name="pos">
                <dia:point val="2,2"/>
              </dia:attribute>
              <dia:attribute name="color">
                <dia:color val="#000000"/>
              </dia:attribute>
              <dia:attribute name="alignment">
                <dia:enum val="0"/>
              </dia:attribute>
            </dia:composite>
          </dia:attribute>
          <dia:attribute name="valign">
            <dia:enum val="3"/>
          </dia:attribute>
        </dia:object>
      </dia:group>
      <dia:group>
        <dia:object type="Standard - Box" version="0" id="O2">
          <dia:attribute name="obj_pos">
            <dia:point val="5,1"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="4.95,0.95;9.05,3.05"/>
          </dia:attribute>
          <dia:attribute name="elem_corner">
            <dia:point val="5,1"/>
          </dia:attribute>
          <dia:attribute name="elem_width">
            <dia:real val="4"/>
          </dia:attribute>
          <dia:attribute name="elem_height">
            <dia:real val="2"/>
          </dia:attribute>
          <dia:attribute name="show_background">
            <dia:boolean val="true"/>
          </dia:attribute>
        </dia:object>
        <dia:object type="Standard - Text" version="1" id="O3">
          <dia:attribute name="obj_pos">
            <dia:point val="5.53022,2"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="5.53022,1.4575;8.45272,2.3975"/>
          </dia:attribute>
          <dia:attribute name="text">
            <dia:composite type="text">
              <dia:attribute name="string">
                <dia:string>#Segments#</dia:string>
              </dia:attribute>
              <dia:attribute name="font">
                <dia:font family="sans" style="0" name="Helvetica"/>
              </dia:attribute>
              <dia:attribute name="height">
                <dia:real val="0.80000000000000004"/>
              </dia:attribute>
              <dia:attribute name="pos">
                <dia:point val="5.53022,2"/>
              </dia:attribute>
              <dia:attribute name="color">
                <dia:color val="#000000"/>
              </dia:attribute>
              <dia:attribute name="alignment">
                <dia:enum val="0"/>
              </dia:attribute>
            </dia:composite>
          </dia:attribute>
          <dia:attribute name="valign">
            <dia:enum val="3"/>
          </dia:attribute>
        </dia:object>
      </dia:group>
      <dia:group>
        <dia:object type="Standard - Box" version="0" id="O4">
          <dia:attribute name="obj_pos">
            <dia:point val="9,1"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="8.95,0.95;13.05,3.05"/>
          </dia:attribute>
          <dia:attribute name="elem_corner">
            <dia:point val="9,1"/>
          </dia:attribute>
          <dia:attribute name="elem_width">
            <dia:real val="4"/>
          </dia:attribute>
          <dia:attribute name="elem_height">
            <dia:real val="2"/>
          </dia:attribute>
          <dia:attribute name="show_background">
            <dia:boolean val="true"/>
          </dia:attribute>
        </dia:object>
        <dia:object type="Standard - Text" version="1" id="O5">
          <dia:attribute name="obj_pos">
            <dia:point val="10,2"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="10,1.4575;11.7893,2.3975"/>
          </dia:attribute>
          <dia:attribute name="text">
            <dia:composite type="text">
              <dia:attribute name="string">
                <dia:string>#Trailer#</dia:string>
              </dia:attribute>
              <dia:attribute name="font">
                <dia:font family="sans" style="0" name="Helvetica"/>
              </dia:attribute>
              <dia:attribute name="height">
                <dia:real val="0.80000000000000004"/>
              </dia:attribute>
              <dia:attribute name="pos">
                <dia:point val="10,2"/>
              </dia:attribute>
              <dia:attribute name="color">
                <dia:color val="#000000"/>
              </dia:attribute>
              <dia:attribute name="alignment">
                <dia:enum val="0"/>
              </dia:attribute>
            </dia:composite>
          </dia:attribute>
          <dia:attribute name="valign">
            <dia:enum val="3"/>
          </dia:attribute>
        </dia:object>
      </dia:group>
    </dia:group>
  </dia:layer>
</dia:diagram>

Deleted art/delta2.dia.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
































































































































































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
<?xml version="1.0" encoding="UTF-8"?>
<dia:diagram xmlns:dia="http://www.lysator.liu.se/~alla/dia/">
  <dia:diagramdata>
    <dia:attribute name="background">
      <dia:color val="#ffffff"/>
    </dia:attribute>
    <dia:attribute name="pagebreak">
      <dia:color val="#000099"/>
    </dia:attribute>
    <dia:attribute name="paper">
      <dia:composite type="paper">
        <dia:attribute name="name">
          <dia:string>#Letter#</dia:string>
        </dia:attribute>
        <dia:attribute name="tmargin">
          <dia:real val="2.5399999618530273"/>
        </dia:attribute>
        <dia:attribute name="bmargin">
          <dia:real val="2.5399999618530273"/>
        </dia:attribute>
        <dia:attribute name="lmargin">
          <dia:real val="2.5399999618530273"/>
        </dia:attribute>
        <dia:attribute name="rmargin">
          <dia:real val="2.5399999618530273"/>
        </dia:attribute>
        <dia:attribute name="is_portrait">
          <dia:boolean val="true"/>
        </dia:attribute>
        <dia:attribute name="scaling">
          <dia:real val="1"/>
        </dia:attribute>
        <dia:attribute name="fitto">
          <dia:boolean val="false"/>
        </dia:attribute>
      </dia:composite>
    </dia:attribute>
    <dia:attribute name="grid">
      <dia:composite type="grid">
        <dia:attribute name="width_x">
          <dia:real val="1"/>
        </dia:attribute>
        <dia:attribute name="width_y">
          <dia:real val="1"/>
        </dia:attribute>
        <dia:attribute name="visible_x">
          <dia:int val="1"/>
        </dia:attribute>
        <dia:attribute name="visible_y">
          <dia:int val="1"/>
        </dia:attribute>
        <dia:composite type="color"/>
      </dia:composite>
    </dia:attribute>
    <dia:attribute name="color">
      <dia:color val="#d8e5e5"/>
    </dia:attribute>
    <dia:attribute name="guides">
      <dia:composite type="guides">
        <dia:attribute name="hguides"/>
        <dia:attribute name="vguides"/>
      </dia:composite>
    </dia:attribute>
  </dia:diagramdata>
  <dia:layer name="Background" visible="true">
    <dia:group>
      <dia:group>
        <dia:object type="Standard - Box" version="0" id="O0">
          <dia:attribute name="obj_pos">
            <dia:point val="4,7"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="3.95,6.95;9.05,9.05"/>
          </dia:attribute>
          <dia:attribute name="elem_corner">
            <dia:point val="4,7"/>
          </dia:attribute>
          <dia:attribute name="elem_width">
            <dia:real val="5"/>
          </dia:attribute>
          <dia:attribute name="elem_height">
            <dia:real val="2"/>
          </dia:attribute>
          <dia:attribute name="show_background">
            <dia:boolean val="true"/>
          </dia:attribute>
        </dia:object>
        <dia:object type="Standard - Text" version="1" id="O1">
          <dia:attribute name="obj_pos">
            <dia:point val="4.73538,8.06397"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="4.73538,7.52147;8.24757,8.46147"/>
          </dia:attribute>
          <dia:attribute name="text">
            <dia:composite type="text">
              <dia:attribute name="string">
                <dia:string>#Copy Range#</dia:string>
              </dia:attribute>
              <dia:attribute name="font">
                <dia:font family="sans" style="0" name="Helvetica"/>
              </dia:attribute>
              <dia:attribute name="height">
                <dia:real val="0.80000000000000004"/>
              </dia:attribute>
              <dia:attribute name="pos">
                <dia:point val="4.73538,8.06397"/>
              </dia:attribute>
              <dia:attribute name="color">
                <dia:color val="#000000"/>
              </dia:attribute>
              <dia:attribute name="alignment">
                <dia:enum val="0"/>
              </dia:attribute>
            </dia:composite>
          </dia:attribute>
          <dia:attribute name="valign">
            <dia:enum val="3"/>
          </dia:attribute>
        </dia:object>
      </dia:group>
      <dia:group>
        <dia:object type="Standard - Box" version="0" id="O2">
          <dia:attribute name="obj_pos">
            <dia:point val="4,4"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="3.95,3.95;9.05,6.05"/>
          </dia:attribute>
          <dia:attribute name="elem_corner">
            <dia:point val="4,4"/>
          </dia:attribute>
          <dia:attribute name="elem_width">
            <dia:real val="5"/>
          </dia:attribute>
          <dia:attribute name="elem_height">
            <dia:real val="2"/>
          </dia:attribute>
          <dia:attribute name="show_background">
            <dia:boolean val="true"/>
          </dia:attribute>
        </dia:object>
        <dia:object type="Standard - Text" version="1" id="O3">
          <dia:attribute name="obj_pos">
            <dia:point val="4.64772,5.06397"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="4.64772,4.52147;8.33522,5.46147"/>
          </dia:attribute>
          <dia:attribute name="text">
            <dia:composite type="text">
              <dia:attribute name="string">
                <dia:string>#Insert Literal#</dia:string>
              </dia:attribute>
              <dia:attribute name="font">
                <dia:font family="sans" style="0" name="Helvetica"/>
              </dia:attribute>
              <dia:attribute name="height">
                <dia:real val="0.80000000000000004"/>
              </dia:attribute>
              <dia:attribute name="pos">
                <dia:point val="4.64772,5.06397"/>
              </dia:attribute>
              <dia:attribute name="color">
                <dia:color val="#000000"/>
              </dia:attribute>
              <dia:attribute name="alignment">
                <dia:enum val="0"/>
              </dia:attribute>
            </dia:composite>
          </dia:attribute>
          <dia:attribute name="valign">
            <dia:enum val="3"/>
          </dia:attribute>
        </dia:object>
      </dia:group>
      <dia:object type="Standard - ZigZagLine" version="1" id="O4">
        <dia:attribute name="obj_pos">
          <dia:point val="2.5,3"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="2.45,2.95;4.05,5.5"/>
        </dia:attribute>
        <dia:attribute name="orth_points">
          <dia:point val="2.5,3"/>
          <dia:point val="2.5,5"/>
          <dia:point val="4,5"/>
        </dia:attribute>
        <dia:attribute name="orth_orient">
          <dia:enum val="1"/>
          <dia:enum val="0"/>
        </dia:attribute>
        <dia:attribute name="autorouting">
          <dia:boolean val="true"/>
        </dia:attribute>
        <dia:attribute name="end_arrow">
          <dia:enum val="22"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_length">
          <dia:real val="0.5"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_width">
          <dia:real val="0.5"/>
        </dia:attribute>
      </dia:object>
      <dia:object type="Standard - ZigZagLine" version="1" id="O5">
        <dia:attribute name="obj_pos">
          <dia:point val="2.5,3"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="2.45,2.95;4.05,8.5"/>
        </dia:attribute>
        <dia:attribute name="orth_points">
          <dia:point val="2.5,3"/>
          <dia:point val="2.5,8"/>
          <dia:point val="4,8"/>
        </dia:attribute>
        <dia:attribute name="orth_orient">
          <dia:enum val="1"/>
          <dia:enum val="0"/>
        </dia:attribute>
        <dia:attribute name="autorouting">
          <dia:boolean val="true"/>
        </dia:attribute>
        <dia:attribute name="end_arrow">
          <dia:enum val="22"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_length">
          <dia:real val="0.5"/>
        </dia:attribute>
        <dia:attribute name="end_arrow_width">
          <dia:real val="0.5"/>
        </dia:attribute>
      </dia:object>
      <dia:group>
        <dia:object type="Standard - Box" version="0" id="O6">
          <dia:attribute name="obj_pos">
            <dia:point val="1,1"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="0.95,0.95;2.05,3.05"/>
          </dia:attribute>
          <dia:attribute name="elem_corner">
            <dia:point val="1,1"/>
          </dia:attribute>
          <dia:attribute name="elem_width">
            <dia:real val="1"/>
          </dia:attribute>
          <dia:attribute name="elem_height">
            <dia:real val="2"/>
          </dia:attribute>
          <dia:attribute name="show_background">
            <dia:boolean val="true"/>
          </dia:attribute>
        </dia:object>
        <dia:object type="Standard - Box" version="0" id="O7">
          <dia:attribute name="obj_pos">
            <dia:point val="2,1"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="1.95,0.95;3.05,3.05"/>
          </dia:attribute>
          <dia:attribute name="elem_corner">
            <dia:point val="2,1"/>
          </dia:attribute>
          <dia:attribute name="elem_width">
            <dia:real val="1"/>
          </dia:attribute>
          <dia:attribute name="elem_height">
            <dia:real val="2"/>
          </dia:attribute>
          <dia:attribute name="show_background">
            <dia:boolean val="true"/>
          </dia:attribute>
        </dia:object>
        <dia:object type="Standard - Box" version="0" id="O8">
          <dia:attribute name="obj_pos">
            <dia:point val="3,1"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="2.95,0.95;4.05,3.05"/>
          </dia:attribute>
          <dia:attribute name="elem_corner">
            <dia:point val="3,1"/>
          </dia:attribute>
          <dia:attribute name="elem_width">
            <dia:real val="1"/>
          </dia:attribute>
          <dia:attribute name="elem_height">
            <dia:real val="2"/>
          </dia:attribute>
          <dia:attribute name="show_background">
            <dia:boolean val="true"/>
          </dia:attribute>
        </dia:object>
        <dia:object type="Standard - Box" version="0" id="O9">
          <dia:attribute name="obj_pos">
            <dia:point val="7,1"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="6.95,0.95;8.05,3.05"/>
          </dia:attribute>
          <dia:attribute name="elem_corner">
            <dia:point val="7,1"/>
          </dia:attribute>
          <dia:attribute name="elem_width">
            <dia:real val="1"/>
          </dia:attribute>
          <dia:attribute name="elem_height">
            <dia:real val="2"/>
          </dia:attribute>
          <dia:attribute name="show_background">
            <dia:boolean val="true"/>
          </dia:attribute>
        </dia:object>
        <dia:object type="Standard - Text" version="1" id="O10">
          <dia:attribute name="obj_pos">
            <dia:point val="5,2.06397"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="5,1.52147;5.8775,2.46147"/>
          </dia:attribute>
          <dia:attribute name="text">
            <dia:composite type="text">
              <dia:attribute name="string">
                <dia:string>#***#</dia:string>
              </dia:attribute>
              <dia:attribute name="font">
                <dia:font family="sans" style="0" name="Helvetica"/>
              </dia:attribute>
              <dia:attribute name="height">
                <dia:real val="0.80000000000000004"/>
              </dia:attribute>
              <dia:attribute name="pos">
                <dia:point val="5,2.06397"/>
              </dia:attribute>
              <dia:attribute name="color">
                <dia:color val="#000000"/>
              </dia:attribute>
              <dia:attribute name="alignment">
                <dia:enum val="0"/>
              </dia:attribute>
            </dia:composite>
          </dia:attribute>
          <dia:attribute name="valign">
            <dia:enum val="3"/>
          </dia:attribute>
        </dia:object>
      </dia:group>
    </dia:group>
  </dia:layer>
</dia:diagram>

Deleted art/delta3.dia.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289

































































































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
<?xml version="1.0" encoding="UTF-8"?>
<dia:diagram xmlns:dia="http://www.lysator.liu.se/~alla/dia/">
  <dia:diagramdata>
    <dia:attribute name="background">
      <dia:color val="#ffffff"/>
    </dia:attribute>
    <dia:attribute name="pagebreak">
      <dia:color val="#000099"/>
    </dia:attribute>
    <dia:attribute name="paper">
      <dia:composite type="paper">
        <dia:attribute name="name">
          <dia:string>#Letter#</dia:string>
        </dia:attribute>
        <dia:attribute name="tmargin">
          <dia:real val="2.5399999618530273"/>
        </dia:attribute>
        <dia:attribute name="bmargin">
          <dia:real val="2.5399999618530273"/>
        </dia:attribute>
        <dia:attribute name="lmargin">
          <dia:real val="2.5399999618530273"/>
        </dia:attribute>
        <dia:attribute name="rmargin">
          <dia:real val="2.5399999618530273"/>
        </dia:attribute>
        <dia:attribute name="is_portrait">
          <dia:boolean val="true"/>
        </dia:attribute>
        <dia:attribute name="scaling">
          <dia:real val="1"/>
        </dia:attribute>
        <dia:attribute name="fitto">
          <dia:boolean val="false"/>
        </dia:attribute>
      </dia:composite>
    </dia:attribute>
    <dia:attribute name="grid">
      <dia:composite type="grid">
        <dia:attribute name="width_x">
          <dia:real val="1"/>
        </dia:attribute>
        <dia:attribute name="width_y">
          <dia:real val="1"/>
        </dia:attribute>
        <dia:attribute name="visible_x">
          <dia:int val="1"/>
        </dia:attribute>
        <dia:attribute name="visible_y">
          <dia:int val="1"/>
        </dia:attribute>
        <dia:composite type="color"/>
      </dia:composite>
    </dia:attribute>
    <dia:attribute name="color">
      <dia:color val="#d8e5e5"/>
    </dia:attribute>
    <dia:attribute name="guides">
      <dia:composite type="guides">
        <dia:attribute name="hguides"/>
        <dia:attribute name="vguides"/>
      </dia:composite>
    </dia:attribute>
  </dia:diagramdata>
  <dia:layer name="Background" visible="true">
    <dia:group>
      <dia:group>
        <dia:object type="Standard - Box" version="0" id="O0">
          <dia:attribute name="obj_pos">
            <dia:point val="1,1"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="0.95,0.95;5.05,3.05"/>
          </dia:attribute>
          <dia:attribute name="elem_corner">
            <dia:point val="1,1"/>
          </dia:attribute>
          <dia:attribute name="elem_width">
            <dia:real val="4"/>
          </dia:attribute>
          <dia:attribute name="elem_height">
            <dia:real val="2"/>
          </dia:attribute>
          <dia:attribute name="show_background">
            <dia:boolean val="true"/>
          </dia:attribute>
        </dia:object>
        <dia:object type="Standard - Text" version="1" id="O1">
          <dia:attribute name="obj_pos">
            <dia:point val="1.98659,2.0725"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="1.98659,1.53;3.99635,2.47"/>
          </dia:attribute>
          <dia:attribute name="text">
            <dia:composite type="text">
              <dia:attribute name="string">
                <dia:string>#Length#</dia:string>
              </dia:attribute>
              <dia:attribute name="font">
                <dia:font family="sans" style="0" name="Helvetica"/>
              </dia:attribute>
              <dia:attribute name="height">
                <dia:real val="0.80000000000000004"/>
              </dia:attribute>
              <dia:attribute name="pos">
                <dia:point val="1.98659,2.0725"/>
              </dia:attribute>
              <dia:attribute name="color">
                <dia:color val="#000000"/>
              </dia:attribute>
              <dia:attribute name="alignment">
                <dia:enum val="0"/>
              </dia:attribute>
            </dia:composite>
          </dia:attribute>
          <dia:attribute name="valign">
            <dia:enum val="3"/>
          </dia:attribute>
        </dia:object>
      </dia:group>
      <dia:group>
        <dia:object type="Standard - Box" version="0" id="O2">
          <dia:attribute name="obj_pos">
            <dia:point val="5,1"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="4.95,0.95;7.05,3.05"/>
          </dia:attribute>
          <dia:attribute name="elem_corner">
            <dia:point val="5,1"/>
          </dia:attribute>
          <dia:attribute name="elem_width">
            <dia:real val="2"/>
          </dia:attribute>
          <dia:attribute name="elem_height">
            <dia:real val="2"/>
          </dia:attribute>
          <dia:attribute name="show_background">
            <dia:boolean val="true"/>
          </dia:attribute>
        </dia:object>
        <dia:object type="Standard - Text" version="1" id="O3">
          <dia:attribute name="obj_pos">
            <dia:point val="5.43272,2.06397"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="5.43272,1.52147;6.55022,2.46147"/>
          </dia:attribute>
          <dia:attribute name="text">
            <dia:composite type="text">
              <dia:attribute name="string">
                <dia:string>#"@"#</dia:string>
              </dia:attribute>
              <dia:attribute name="font">
                <dia:font family="sans" style="0" name="Helvetica"/>
              </dia:attribute>
              <dia:attribute name="height">
                <dia:real val="0.80000000000000004"/>
              </dia:attribute>
              <dia:attribute name="pos">
                <dia:point val="5.43272,2.06397"/>
              </dia:attribute>
              <dia:attribute name="color">
                <dia:color val="#000000"/>
              </dia:attribute>
              <dia:attribute name="alignment">
                <dia:enum val="0"/>
              </dia:attribute>
            </dia:composite>
          </dia:attribute>
          <dia:attribute name="valign">
            <dia:enum val="3"/>
          </dia:attribute>
        </dia:object>
      </dia:group>
      <dia:group>
        <dia:object type="Standard - Box" version="0" id="O4">
          <dia:attribute name="obj_pos">
            <dia:point val="7,1"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="6.95,0.95;11.05,3.05"/>
          </dia:attribute>
          <dia:attribute name="elem_corner">
            <dia:point val="7,1"/>
          </dia:attribute>
          <dia:attribute name="elem_width">
            <dia:real val="4"/>
          </dia:attribute>
          <dia:attribute name="elem_height">
            <dia:real val="2"/>
          </dia:attribute>
          <dia:attribute name="show_background">
            <dia:boolean val="true"/>
          </dia:attribute>
        </dia:object>
        <dia:object type="Standard - Text" version="1" id="O5">
          <dia:attribute name="obj_pos">
            <dia:point val="8.12625,2.06397"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="8.12625,1.52147;9.87375,2.46147"/>
          </dia:attribute>
          <dia:attribute name="text">
            <dia:composite type="text">
              <dia:attribute name="string">
                <dia:string>#Offset#</dia:string>
              </dia:attribute>
              <dia:attribute name="font">
                <dia:font family="sans" style="0" name="Helvetica"/>
              </dia:attribute>
              <dia:attribute name="height">
                <dia:real val="0.80000000000000004"/>
              </dia:attribute>
              <dia:attribute name="pos">
                <dia:point val="8.12625,2.06397"/>
              </dia:attribute>
              <dia:attribute name="color">
                <dia:color val="#000000"/>
              </dia:attribute>
              <dia:attribute name="alignment">
                <dia:enum val="0"/>
              </dia:attribute>
            </dia:composite>
          </dia:attribute>
          <dia:attribute name="valign">
            <dia:enum val="3"/>
          </dia:attribute>
        </dia:object>
      </dia:group>
      <dia:group>
        <dia:object type="Standard - Box" version="0" id="O6">
          <dia:attribute name="obj_pos">
            <dia:point val="11,1"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="10.95,0.95;13.05,3.05"/>
          </dia:attribute>
          <dia:attribute name="elem_corner">
            <dia:point val="11,1"/>
          </dia:attribute>
          <dia:attribute name="elem_width">
            <dia:real val="2"/>
          </dia:attribute>
          <dia:attribute name="elem_height">
            <dia:real val="2"/>
          </dia:attribute>
          <dia:attribute name="show_background">
            <dia:boolean val="true"/>
          </dia:attribute>
        </dia:object>
        <dia:object type="Standard - Text" version="1" id="O7">
          <dia:attribute name="obj_pos">
            <dia:point val="11.64,2.06397"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="11.64,1.52147;12.36,2.46147"/>
          </dia:attribute>
          <dia:attribute name="text">
            <dia:composite type="text">
              <dia:attribute name="string">
                <dia:string>#","#</dia:string>
              </dia:attribute>
              <dia:attribute name="font">
                <dia:font family="sans" style="0" name="Helvetica"/>
              </dia:attribute>
              <dia:attribute name="height">
                <dia:real val="0.80000000000000004"/>
              </dia:attribute>
              <dia:attribute name="pos">
                <dia:point val="11.64,2.06397"/>
              </dia:attribute>
              <dia:attribute name="color">
                <dia:color val="#000000"/>
              </dia:attribute>
              <dia:attribute name="alignment">
                <dia:enum val="0"/>
              </dia:attribute>
            </dia:composite>
          </dia:attribute>
          <dia:attribute name="valign">
            <dia:enum val="3"/>
          </dia:attribute>
        </dia:object>
      </dia:group>
    </dia:group>
  </dia:layer>
</dia:diagram>

Deleted art/delta4.dia.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234










































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
<?xml version="1.0" encoding="UTF-8"?>
<dia:diagram xmlns:dia="http://www.lysator.liu.se/~alla/dia/">
  <dia:diagramdata>
    <dia:attribute name="background">
      <dia:color val="#ffffff"/>
    </dia:attribute>
    <dia:attribute name="pagebreak">
      <dia:color val="#000099"/>
    </dia:attribute>
    <dia:attribute name="paper">
      <dia:composite type="paper">
        <dia:attribute name="name">
          <dia:string>#Letter#</dia:string>
        </dia:attribute>
        <dia:attribute name="tmargin">
          <dia:real val="2.5399999618530273"/>
        </dia:attribute>
        <dia:attribute name="bmargin">
          <dia:real val="2.5399999618530273"/>
        </dia:attribute>
        <dia:attribute name="lmargin">
          <dia:real val="2.5399999618530273"/>
        </dia:attribute>
        <dia:attribute name="rmargin">
          <dia:real val="2.5399999618530273"/>
        </dia:attribute>
        <dia:attribute name="is_portrait">
          <dia:boolean val="true"/>
        </dia:attribute>
        <dia:attribute name="scaling">
          <dia:real val="1"/>
        </dia:attribute>
        <dia:attribute name="fitto">
          <dia:boolean val="false"/>
        </dia:attribute>
      </dia:composite>
    </dia:attribute>
    <dia:attribute name="grid">
      <dia:composite type="grid">
        <dia:attribute name="width_x">
          <dia:real val="1"/>
        </dia:attribute>
        <dia:attribute name="width_y">
          <dia:real val="1"/>
        </dia:attribute>
        <dia:attribute name="visible_x">
          <dia:int val="1"/>
        </dia:attribute>
        <dia:attribute name="visible_y">
          <dia:int val="1"/>
        </dia:attribute>
        <dia:composite type="color"/>
      </dia:composite>
    </dia:attribute>
    <dia:attribute name="color">
      <dia:color val="#d8e5e5"/>
    </dia:attribute>
    <dia:attribute name="guides">
      <dia:composite type="guides">
        <dia:attribute name="hguides"/>
        <dia:attribute name="vguides"/>
      </dia:composite>
    </dia:attribute>
  </dia:diagramdata>
  <dia:layer name="Background" visible="true">
    <dia:group>
      <dia:group>
        <dia:object type="Standard - Box" version="0" id="O0">
          <dia:attribute name="obj_pos">
            <dia:point val="1,1"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="0.95,0.95;5.05,3.05"/>
          </dia:attribute>
          <dia:attribute name="elem_corner">
            <dia:point val="1,1"/>
          </dia:attribute>
          <dia:attribute name="elem_width">
            <dia:real val="4"/>
          </dia:attribute>
          <dia:attribute name="elem_height">
            <dia:real val="2"/>
          </dia:attribute>
          <dia:attribute name="show_background">
            <dia:boolean val="true"/>
          </dia:attribute>
        </dia:object>
        <dia:object type="Standard - Text" version="1" id="O1">
          <dia:attribute name="obj_pos">
            <dia:point val="1.98659,2.0725"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="1.98659,1.53;3.99636,2.47"/>
          </dia:attribute>
          <dia:attribute name="text">
            <dia:composite type="text">
              <dia:attribute name="string">
                <dia:string>#Length#</dia:string>
              </dia:attribute>
              <dia:attribute name="font">
                <dia:font family="sans" style="0" name="Helvetica"/>
              </dia:attribute>
              <dia:attribute name="height">
                <dia:real val="0.80000000000000004"/>
              </dia:attribute>
              <dia:attribute name="pos">
                <dia:point val="1.98659,2.0725"/>
              </dia:attribute>
              <dia:attribute name="color">
                <dia:color val="#000000"/>
              </dia:attribute>
              <dia:attribute name="alignment">
                <dia:enum val="0"/>
              </dia:attribute>
            </dia:composite>
          </dia:attribute>
          <dia:attribute name="valign">
            <dia:enum val="3"/>
          </dia:attribute>
        </dia:object>
      </dia:group>
      <dia:group>
        <dia:object type="Standard - Box" version="0" id="O2">
          <dia:attribute name="obj_pos">
            <dia:point val="7,1"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="6.95,0.95;11.05,3.05"/>
          </dia:attribute>
          <dia:attribute name="elem_corner">
            <dia:point val="7,1"/>
          </dia:attribute>
          <dia:attribute name="elem_width">
            <dia:real val="4"/>
          </dia:attribute>
          <dia:attribute name="elem_height">
            <dia:real val="2"/>
          </dia:attribute>
          <dia:attribute name="show_background">
            <dia:boolean val="true"/>
          </dia:attribute>
        </dia:object>
        <dia:object type="Standard - Text" version="1" id="O3">
          <dia:attribute name="obj_pos">
            <dia:point val="8.18375,2.06397"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="8.18375,1.52147;9.81625,2.46147"/>
          </dia:attribute>
          <dia:attribute name="text">
            <dia:composite type="text">
              <dia:attribute name="string">
                <dia:string>#Bytes#</dia:string>
              </dia:attribute>
              <dia:attribute name="font">
                <dia:font family="sans" style="0" name="Helvetica"/>
              </dia:attribute>
              <dia:attribute name="height">
                <dia:real val="0.80000000000000004"/>
              </dia:attribute>
              <dia:attribute name="pos">
                <dia:point val="8.18375,2.06397"/>
              </dia:attribute>
              <dia:attribute name="color">
                <dia:color val="#000000"/>
              </dia:attribute>
              <dia:attribute name="alignment">
                <dia:enum val="0"/>
              </dia:attribute>
            </dia:composite>
          </dia:attribute>
          <dia:attribute name="valign">
            <dia:enum val="3"/>
          </dia:attribute>
        </dia:object>
      </dia:group>
      <dia:group>
        <dia:object type="Standard - Box" version="0" id="O4">
          <dia:attribute name="obj_pos">
            <dia:point val="5,1"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="4.95,0.95;7.05,3.05"/>
          </dia:attribute>
          <dia:attribute name="elem_corner">
            <dia:point val="5,1"/>
          </dia:attribute>
          <dia:attribute name="elem_width">
            <dia:real val="2"/>
          </dia:attribute>
          <dia:attribute name="elem_height">
            <dia:real val="2"/>
          </dia:attribute>
          <dia:attribute name="show_background">
            <dia:boolean val="true"/>
          </dia:attribute>
        </dia:object>
        <dia:object type="Standard - Text" version="1" id="O5">
          <dia:attribute name="obj_pos">
            <dia:point val="5.63375,2.06397"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="5.63375,1.52147;6.36625,2.46147"/>
          </dia:attribute>
          <dia:attribute name="text">
            <dia:composite type="text">
              <dia:attribute name="string">
                <dia:string>#":"#</dia:string>
              </dia:attribute>
              <dia:attribute name="font">
                <dia:font family="sans" style="0" name="Helvetica"/>
              </dia:attribute>
              <dia:attribute name="height">
                <dia:real val="0.80000000000000004"/>
              </dia:attribute>
              <dia:attribute name="pos">
                <dia:point val="5.63375,2.06397"/>
              </dia:attribute>
              <dia:attribute name="color">
                <dia:color val="#000000"/>
              </dia:attribute>
              <dia:attribute name="alignment">
                <dia:enum val="0"/>
              </dia:attribute>
            </dia:composite>
          </dia:attribute>
          <dia:attribute name="valign">
            <dia:enum val="3"/>
          </dia:attribute>
        </dia:object>
      </dia:group>
    </dia:group>
  </dia:layer>
</dia:diagram>

Deleted art/delta5.dia.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179



















































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
<?xml version="1.0" encoding="UTF-8"?>
<dia:diagram xmlns:dia="http://www.lysator.liu.se/~alla/dia/">
  <dia:diagramdata>
    <dia:attribute name="background">
      <dia:color val="#ffffff"/>
    </dia:attribute>
    <dia:attribute name="pagebreak">
      <dia:color val="#000099"/>
    </dia:attribute>
    <dia:attribute name="paper">
      <dia:composite type="paper">
        <dia:attribute name="name">
          <dia:string>#Letter#</dia:string>
        </dia:attribute>
        <dia:attribute name="tmargin">
          <dia:real val="2.5399999618530273"/>
        </dia:attribute>
        <dia:attribute name="bmargin">
          <dia:real val="2.5399999618530273"/>
        </dia:attribute>
        <dia:attribute name="lmargin">
          <dia:real val="2.5399999618530273"/>
        </dia:attribute>
        <dia:attribute name="rmargin">
          <dia:real val="2.5399999618530273"/>
        </dia:attribute>
        <dia:attribute name="is_portrait">
          <dia:boolean val="true"/>
        </dia:attribute>
        <dia:attribute name="scaling">
          <dia:real val="1"/>
        </dia:attribute>
        <dia:attribute name="fitto">
          <dia:boolean val="false"/>
        </dia:attribute>
      </dia:composite>
    </dia:attribute>
    <dia:attribute name="grid">
      <dia:composite type="grid">
        <dia:attribute name="width_x">
          <dia:real val="1"/>
        </dia:attribute>
        <dia:attribute name="width_y">
          <dia:real val="1"/>
        </dia:attribute>
        <dia:attribute name="visible_x">
          <dia:int val="1"/>
        </dia:attribute>
        <dia:attribute name="visible_y">
          <dia:int val="1"/>
        </dia:attribute>
        <dia:composite type="color"/>
      </dia:composite>
    </dia:attribute>
    <dia:attribute name="color">
      <dia:color val="#d8e5e5"/>
    </dia:attribute>
    <dia:attribute name="guides">
      <dia:composite type="guides">
        <dia:attribute name="hguides"/>
        <dia:attribute name="vguides"/>
      </dia:composite>
    </dia:attribute>
  </dia:diagramdata>
  <dia:layer name="Background" visible="true">
    <dia:group>
      <dia:group>
        <dia:object type="Standard - Box" version="0" id="O0">
          <dia:attribute name="obj_pos">
            <dia:point val="5,1"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="4.95,0.95;7.05,3.05"/>
          </dia:attribute>
          <dia:attribute name="elem_corner">
            <dia:point val="5,1"/>
          </dia:attribute>
          <dia:attribute name="elem_width">
            <dia:real val="2"/>
          </dia:attribute>
          <dia:attribute name="elem_height">
            <dia:real val="2"/>
          </dia:attribute>
          <dia:attribute name="show_background">
            <dia:boolean val="true"/>
          </dia:attribute>
        </dia:object>
        <dia:object type="Standard - Text" version="1" id="O1">
          <dia:attribute name="obj_pos">
            <dia:point val="5.62522,2.06397"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="5.62522,1.52147;6.35772,2.46147"/>
          </dia:attribute>
          <dia:attribute name="text">
            <dia:composite type="text">
              <dia:attribute name="string">
                <dia:string>#";"#</dia:string>
              </dia:attribute>
              <dia:attribute name="font">
                <dia:font family="sans" style="0" name="Helvetica"/>
              </dia:attribute>
              <dia:attribute name="height">
                <dia:real val="0.80000000000000004"/>
              </dia:attribute>
              <dia:attribute name="pos">
                <dia:point val="5.62522,2.06397"/>
              </dia:attribute>
              <dia:attribute name="color">
                <dia:color val="#000000"/>
              </dia:attribute>
              <dia:attribute name="alignment">
                <dia:enum val="0"/>
              </dia:attribute>
            </dia:composite>
          </dia:attribute>
          <dia:attribute name="valign">
            <dia:enum val="3"/>
          </dia:attribute>
        </dia:object>
      </dia:group>
      <dia:group>
        <dia:object type="Standard - Box" version="0" id="O2">
          <dia:attribute name="obj_pos">
            <dia:point val="1,1"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="0.95,0.95;5.05,3.05"/>
          </dia:attribute>
          <dia:attribute name="elem_corner">
            <dia:point val="1,1"/>
          </dia:attribute>
          <dia:attribute name="elem_width">
            <dia:real val="4"/>
          </dia:attribute>
          <dia:attribute name="elem_height">
            <dia:real val="2"/>
          </dia:attribute>
          <dia:attribute name="show_background">
            <dia:boolean val="true"/>
          </dia:attribute>
        </dia:object>
        <dia:object type="Standard - Text" version="1" id="O3">
          <dia:attribute name="obj_pos">
            <dia:point val="1.47522,2.06397"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="1.47522,1.52147;4.50772,2.46147"/>
          </dia:attribute>
          <dia:attribute name="text">
            <dia:composite type="text">
              <dia:attribute name="string">
                <dia:string>#Checksum#</dia:string>
              </dia:attribute>
              <dia:attribute name="font">
                <dia:font family="sans" style="0" name="Helvetica"/>
              </dia:attribute>
              <dia:attribute name="height">
                <dia:real val="0.80000000000000004"/>
              </dia:attribute>
              <dia:attribute name="pos">
                <dia:point val="1.47522,2.06397"/>
              </dia:attribute>
              <dia:attribute name="color">
                <dia:color val="#000000"/>
              </dia:attribute>
              <dia:attribute name="alignment">
                <dia:enum val="0"/>
              </dia:attribute>
            </dia:composite>
          </dia:attribute>
          <dia:attribute name="valign">
            <dia:enum val="3"/>
          </dia:attribute>
        </dia:object>
      </dia:group>
    </dia:group>
  </dia:layer>
</dia:diagram>

Deleted art/delta6.dia.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179



















































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
<?xml version="1.0" encoding="UTF-8"?>
<dia:diagram xmlns:dia="http://www.lysator.liu.se/~alla/dia/">
  <dia:diagramdata>
    <dia:attribute name="background">
      <dia:color val="#ffffff"/>
    </dia:attribute>
    <dia:attribute name="pagebreak">
      <dia:color val="#000099"/>
    </dia:attribute>
    <dia:attribute name="paper">
      <dia:composite type="paper">
        <dia:attribute name="name">
          <dia:string>#Letter#</dia:string>
        </dia:attribute>
        <dia:attribute name="tmargin">
          <dia:real val="2.5399999618530273"/>
        </dia:attribute>
        <dia:attribute name="bmargin">
          <dia:real val="2.5399999618530273"/>
        </dia:attribute>
        <dia:attribute name="lmargin">
          <dia:real val="2.5399999618530273"/>
        </dia:attribute>
        <dia:attribute name="rmargin">
          <dia:real val="2.5399999618530273"/>
        </dia:attribute>
        <dia:attribute name="is_portrait">
          <dia:boolean val="true"/>
        </dia:attribute>
        <dia:attribute name="scaling">
          <dia:real val="1"/>
        </dia:attribute>
        <dia:attribute name="fitto">
          <dia:boolean val="false"/>
        </dia:attribute>
      </dia:composite>
    </dia:attribute>
    <dia:attribute name="grid">
      <dia:composite type="grid">
        <dia:attribute name="width_x">
          <dia:real val="1"/>
        </dia:attribute>
        <dia:attribute name="width_y">
          <dia:real val="1"/>
        </dia:attribute>
        <dia:attribute name="visible_x">
          <dia:int val="1"/>
        </dia:attribute>
        <dia:attribute name="visible_y">
          <dia:int val="1"/>
        </dia:attribute>
        <dia:composite type="color"/>
      </dia:composite>
    </dia:attribute>
    <dia:attribute name="color">
      <dia:color val="#d8e5e5"/>
    </dia:attribute>
    <dia:attribute name="guides">
      <dia:composite type="guides">
        <dia:attribute name="hguides"/>
        <dia:attribute name="vguides"/>
      </dia:composite>
    </dia:attribute>
  </dia:diagramdata>
  <dia:layer name="Background" visible="true">
    <dia:group>
      <dia:group>
        <dia:object type="Standard - Box" version="0" id="O0">
          <dia:attribute name="obj_pos">
            <dia:point val="1,1"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="0.95,0.95;5.05,3.05"/>
          </dia:attribute>
          <dia:attribute name="elem_corner">
            <dia:point val="1,1"/>
          </dia:attribute>
          <dia:attribute name="elem_width">
            <dia:real val="4"/>
          </dia:attribute>
          <dia:attribute name="elem_height">
            <dia:real val="2"/>
          </dia:attribute>
          <dia:attribute name="show_background">
            <dia:boolean val="true"/>
          </dia:attribute>
        </dia:object>
        <dia:object type="Standard - Text" version="1" id="O1">
          <dia:attribute name="obj_pos">
            <dia:point val="2.39397,2.06397"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="2.39397,1.50441;3.60603,2.49559"/>
          </dia:attribute>
          <dia:attribute name="text">
            <dia:composite type="text">
              <dia:attribute name="string">
                <dia:string>#Size#</dia:string>
              </dia:attribute>
              <dia:attribute name="font">
                <dia:font family="sans" style="0" name="Helvetica"/>
              </dia:attribute>
              <dia:attribute name="height">
                <dia:real val="0.80000000000000004"/>
              </dia:attribute>
              <dia:attribute name="pos">
                <dia:point val="2.39397,2.06397"/>
              </dia:attribute>
              <dia:attribute name="color">
                <dia:color val="#000000"/>
              </dia:attribute>
              <dia:attribute name="alignment">
                <dia:enum val="0"/>
              </dia:attribute>
            </dia:composite>
          </dia:attribute>
          <dia:attribute name="valign">
            <dia:enum val="3"/>
          </dia:attribute>
        </dia:object>
      </dia:group>
      <dia:group>
        <dia:object type="Standard - Box" version="0" id="O2">
          <dia:attribute name="obj_pos">
            <dia:point val="5,1"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="4.95,0.95;7.05,3.05"/>
          </dia:attribute>
          <dia:attribute name="elem_corner">
            <dia:point val="5,1"/>
          </dia:attribute>
          <dia:attribute name="elem_width">
            <dia:real val="2"/>
          </dia:attribute>
          <dia:attribute name="elem_height">
            <dia:real val="2"/>
          </dia:attribute>
          <dia:attribute name="show_background">
            <dia:boolean val="true"/>
          </dia:attribute>
        </dia:object>
        <dia:object type="Standard - Text" version="1" id="O3">
          <dia:attribute name="obj_pos">
            <dia:point val="5.44022,2.06397"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="5.44022,1.50441;6.55978,2.49559"/>
          </dia:attribute>
          <dia:attribute name="text">
            <dia:composite type="text">
              <dia:attribute name="string">
                <dia:string>#"\n"#</dia:string>
              </dia:attribute>
              <dia:attribute name="font">
                <dia:font family="sans" style="0" name="Helvetica"/>
              </dia:attribute>
              <dia:attribute name="height">
                <dia:real val="0.80000000000000004"/>
              </dia:attribute>
              <dia:attribute name="pos">
                <dia:point val="5.44022,2.06397"/>
              </dia:attribute>
              <dia:attribute name="color">
                <dia:color val="#000000"/>
              </dia:attribute>
              <dia:attribute name="alignment">
                <dia:enum val="0"/>
              </dia:attribute>
            </dia:composite>
          </dia:attribute>
          <dia:attribute name="valign">
            <dia:enum val="3"/>
          </dia:attribute>
        </dia:object>
      </dia:group>
    </dia:group>
  </dia:layer>
</dia:diagram>

Deleted art/encode1.tex.

1
2


-
-
\LARGE A = (\sum_{i=0}^{NHASH-1} z_i) \bmod 2^{16}

Deleted art/encode10.dia.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
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

















































































































































































































































































































































































































































































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
<?xml version="1.0" encoding="UTF-8"?>
<dia:diagram xmlns:dia="http://www.lysator.liu.se/~alla/dia/">
  <dia:diagramdata>
    <dia:attribute name="background">
      <dia:color val="#ffffff"/>
    </dia:attribute>
    <dia:attribute name="pagebreak">
      <dia:color val="#000099"/>
    </dia:attribute>
    <dia:attribute name="paper">
      <dia:composite type="paper">
        <dia:attribute name="name">
          <dia:string>#Letter#</dia:string>
        </dia:attribute>
        <dia:attribute name="tmargin">
          <dia:real val="2.5399999618530273"/>
        </dia:attribute>
        <dia:attribute name="bmargin">
          <dia:real val="2.5399999618530273"/>
        </dia:attribute>
        <dia:attribute name="lmargin">
          <dia:real val="2.5399999618530273"/>
        </dia:attribute>
        <dia:attribute name="rmargin">
          <dia:real val="2.5399999618530273"/>
        </dia:attribute>
        <dia:attribute name="is_portrait">
          <dia:boolean val="true"/>
        </dia:attribute>
        <dia:attribute name="scaling">
          <dia:real val="1"/>
        </dia:attribute>
        <dia:attribute name="fitto">
          <dia:boolean val="false"/>
        </dia:attribute>
      </dia:composite>
    </dia:attribute>
    <dia:attribute name="grid">
      <dia:composite type="grid">
        <dia:attribute name="width_x">
          <dia:real val="1"/>
        </dia:attribute>
        <dia:attribute name="width_y">
          <dia:real val="1"/>
        </dia:attribute>
        <dia:attribute name="visible_x">
          <dia:int val="1"/>
        </dia:attribute>
        <dia:attribute name="visible_y">
          <dia:int val="1"/>
        </dia:attribute>
        <dia:composite type="color"/>
      </dia:composite>
    </dia:attribute>
    <dia:attribute name="color">
      <dia:color val="#d8e5e5"/>
    </dia:attribute>
    <dia:attribute name="guides">
      <dia:composite type="guides">
        <dia:attribute name="hguides"/>
        <dia:attribute name="vguides"/>
      </dia:composite>
    </dia:attribute>
  </dia:diagramdata>
  <dia:layer name="Background" visible="true">
    <dia:group>
      <dia:group>
        <dia:object type="Standard - Box" version="0" id="O0">
          <dia:attribute name="obj_pos">
            <dia:point val="6,2"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="5.975,1.975;12.025,22.025"/>
          </dia:attribute>
          <dia:attribute name="elem_corner">
            <dia:point val="6,2"/>
          </dia:attribute>
          <dia:attribute name="elem_width">
            <dia:real val="6"/>
          </dia:attribute>
          <dia:attribute name="elem_height">
            <dia:real val="20"/>
          </dia:attribute>
          <dia:attribute name="border_width">
            <dia:real val="0.050000000000000003"/>
          </dia:attribute>
          <dia:attribute name="show_background">
            <dia:boolean val="true"/>
          </dia:attribute>
          <dia:attribute name="corner_radius">
            <dia:real val="1.1754943508222875e-38"/>
          </dia:attribute>
        </dia:object>
        <dia:group>
          <dia:object type="Standard - Box" version="0" id="O1">
            <dia:attribute name="obj_pos">
              <dia:point val="6,8"/>
            </dia:attribute>
            <dia:attribute name="obj_bb">
              <dia:rectangle val="5.975,7.975;12.025,9.025"/>
            </dia:attribute>
            <dia:attribute name="elem_corner">
              <dia:point val="6,8"/>
            </dia:attribute>
            <dia:attribute name="elem_width">
              <dia:real val="6"/>
            </dia:attribute>
            <dia:attribute name="elem_height">
              <dia:real val="1"/>
            </dia:attribute>
            <dia:attribute name="border_width">
              <dia:real val="0.050000000000000003"/>
            </dia:attribute>
            <dia:attribute name="inner_color">
              <dia:color val="#ffff00"/>
            </dia:attribute>
            <dia:attribute name="show_background">
              <dia:boolean val="true"/>
            </dia:attribute>
            <dia:attribute name="corner_radius">
              <dia:real val="1.1754943508222875e-38"/>
            </dia:attribute>
          </dia:object>
          <dia:object type="Standard - Text" version="1" id="O2">
            <dia:attribute name="obj_pos">
              <dia:point val="5.95,8.4925"/>
            </dia:attribute>
            <dia:attribute name="obj_bb">
              <dia:rectangle val="5.95,7.95;10.145,8.89"/>
            </dia:attribute>
            <dia:attribute name="text">
              <dia:composite type="text">
                <dia:attribute name="string">
                  <dia:string>#Gap =&gt; Insert#</dia:string>
                </dia:attribute>
                <dia:attribute name="font">
                  <dia:font family="sans" style="0" name="Helvetica"/>
                </dia:attribute>
                <dia:attribute name="height">
                  <dia:real val="0.80000000000000004"/>
                </dia:attribute>
                <dia:attribute name="pos">
                  <dia:point val="5.95,8.4925"/>
                </dia:attribute>
                <dia:attribute name="color">
                  <dia:color val="#000000"/>
                </dia:attribute>
                <dia:attribute name="alignment">
                  <dia:enum val="0"/>
                </dia:attribute>
              </dia:composite>
            </dia:attribute>
            <dia:attribute name="valign">
              <dia:enum val="3"/>
            </dia:attribute>
          </dia:object>
        </dia:group>
        <dia:group>
          <dia:object type="Standard - Box" version="0" id="O3">
            <dia:attribute name="obj_pos">
              <dia:point val="6,2"/>
            </dia:attribute>
            <dia:attribute name="obj_bb">
              <dia:rectangle val="5.975,1.975;12.025,8.025"/>
            </dia:attribute>
            <dia:attribute name="elem_corner">
              <dia:point val="6,2"/>
            </dia:attribute>
            <dia:attribute name="elem_width">
              <dia:real val="6"/>
            </dia:attribute>
            <dia:attribute name="elem_height">
              <dia:real val="6"/>
            </dia:attribute>
            <dia:attribute name="border_width">
              <dia:real val="0.050000000000000003"/>
            </dia:attribute>
            <dia:attribute name="inner_color">
              <dia:color val="#90ee90"/>
            </dia:attribute>
            <dia:attribute name="show_background">
              <dia:boolean val="true"/>
            </dia:attribute>
            <dia:attribute name="corner_radius">
              <dia:real val="1.1754943508222875e-38"/>
            </dia:attribute>
          </dia:object>
          <dia:object type="Standard - Text" version="1" id="O4">
            <dia:attribute name="obj_pos">
              <dia:point val="5.975,2.53456"/>
            </dia:attribute>
            <dia:attribute name="obj_bb">
              <dia:rectangle val="5.975,1.99206;8.91195,2.93206"/>
            </dia:attribute>
            <dia:attribute name="text">
              <dia:composite type="text">
                <dia:attribute name="string">
                  <dia:string>#Processed#</dia:string>
                </dia:attribute>
                <dia:attribute name="font">
                  <dia:font family="sans" style="0" name="Helvetica"/>
                </dia:attribute>
                <dia:attribute name="height">
                  <dia:real val="0.80000000000000004"/>
                </dia:attribute>
                <dia:attribute name="pos">
                  <dia:point val="5.975,2.53456"/>
                </dia:attribute>
                <dia:attribute name="color">
                  <dia:color val="#000000"/>
                </dia:attribute>
                <dia:attribute name="alignment">
                  <dia:enum val="0"/>
                </dia:attribute>
              </dia:composite>
            </dia:attribute>
            <dia:attribute name="valign">
              <dia:enum val="3"/>
            </dia:attribute>
          </dia:object>
        </dia:group>
        <dia:group>
          <dia:object type="Standard - Box" version="0" id="O5">
            <dia:attribute name="obj_pos">
              <dia:point val="6,9"/>
            </dia:attribute>
            <dia:attribute name="obj_bb">
              <dia:rectangle val="5.975,8.975;12.025,18.025"/>
            </dia:attribute>
            <dia:attribute name="elem_corner">
              <dia:point val="6,9"/>
            </dia:attribute>
            <dia:attribute name="elem_width">
              <dia:real val="6"/>
            </dia:attribute>
            <dia:attribute name="elem_height">
              <dia:real val="9"/>
            </dia:attribute>
            <dia:attribute name="border_width">
              <dia:real val="0.050000000000000003"/>
            </dia:attribute>
            <dia:attribute name="inner_color">
              <dia:color val="#ffa500"/>
            </dia:attribute>
            <dia:attribute name="show_background">
              <dia:boolean val="true"/>
            </dia:attribute>
            <dia:attribute name="corner_radius">
              <dia:real val="1.1754943508222875e-38"/>
            </dia:attribute>
          </dia:object>
          <dia:object type="Standard - Text" version="1" id="O6">
            <dia:attribute name="obj_pos">
              <dia:point val="5.975,9.53456"/>
            </dia:attribute>
            <dia:attribute name="obj_bb">
              <dia:rectangle val="5.975,8.99206;11.4275,9.93206"/>
            </dia:attribute>
            <dia:attribute name="text">
              <dia:composite type="text">
                <dia:attribute name="string">
                  <dia:string>#Common =&gt; Copy#</dia:string>
                </dia:attribute>
                <dia:attribute name="font">
                  <dia:font family="sans" style="0" name="Helvetica"/>
                </dia:attribute>
                <dia:attribute name="height">
                  <dia:real val="0.80000000000000004"/>
                </dia:attribute>
                <dia:attribute name="pos">
                  <dia:point val="5.975,9.53456"/>
                </dia:attribute>
                <dia:attribute name="color">
                  <dia:color val="#000000"/>
                </dia:attribute>
                <dia:attribute name="alignment">
                  <dia:enum val="0"/>
                </dia:attribute>
              </dia:composite>
            </dia:attribute>
            <dia:attribute name="valign">
              <dia:enum val="3"/>
            </dia:attribute>
          </dia:object>
        </dia:group>
        <dia:group>
          <dia:object type="Standard - Box" version="0" id="O7">
            <dia:attribute name="obj_pos">
              <dia:point val="5,11"/>
            </dia:attribute>
            <dia:attribute name="obj_bb">
              <dia:rectangle val="4.95,10.95;13.05,15.05"/>
            </dia:attribute>
            <dia:attribute name="elem_corner">
              <dia:point val="5,11"/>
            </dia:attribute>
            <dia:attribute name="elem_width">
              <dia:real val="8"/>
            </dia:attribute>
            <dia:attribute name="elem_height">
              <dia:real val="4"/>
            </dia:attribute>
            <dia:attribute name="inner_color">
              <dia:color val="#bfbfbf"/>
            </dia:attribute>
            <dia:attribute name="show_background">
              <dia:boolean val="true"/>
            </dia:attribute>
            <dia:attribute name="corner_radius">
              <dia:real val="1.1754943508222875e-38"/>
            </dia:attribute>
          </dia:object>
          <dia:object type="Standard - Text" version="1" id="O8">
            <dia:attribute name="obj_pos">
              <dia:point val="4.95,11.9675"/>
            </dia:attribute>
            <dia:attribute name="obj_bb">
              <dia:rectangle val="4.95,10.95;9.26848,12.71"/>
            </dia:attribute>
            <dia:attribute name="text">
              <dia:composite type="text">
                <dia:attribute name="string">
                  <dia:string>#Window#</dia:string>
                </dia:attribute>
                <dia:attribute name="font">
                  <dia:font family="sans" style="0" name="Helvetica"/>
                </dia:attribute>
                <dia:attribute name="height">
                  <dia:real val="1.5"/>
                </dia:attribute>
                <dia:attribute name="pos">
                  <dia:point val="4.95,11.9675"/>
                </dia:attribute>
                <dia:attribute name="color">
                  <dia:color val="#000000"/>
                </dia:attribute>
                <dia:attribute name="alignment">
                  <dia:enum val="0"/>
                </dia:attribute>
              </dia:composite>
            </dia:attribute>
            <dia:attribute name="valign">
              <dia:enum val="3"/>
            </dia:attribute>
          </dia:object>
        </dia:group>
      </dia:group>
      <dia:group>
        <dia:object type="Standard - Box" version="0" id="O9">
          <dia:attribute name="obj_pos">
            <dia:point val="18,2"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="17.975,1.975;24.025,24.025"/>
          </dia:attribute>
          <dia:attribute name="elem_corner">
            <dia:point val="18,2"/>
          </dia:attribute>
          <dia:attribute name="elem_width">
            <dia:real val="6"/>
          </dia:attribute>
          <dia:attribute name="elem_height">
            <dia:real val="22"/>
          </dia:attribute>
          <dia:attribute name="border_width">
            <dia:real val="0.050000000000000003"/>
          </dia:attribute>
          <dia:attribute name="show_background">
            <dia:boolean val="true"/>
          </dia:attribute>
          <dia:attribute name="corner_radius">
            <dia:real val="1.1754943508222875e-38"/>
          </dia:attribute>
        </dia:object>
        <dia:object type="Standard - Box" version="0" id="O10">
          <dia:attribute name="obj_pos">
            <dia:point val="18,5"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="17.975,4.975;24.025,14.025"/>
          </dia:attribute>
          <dia:attribute name="elem_corner">
            <dia:point val="18,5"/>
          </dia:attribute>
          <dia:attribute name="elem_width">
            <dia:real val="6"/>
          </dia:attribute>
          <dia:attribute name="elem_height">
            <dia:real val="9"/>
          </dia:attribute>
          <dia:attribute name="border_width">
            <dia:real val="0.050000000000000003"/>
          </dia:attribute>
          <dia:attribute name="inner_color">
            <dia:color val="#ffa500"/>
          </dia:attribute>
          <dia:attribute name="show_background">
            <dia:boolean val="true"/>
          </dia:attribute>
          <dia:attribute name="corner_radius">
            <dia:real val="1.1754943508222875e-38"/>
          </dia:attribute>
        </dia:object>
      </dia:group>
      <dia:group>
        <dia:object type="Standard - Text" version="1" id="O11">
          <dia:attribute name="obj_pos">
            <dia:point val="14,9"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="14,8.4575;16.8122,9.3975"/>
          </dia:attribute>
          <dia:attribute name="text">
            <dia:composite type="text">
              <dia:attribute name="string">
                <dia:string>#Signature#</dia:string>
              </dia:attribute>
              <dia:attribute name="font">
                <dia:font family="sans" style="0" name="Helvetica"/>
              </dia:attribute>
              <dia:attribute name="height">
                <dia:real val="0.80000000000000004"/>
              </dia:attribute>
              <dia:attribute name="pos">
                <dia:point val="14,9"/>
              </dia:attribute>
              <dia:attribute name="color">
                <dia:color val="#000000"/>
              </dia:attribute>
              <dia:attribute name="alignment">
                <dia:enum val="0"/>
              </dia:attribute>
            </dia:composite>
          </dia:attribute>
          <dia:attribute name="valign">
            <dia:enum val="3"/>
          </dia:attribute>
        </dia:object>
        <dia:object type="Standard - ZigZagLine" version="1" id="O12">
          <dia:attribute name="obj_pos">
            <dia:point val="12,13"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="11.95,9;18.05,13.05"/>
          </dia:attribute>
          <dia:attribute name="orth_points">
            <dia:point val="12,13"/>
            <dia:point val="15,13"/>
            <dia:point val="15,9.5"/>
            <dia:point val="18,9.5"/>
          </dia:attribute>
          <dia:attribute name="orth_orient">
            <dia:enum val="0"/>
            <dia:enum val="1"/>
            <dia:enum val="0"/>
          </dia:attribute>
          <dia:attribute name="autorouting">
            <dia:boolean val="true"/>
          </dia:attribute>
          <dia:attribute name="end_arrow">
            <dia:enum val="22"/>
          </dia:attribute>
          <dia:attribute name="end_arrow_length">
            <dia:real val="0.5"/>
          </dia:attribute>
          <dia:attribute name="end_arrow_width">
            <dia:real val="0.5"/>
          </dia:attribute>
        </dia:object>
      </dia:group>
      <dia:group>
        <dia:object type="Standard - Text" version="1" id="O13">
          <dia:attribute name="obj_pos">
            <dia:point val="2,8"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="2,6.9825;4.6625,8.7425"/>
          </dia:attribute>
          <dia:attribute name="text">
            <dia:composite type="text">
              <dia:attribute name="string">
                <dia:string>#Base#</dia:string>
              </dia:attribute>
              <dia:attribute name="font">
                <dia:font family="sans" style="0" name="Helvetica"/>
              </dia:attribute>
              <dia:attribute name="height">
                <dia:real val="1.5"/>
              </dia:attribute>
              <dia:attribute name="pos">
                <dia:point val="2,8"/>
              </dia:attribute>
              <dia:attribute name="color">
                <dia:color val="#000000"/>
              </dia:attribute>
              <dia:attribute name="alignment">
                <dia:enum val="0"/>
              </dia:attribute>
            </dia:composite>
          </dia:attribute>
          <dia:attribute name="valign">
            <dia:enum val="3"/>
          </dia:attribute>
        </dia:object>
        <dia:object type="Standard - Line" version="0" id="O14">
          <dia:attribute name="obj_pos">
            <dia:point val="5,8"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="4.95,7.5;6.05,8.5"/>
          </dia:attribute>
          <dia:attribute name="conn_endpoints">
            <dia:point val="5,8"/>
            <dia:point val="6,8"/>
          </dia:attribute>
          <dia:attribute name="numcp">
            <dia:int val="1"/>
          </dia:attribute>
          <dia:attribute name="end_arrow">
            <dia:enum val="22"/>
          </dia:attribute>
          <dia:attribute name="end_arrow_length">
            <dia:real val="0.5"/>
          </dia:attribute>
          <dia:attribute name="end_arrow_width">
            <dia:real val="0.5"/>
          </dia:attribute>
        </dia:object>
      </dia:group>
      <dia:group>
        <dia:object type="Standard - Text" version="1" id="O15">
          <dia:attribute name="obj_pos">
            <dia:point val="1,11"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="1,9.9825;3.6725,11.7425"/>
          </dia:attribute>
          <dia:attribute name="text">
            <dia:composite type="text">
              <dia:attribute name="string">
                <dia:string>#Slide#</dia:string>
              </dia:attribute>
              <dia:attribute name="font">
                <dia:font family="sans" style="0" name="Helvetica"/>
              </dia:attribute>
              <dia:attribute name="height">
                <dia:real val="1.5"/>
              </dia:attribute>
              <dia:attribute name="pos">
                <dia:point val="1,11"/>
              </dia:attribute>
              <dia:attribute name="color">
                <dia:color val="#000000"/>
              </dia:attribute>
              <dia:attribute name="alignment">
                <dia:enum val="0"/>
              </dia:attribute>
            </dia:composite>
          </dia:attribute>
          <dia:attribute name="valign">
            <dia:enum val="3"/>
          </dia:attribute>
        </dia:object>
        <dia:object type="Standard - Line" version="0" id="O16">
          <dia:attribute name="obj_pos">
            <dia:point val="4,11"/>
          </dia:attribute>
          <dia:attribute name="obj_bb">
            <dia:rectangle val="3.95,10.5;5.05,11.5"/>
          </dia:attribute>
          <dia:attribute name="conn_endpoints">
            <dia:point val="4,11"/>
            <dia:point val="5,11"/>
          </dia:attribute>
          <dia:attribute name="numcp">
            <dia:int val="1"/>
          </dia:attribute>
          <dia:attribute name="end_arrow">
            <dia:enum val="22"/>
          </dia:attribute>
          <dia:attribute name="end_arrow_length">
            <dia:real val="0.5"/>
          </dia:attribute>
          <dia:attribute name="end_arrow_width">
            <dia:real val="0.5"/>
          </dia:attribute>
        </dia:object>
      </dia:group>
      <dia:object type="Standard - Text" version="1" id="O17">
        <dia:attribute name="obj_pos">
          <dia:point val="6,2"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="6,0.9825;9.38727,2.7425"/>
        </dia:attribute>
        <dia:attribute name="text">
          <dia:composite type="text">
            <dia:attribute name="string">
              <dia:string>#Target#</dia:string>
            </dia:attribute>
            <dia:attribute name="font">
              <dia:font family="sans" style="0" name="Helvetica"/>
            </dia:attribute>
            <dia:attribute name="height">
              <dia:real val="1.5"/>
            </dia:attribute>
            <dia:attribute name="pos">
              <dia:point val="6,2"/>
            </dia:attribute>
            <dia:attribute name="color">
              <dia:color val="#000000"/>
            </dia:attribute>
            <dia:attribute name="alignment">
              <dia:enum val="0"/>
            </dia:attribute>
          </dia:composite>
        </dia:attribute>
        <dia:attribute name="valign">
          <dia:enum val="3"/>
        </dia:attribute>
      </dia:object>
      <dia:object type="Standard - Text" version="1" id="O18">
        <dia:attribute name="obj_pos">
          <dia:point val="18,2"/>
        </dia:attribute>
        <dia:attribute name="obj_bb">
          <dia:rectangle val="18,0.9825;21.3125,2.7425"/>
        </dia:attribute>
        <dia:attribute name="text">
          <dia:composite type="text">
            <dia:attribute name="string">
              <dia:string>#Origin#</dia:string>
            </dia:attribute>
            <dia:attribute name="font">
              <dia:font family="sans" style="0" name="Helvetica"/>
            </dia:attribute>
            <dia:attribute name="height">
              <dia:real val="1.5"/>
            </dia:attribute>
            <dia:attribute name="pos">
              <dia:point val="18,2"/>
            </dia:attribute>
            <dia:attribute name="color">
              <dia:color val="#000000"/>
            </dia:attribute>
            <dia:attribute name="alignment">
              <dia:enum val="0"/>
            </dia:attribute>
          </dia:composite>
        </dia:attribute>
        <dia:attribute name="valign">
          <dia:enum val="3"/>
        </dia:attribute>
      </dia:object>
    </dia:group>
  </dia:layer>
</dia:diagram>

Deleted art/encode2.tex.

1

-
\LARGE B = (\sum_{i=0}^{NHASH-1} (NHASH-i)z_i) \bmod 2^{16}

Deleted art/encode3.tex.

1

-
\LARGE V = 2^{16}B + A

Deleted art/encode4.tex.

1

-
\LARGE z_0

Deleted art/encode5.tex.

1

-
\LARGE z_{new}

Deleted art/encode6.tex.

1

-
\LARGE A_{new} = (A - z_0 + z_{new}) \bmod 2^{16}

Deleted art/encode7.tex.

1

-
\LARGE B_{new} = (B - z_0 NHASH + A_{new}) \bmod 2^{16}

Deleted art/encode8.tex.

1

-
\LARGE V_{new} = 2^{16}B_{new} + A_{new}

Deleted art/encode9.tex.

1

-
\LARGE A_{new}

Changes to 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
1
2
3
4
5
6
7
8
9

10
11
12
13
14
15

16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87

88
89
90
91
92
93
94
95


+
+





-


+
+


-
+








+
+






+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+









+
+
+
+
+
+
+


















-
+







# 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-miniz=0         => {Use miniz from the source tree}
    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-legacy-mv-rm=1  => {Enable legacy behavior for mv/rm (skip checkout files)}
    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]} {
    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
73
74
75
76
77
78
79
80
81
82









83
84
85
86
87
88
89
106
107
108
109
110
111
112

113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130







-


+
+
+
+
+
+
+
+
+







    }
}

define EXTRA_CFLAGS "-Wall"
define EXTRA_LDFLAGS ""
define USE_SYSTEM_SQLITE 0
define USE_LINENOISE 0
define FOSSIL_ENABLE_MINIZ 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..."
114
115
116
117
118
119
120
121

122
123
124
125
126
127
128
129
130
131
132


133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150







































151


152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170

171
172
173
174
175
176
177
178
179
180
181



182
183
184
185
186
187
188
189
190
191
192

193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
155
156
157
158
159
160
161

162
163
164
165
166
167
168
169
170
171


172
173
174
175
176




177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226

227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246

247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286






287
288
289
290
291
292
293







-
+









-
-
+
+



-
-
-
-











+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+


















-
+











+
+
+











+













-
-
-
-
-
-







    } else {
        msg-result "no"
    }
    return $found
}

if {![opt-bool internal-sqlite]} {
  proc find_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_create_window_function can be found as well. If we can find open() but
      # not create_window_function(), then the system SQLite is too old to link against
      # 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]} {
        if {![check-function-in-lib sqlite3_create_window_function sqlite3 $extralibs]} {
          user-error "system sqlite3 too old (require >= 3.25.0)"
        }

        # 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__
  }
  find_internal_sqlite
  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_XOPEN_SOURCE=500 -D__EXTENSIONS__}
    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-legacy-mv-rm]} {
    define-append EXTRA_CFLAGS -DFOSSIL_ENABLE_LEGACY_MV_RM=1
    define FOSSIL_ENABLE_LEGACY_MV_RM
    msg-result "Legacy mv/rm 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]} {
241
242
243
244
245
246
247
248

249
250
251
252
253
254
255
316
317
318
319
320
321
322

323
324
325
326
327
328
329
330







-
+







    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}}} {
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
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304

















































305
306
307


308
309


310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326

327
328
329
330
331
332
333
334
335
336
337


338
339
340
341
342
343
344
350
351
352
353
354
355
356























357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405



406
407


408
409
410
411
412
413



414
415
416
417
418
419
420
421
422

423
424
425
426
427
428
429
430
431
432
433

434
435
436
437
438
439
440
441
442







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
-
-
+
+




-
-
-









-
+










-
+
+







        return 1
    } else {
        msg-result "no"
        return 0
    }
}

if {[opt-bool with-miniz]} {
    define FOSSIL_ENABLE_MINIZ 1
    msg-result "Using miniz for compression"
} else {
    # Check for zlib, using the given location if specified
    set zlibpath [opt-val with-zlib]
    if {$zlibpath 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"
        }
        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 {
        if {$zlibpath ni {auto ""}} {
            cc-with [list -cflags "-I$zlibpath -L$zlibpath"]
            define-append EXTRA_CFLAGS -I$zlibpath
            define-append EXTRA_LDFLAGS -L$zlibpath
            msg-result "Using zlib from $zlibpath"
        }
# 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"
      }
        if {![cc-check-includes zlib.h] || ![check-function-in-lib inflateEnd z]} {
            user-error "zlib not found please install it or specify the location with --with-zlib"
        }
      break
    }
        set ::zlib_lib -lz
    }
  }
  set ::zlib_lib -lz
}

set ssldirs [opt-val with-openssl]
if {$ssldirs ne "none"} {
    if {[opt-bool with-miniz]} {
        user-error "The --with-miniz option is incompatible with OpenSSL"
    }
    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"
        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"
                             /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 ""
355
356
357
358
359
360
361


362
363
364
365
366
367
368
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468







+
+







            }
        }
    }
    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
383
384
385
386
387
388
389






































































390
391
392
393
394
395
396
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







    }
} 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} {
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
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







+

















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







    }
    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
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
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







+
+
+
+
+
+
+
+
















+
+
-
+







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+




# 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"
  if {[cc-check-function-in-lib fuse_mount fuse]} {
  } 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
    }
}
# Finally, append -ldl to make sure it's the last in the list.
# The library order matters in case of static linking.

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_*}

Changes to autosetup/README.autosetup.











1

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.

This is autosetup v0.6.6. See http://msteveb.github.com/autosetup/
For more information, see http://msteveb.github.com/autosetup/

Changes to autosetup/autosetup.

1
2
3
4
5
6

7

8

9
10
11

12
13
14
15
16
17
18
1
2
3
4
5

6
7
8

9
10
11

12
13
14
15
16
17
18
19





-
+

+
-
+


-
+







#!/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/find-tclsh`" "$0" "$@"
dir=`dirname "$0"`; exec "`$dir/autosetup-find-tclsh`" "$0" "$@"

# Note that the version has a trailing + on unreleased versions
set autosetup(version) 0.6.6
set autosetup(version) 0.6.9

# Can be set to 1 to debug early-init problems
set autosetup(debug) 0
set autosetup(debug) [expr {"--debug" in $argv}]

##################################################################
#
# Main flow of control, option handling
#
proc main {argv} {
	global autosetup define
70
71
72
73
74
75
76
77

78


79
80


81
82
83
84
85
86
87
88
89
90
91

92
93
94
95
96













97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113


114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131


132
133

134
135
136

137
138






139

140
141

142
143
144
145
146

147
148

149
150
151
152

153
154

155
156
157
158
159





160
161
162
163
164
165
166
71
72
73
74
75
76
77

78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101

102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129


130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146



147
148


149
150
151

152
153
154
155
156
157
158
159
160

161
162

163
164
165
166
167

168
169

170
171
172
173

174
175

176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193







-
+

+
+


+
+











+




-
+
+
+
+
+
+
+
+
+
+
+
+
+















-
-
+
+















-
-
-
+
+
-
-
+


-
+


+
+
+
+
+
+
-
+

-
+




-
+

-
+



-
+

-
+





+
+
+
+
+








	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 for options
	# 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 (in the 'autosetup/' subdirectory)"
		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-val conf] ne ""} {
		set autosetup(autodef) [opt-val conf]
	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
	foreach file [glob -nocomplain $autosetup(libdir)/*.auto $autosetup(libdir)/*/*.auto] {
		automf_load source $file
	}
	autosetup_load_auto_modules


	if {[opt-val help] ne ""} {
	if {[opt-str help o]} {
		incr autosetup(showhelp)
		use help
		autosetup_help [opt-val help]
		autosetup_help $o
	}

	if {[opt-bool licence license]} {
		use help
		autosetup_show_license
		exit 0
	}

	if {[opt-val {manual ref reference}] ne ""} {
	if {[opt-str {manual ref reference} o]} {
		use help
		autosetup_reference [opt-val {manual ref reference}]
		autosetup_reference $o
	}

	# Allow combining --install and --init
	set earlyexit 0
	if {[opt-val install] ne ""} {
	if {[opt-str install o]} {
		use install
		autosetup_install [opt-val install]
		autosetup_install $o
		incr earlyexit
	}

	if {[opt-val init] ne ""} {
	if {[opt-str init o]} {
		use init
		autosetup_init [opt-val 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)"
	}
183
184
185
186
187
188
189

190
191
192
193
194
195
196
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224







+







	}
	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
208
209
210
211
212
213
214
215

216
217
218

219
220
221
222
223
224
225
236
237
238
239
240
241
242

243
244
245

246
247
248
249
250
251
252
253







-
+


-
+








# @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
# 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
# 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]
235
236
237
238
239
240
241
242

243
244

245
246
247
248
249

250
251

252
253
254
255
256
257
258
259
260
261
262
263
264
265









































































266
267
268
269
270
271
272
263
264
265
266
267
268
269

270
271

272
273
274

275

276
277

278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372







-
+

-
+


-

-
+

-
+














+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







	if {$nodefault} {
		return -1
	}
	# Default value is the default for the first option
	return [dict get $::autosetup(optdefault) [lindex $args 0]]
}

# @opt-val option-list ?default=""?
# @opt-val optionlist ?default=""?
#
# Returns a list containing all the values given for the non-boolean options in 'option-list'.
# 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 only a single value is required, use something like:
#
## lindex [opt-val $names] end
# If no options were set, '$default' is returned (exactly, not as a list).
#
# 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"
		}
	}
291
292
293
294
295
296
297

298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314






315
316
317
318
319
320
321
322
323
324
325

326

327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346





















347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368

369
370
371
372
373
374
375
376
377
378
379
380



381
382
383
384
385
386
387
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433

434
435
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







+

















+
+
+
+
+
+











+
-
+




















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+








-












-
+












+
+
+







		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 $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"
			}
			dict set autosetup(optdefault) $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 $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
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
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







-
+


-
+
+





-
-
+
+






-
-
+
+


+
+
+
-
-
+
+

-
+







			puts $desc
		} else {
			options-wrap-desc [string trim $desc] $cols "  " $indent [expr $max + 2]
		}
	}
}

# @options options-spec
# @options optionspec
#
# Specifies configuration-time options which may be selected by the user
# and checked with opt-val and opt-bool. The format of options-spec follows.
# 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
# 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 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.
# 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:
# 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:"

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
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








+
+
+
+
+
+
+
+
+
+
+

-
+
-
-
+




-
+





-
-
+
+










-
+








-
+








-
-
+
+






-
+

+
+
+
+
-
+
+
+
+
+
+











-
+


















+
+
+
+
+
+
+
+
+
+
+
+



-
+











-
-
-
+
+
+










-
+















-
+













-
+







		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)/config.guess]} {
	if {[file-isexec $::autosetup(dir)/autosetup-config.guess]} {
		exec-with-stderr sh $::autosetup(dir)/config.guess
		if {[catch {exec-with-stderr sh $::autosetup(dir)/config.guess} alias]} {
		if {[catch {exec-with-stderr sh $::autosetup(dir)/autosetup-config.guess} alias]} {
			user-error $alias
		}
		return $alias
	} else {
		configlog "No config.guess, so using uname"
		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)/config.sub]} {
		if {[catch {exec-with-stderr sh $::autosetup(dir)/config.sub $alias} 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 checked, modified and substituted.
# 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
# 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.
# 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 ""} {
		# Make a token attempt to avoid duplicates
		# Avoid duplicates
		foreach arg $args {
			if {$arg eq ""} {
				continue
			}
			set found 0
			if {[string first $arg $::define($name)] == -1} {
			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
# 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.
# 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.
# If $name was set in the environment, return it.
# Otherwise return $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 the $name was specified on the command line or in the environment.
# 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.
# 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.
# 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
}
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
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







-
+

-
+
-
-
-
-
-
+


+
-
-
+
+
+



-
+

+
+
-
-
-
+
+
+
-

-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





-
+




-
-
-
-
+


-
-
-
-
-
-
+
-
-
-







	set args {}
	foreach arg $argv {
		lappend args [quote-if-needed $arg]
	}
	join $args
}

# @suffix suf list
# @list-non-empty list
#
# Takes a list and returns a new list with $suf appended
# Returns a copy of the given list with empty elements removed
# to each element
#
## suffix .c {a b c} => {a.c b.c c.c}
#
proc suffix {suf list} {
proc list-non-empty {list} {
	set result {}
	foreach p $list {
		if {$p ne ""} {
		lappend result $p$suf
	}
			lappend result $p
		}
	}
	return $result
}

# @prefix pre list
# @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',
# Takes a list and returns a new list with $pre prepended
# to each element
#
# 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.
## 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
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",
# 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} {
	# Ignore any parameters
	set name [lindex $name 0]
	if {$name eq ""} {
		# The empty string is never a valid executable
	if {[find-executable-path $name] eq {}} {
		return 0
	}
	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 1
	return 1
		}
	}
	return 0
}

# @find-an-executable ?-required? name ...
#
# Given a list of possible executable names,
# searches for one of these on the path.
#
762
763
764
765
766
767
768
769

770
771
772
773
774
775
776
925
926
927
928
929
930
931

932
933
934
935
936
937
938
939







-
+







		}
	}
	return ""
}

# @configlog msg
#
# Writes the given message to the configuration log, config.log
# 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
}
798
799
800
801
802
803
804
805
806


807
808
809
810
811
812
813
961
962
963
964
965
966
967


968
969
970
971
972
973
974
975
976







-
-
+
+







		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.
# '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
839
840
841
842
843
844
845
846

847
848
849
850
851
852
853
1002
1003
1004
1005
1006
1007
1008

1009
1010
1011
1012
1013
1014
1015
1016







-
+







#
# 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.
# '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
}
885
886
887
888
889
890
891











892
893
894
895
896
897
898
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







+
+
+
+
+
+
+
+
+
+
+







}

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)"
}

##################################################################
#
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
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







+
+
+
+
+
+

-
+


-
-
-
+
+
+

-
+

+
+
-
-
-
+
+
+
+
+
+
+















+
+
+
+
+
+
+
+
+
+
+
+













+


+



+
-
+

-
+







# 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)]} {
		if {[info exists libmodule($m)]} {
			continue
		}
		set ::libmodule($m) 1
		if {[info exists ::modsource($m)]} {
			automf_load eval $::modsource($m)
		set libmodule($m) 1
		if {[info exists modsource(${m}.tcl)]} {
			automf_load eval $modsource(${m}.tcl)
		} else {
			set sources [list $::autosetup(libdir)/${m}.tcl $::autosetup(libdir)/${m}/init.tcl]
			set locs [list ${m}.tcl ${m}/init.tcl]
			set found 0
			foreach dir $dirs {
				foreach loc $locs {
			foreach source $sources {
				if {[file exists $source]} {
					incr found
					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 -----
# ----- @module asciidoc-formatting.tcl -----

set modsource(asciidoc-formatting) {
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
1100
1101
1102
1103
1104
1105
1106
1107

1108
1109

1110
1111
1112
1113
1114
1115

1116
1117
1118
1119
1120
1121
1122
1301
1302
1303
1304
1305
1306
1307

1308
1309

1310
1311
1312
1313
1314
1315

1316
1317
1318
1319
1320
1321
1322
1323







-
+

-
+





-
+







    }
    set defn [string trim [join $args \n]]
    regsub -all "\n\n" $defn "\n ::\n" defn
    puts $defn
}
}

# ----- module formatting -----
# ----- @module formatting.tcl -----

set modsource(formatting) {
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 documenation which looks like:
# This is designed for documentation which looks like:
# code {...}
# or
# code {
#    ...
#    ...
# }
# In the second case, we need to work out the indenting
1157
1158
1159
1160
1161
1162
1163
1164

1165
1166

1167
1168
1169
1170
1171
1172
1173
1358
1359
1360
1361
1362
1363
1364

1365
1366

1367
1368
1369
1370
1371
1372
1373
1374







-
+

-
+







    }

    # Return the result
    return $lines
}
}

# ----- module getopt -----
# ----- @module getopt.tcl -----

set modsource(getopt) {
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
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
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







-
+




















-
+

-
+







			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- with- ""}} {
			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 -----
# ----- @module help.tcl -----

set modsource(help) {
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
1249
1250
1251
1252
1253
1254
1255


















1256
1257
1258
1259
1260
1261
1262
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







            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
1302
1303
1304
1305
1306
1307
1308






1309
1310
1311
1312
1313
1314
1315
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540







+
+
+
+
+
+








    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 {
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
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







+
+
+
+
+
-
-
-
-
+
+
+
+
+
+


+
+



+
+
+
+
+








+
-
+

-
+







}

# 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
    section "Core Commands"
    set type p
    set lines {}
    set cmd {}
    # 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
                section "Module: [file rootname [file tail $file]]"
                lappend doc($current) [list section "Module: $modulename"]
            } else {
                subsection $cmd
                lappend doc($current) [list subsection $cmd]
            }

            set lines {}
            set type p

            # Now the description
            while {![eof $f]} {
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
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







-
+








-
+



+
+
+
+
+
+
+
+
+



-
+

-
+







                    set t p
                }

                #puts "hash=$hash, oldhash=$oldhash, lines=[llength $lines], cmd=$cmd"

                if {$t ne $type || $cmd eq ""} {
                    # Finish the current block
                    autosetup_output_block $type $lines
                    lappend doc($current) [list $type $lines]
                    set lines {}
                    set type $t
                }
                if {$cmd ne ""} {
                    lappend lines $cmd
                }
            }

            autosetup_output_block $type $lines
            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 -----
# ----- @module init.tcl -----

set modsource(init) {
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
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
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







-
+

-
+





+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+

-
+


-
+








+

+
-
+
+
+

+
-
-
-
-
-
-
+
+
+
+
+
+
+




+
+
+
+
+
+
+



-
+

+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+

-
-
+
+
+
+
+
+
+
+

-
-
+
+
+
+
+
+
-
-
-
+



+
+
+
+
+
-
+

+
-
-
+
+
+


-
+














+
-
+

+
+
+
+
+

-
+

+





+





-
-
-
+
+
+
+

+
+
+
+
+
+
-
+
+
+
+
+
+
+

+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+

+
+
-
-
+
+
+
+

-
+







	} else {
		puts "I don't see $filename, so I will create it."
	}
	writefile $filename $contents
}
}

# ----- module install -----
# ----- @module install.tcl -----

set modsource(install) {
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} {
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 f [open autosetup/autosetup w]
			cd $dir
			file mkdir autosetup
			set target autosetup/autosetup
		}
		set targetdir [file dirname $target]
		file mkdir $targetdir

		set f [open $target w]

		set publicmodules [glob $::autosetup(libdir)/*.auto]
		set publicmodules {}

		# First the main script, but only up until "CUT HERE"
		set in [open $::autosetup(dir)/autosetup]
		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]] {
			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 {[string match "*\n# @synopsis:*" $buf]} {
					lappend publicmodules $file
					continue
				}
				set modname [file rootname [file tail $file]]
				puts $f "# ----- module $modname -----"
					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
		exec chmod 755 autosetup/autosetup
		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 {
			autosetup_install_file $file autosetup
		}

			# 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 file {config.guess config.sub jimsh0.c find-tclsh test-tclsh LICENSE} {
			autosetup_install_file $::autosetup(dir)/$file autosetup
		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
		}
		exec chmod 755 autosetup/config.sub autosetup/config.guess autosetup/find-tclsh


		# Remove obsolete files
		foreach file $removefiles {
			if {[file exists $targetdir/$file]} {
				file delete $targetdir/$file
			}
		writefile autosetup/README.autosetup \
			"This is [autosetup_version]. See http://msteveb.github.com/autosetup/\n"

		}
	} error]} {
		user-error "Failed to install autosetup: $error"
	}
	if {$shared} {
		set type "system"
	} else {
		set type "local"
	}
	puts "Installed [autosetup_version] to autosetup/"
	puts "Installed $type [autosetup_version] to $installedas"

	if {!$shared} {
	# Now create 'configure' if necessary
	autosetup_create_configure
		# Now create 'configure' if necessary
		autosetup_create_configure 0
	}
}

proc autosetup_create_configure {} {
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 \
		writefile configure \
{#!/bin/sh
WRAPPER="$0"; export WRAPPER; "autosetup" "$@"
}
	} else {
		writefile configure \
{#!/bin/sh
dir="`dirname "$0"`/autosetup"
WRAPPER="$0"; export WRAPPER; exec "`$dir/find-tclsh`" "$dir/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 {file dir} {
	if {![file exists $file]} {
		error "Missing installation file '$file'"
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}
}
	writefile [file join $dir [file tail $file]] [readfile $file]\n

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.

if {$::autosetup(installed)} {
	user-error "autosetup can only be installed from development source, not from installed copy"
}
}
}

	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 -----
}
}

# ----- @module markdown-formatting.tcl -----

set modsource(markdown-formatting) {
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
1625
1626
1627
1628
1629
1630
1631
1632

1633
1634

1635
1636
1637
1638
1639
1640
1641
1987
1988
1989
1990
1991
1992
1993

1994
1995

1996
1997
1998
1999
2000
2001
2002
2003







-
+

-
+







        puts -nonewline ": "
        regsub -all "\n\n" $defn "\n: " defn
    }
    puts "$defn"
}
}

# ----- module misc -----
# ----- @module misc.tcl -----

set modsource(misc) {
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]
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
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







-
+

-
+








-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


-
-
+
+


-
-
+
+


-
-
-
-
+
+
+
+


-
+


-
-
-
+
+
+


-
-
+
+


-
-
+
+


-
+


-
+


-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+



+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+

-
+








		# Remove the trailing newline
		string trim $result
	}
}
}

# ----- module text-formatting -----
# ----- @module text-formatting.tcl -----

set modsource(text-formatting) {
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 == ""} {
            continue
        }
        if {$len && [string length $space$word] + $len >= $length} {
            puts ""
            set len 0
            set space $nextprefix
        }
        incr len [string length $space$word]
	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 {^'(.*)'([^a-zA-Z0-9_]*)$} $word -> bareword dot]} {
            regsub -all . $bareword "_\b&" word
            append word $dot
        } elseif {[regexp {^[*](.*)[*]([^a-zA-Z0-9_]*)$} $word -> bareword dot]} {
            regsub -all . $bareword "&\b&" word
            append word $dot
        }
        puts -nonewline $space$word
        set space " "
    }
    if {$len} {
        puts ""
    }
		# 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
	underline [string trim $text] =
	nl
}
proc p {text} {
    wordwrap $text 80
    nl
	wordwrap $text 80
	nl
}
proc codelines {lines} {
    foreach line $lines {
        puts "    $line"
    }
    nl
	foreach line $lines {
		puts "	  $line"
	}
	nl
}
proc nl {} {
    puts ""
	puts ""
}
proc underline {text char} {
    regexp "^(\[ \t\]*)(.*)" $text -> indent words
    puts $text
    puts $indent[string repeat $char [string length $words]]
	regexp "^(\[ \t\]*)(.*)" $text -> indent words
	puts $text
	puts $indent[string repeat $char [string length $words]]
}
proc section {text} {
    underline "[string trim $text]" -
    nl
	underline "[string trim $text]" -
	nl
}
proc subsection {text} {
    underline "$text" ~
    nl
	underline "$text" ~
	nl
}
proc bullet {text} {
    wordwrap $text 76 "  * " "    "
	wordwrap $text 76 "	 * " "	  "
}
proc indent {text} {
    wordwrap $text 76 "    " "    "
	wordwrap $text 76 "	   " "	  "
}
proc defn {first args} {
    if {$first ne ""} {
        underline "    $first" ~
    }
    foreach p $args {
        if {$p ne ""} {
            indent $p
        }
    }
	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 -----
# ----- @module wiki-formatting.tcl -----

set modsource(wiki-formatting) {
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
1973
1974
1975
1976
1977
1978
1979
1980

1981
1982
1983
1984
1985
2447
2448
2449
2450
2451
2452
2453

2454
2455
2456
2457
2458
2459







-
+





# 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)]
	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

Changes to autosetup/cc-db.tcl.

1
2
3
4
5
6
7


8
9
10
11
12
13
14
1
2
3
4
5


6
7
8
9
10
11
12
13
14





-
-
+
+







# Copyright (c) 2011 WorkWare Systems http://www.workware.net.au/
# All rights reserved

# @synopsis:
#
# The 'cc-db' module provides a knowledge based of system idiosyncrasies
# In general, this module can always be included
# 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

Changes to 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
1
2
3
4
5
6
7
8
9
10
11
12
13


14
15
16
17
18
19
20
21
22













-
-
+
+







# 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
# 
# 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
33
34
35
36
37
38
39
40
41


42
43
44
45
46
47
48
33
34
35
36
37
38
39


40
41
42
43
44
45
46
47
48







-
-
+
+







	}
	define-feature lfs $lfs
	return $lfs
}

# @cc-check-endian
#
# The equivalent of the AC_C_BIGENDIAN macro
# 
# 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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112




























113
114
115
116
117
118
119
120
121
122
123
124
125
126
127







128
129
130
131
132
133


134
135
136
137
138
139
140





141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156









157
158
159
160



161
162
163
164
165

166
167
168


169
170
171
172



173
174
175
176
177

178
179

180
181
182
183
184
185
186
187
188
189
190










191
78
79
80
81
82
83
84




























85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120







121
122
123
124
125
126
127
128
129
130
131


132
133







134
135
136
137
138
139
140
141
142
143
144
145









146
147
148
149
150
151
152
153
154
155



156
157
158
159
160
161
162

163
164


165
166
167



168
169
170
171
172
173
174

175
176

177
178










179
180
181
182
183
184
185
186
187
188
189







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+








-
-
-
-
-
-
-
+
+
+
+
+
+
+




-
-
+
+
-
-
-
-
-
-
-
+
+
+
+
+







-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+

-
-
-
+
+
+




-
+

-
-
+
+

-
-
-
+
+
+




-
+

-
+

-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+


# @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
	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 ""
	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 [subst -nobackslashes {
	msg-checking "Checking for $keyword..."
	if {[cctest -code "int x = ${keyword}(char), y = ${keyword}('x');"]} then {
        printf("minimum alignment is %d == %d\n", ${keyword}(char), ${keyword}('x'));
    }]]} then {
        msg-result ok
        define-feature $keyword
    } else {
        msg-result "not found"
    }
		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"
    }
	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
	cctest_alignof _Alignof
	cctest_alignof __alignof__
	cctest_alignof __alignof
}

# @cc-check-alloca
#
# The equivalent of the AC_FUNC_ALLOCA macro
# The equivalent of the 'AC_FUNC_ALLOCA' macro.
#
# Checks for the existence of alloca
# defines HAVE_ALLOCA and returns 1 if it exists
# 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-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
# The equivalent of the 'AC_TYPE_SIGNAL' macro.
#
# defines RETSIGTYPE to int or void
# 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
    }
	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
	}
}

Changes to 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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

26
27

28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71

72
73
74
75
76
77




78
79
80
81
82
83

84
85
86
87
88
89
90
91











+





+







-
+

-
+


+




















+








+










-
+





-
-
-
-






-
+







# 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_CFLAGS -fPIC
define SHOBJ_LDFLAGS -shared
define SH_CFLAGS -fpic
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. 
			# 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,
		} else {
			# sparc has a very small GOT table limit, so use -fPIC
			define SH_CFLAGS -fPIC
			define SHOBJ_CFLAGS -fPIC
		}
	}
	*-*-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. 
			# 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,
		}
	}
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
102
103
104
105
106
107
108





109
110
111
112
113







-
-
-
-
-





		define SHOBJ_LDFLAGS -shared
		define SH_CFLAGS ""
		define SH_LDFLAGS -shared
		define SH_LINKFLAGS ""
		define SH_SOPREFIX ""
		define LD_LIBRARY_PATH LIBRARY_PATH
	}
	microblaze* {
		# Microblaze generally needs -fPIC rather than -fpic
		define SHOBJ_CFLAGS -fPIC
		define SH_CFLAGS -fPIC
	}
}

if {![is-defined SHOBJ_LDFLAGS_R]} {
	define SHOBJ_LDFLAGS_R [get-define SHOBJ_LDFLAGS]
}

Changes to 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
1
2
3
4
5
6


7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33





34
35
36
37
38
39
40






-
-
+
+





+



















-
-
-
-
-







# 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-autoconf-h and make-template.
# 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 {}

# Note that the return code is not meaningful
proc cc-check-something {name code} {
	uplevel 1 $code
}

# 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
66
67
68
69
70
71
72
73
74


75
76
77
78
79
80
81
62
63
64
65
66
67
68


69
70
71
72
73
74
75
76
77







-
-
+
+







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.
# 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
107
108
109
110
111
112
113
114

115
116
117
118
119
120
121
103
104
105
106
107
108
109

110
111
112
113
114
115
116
117







-
+







		}
	}
	return $ret
}

# @cc-check-includes includes ...
#
# Checks that the given include files can be used
# 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 {
132
133
134
135
136
137
138
139
140


141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158

159
160
161
162
163
164
165
166
167
168
169


170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188

189
190
191
192
193
194
195
196
197
198

199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214



215
216
217
218


219
220
221

222
223
224
225
226
227
228
128
129
130
131
132
133
134


135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153

154
155
156
157
158
159
160
161
162
163


164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183

184
185
186
187
188
189
190
191
192
193

194
195
196
197
198
199
200
201
202
203
204
205
206
207



208
209
210
211
212


213
214
215
216

217
218
219
220
221
222
223
224







-
-
+
+

















-
+









-
-
+
+


















-
+









-
+













-
-
-
+
+
+


-
-
+
+


-
+







			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
# 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 symbol is defined
# 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
# 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)
# 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"
# 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 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
# as $extralibs as "-lotherlib1 -lotherlib2".
# These libraries are not automatically added to LIBS.
# 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 ""
238
239
240
241
242
243
244

245

246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261




262
263

264
265
266
267
268
269
270
234
235
236
237
238
239
240
241

242


243
244
245
246
247
248
249
250
251
252




253
254
255
256
257

258
259
260
261
262
263
264
265







+
-
+
-
-










-
-
-
-
+
+
+
+

-
+







						incr found
						break
					}
				}
			}
		}
	}
	define-feature $function $found
	if {$found} {
	if {!$found} {
		define [feature-define-name $function]
	} else {
		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
# 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,
# 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]
282
283
284
285
286
287
288
289
290


291
292

293
294
295
296
297
298
299
300
301
302
303
304
305
306
307























308
309
310
311
312
313
314
277
278
279
280
281
282
283


284
285
286

287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332







-
-
+
+

-
+















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







	}
}

# @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".
# 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.
# 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.
#
324
325
326
327
328
329
330
331

332
333
334
335
336
337
338

339
340
341
342
343
344
345
342
343
344
345
346
347
348

349
350
351
352
353
354
355

356
357
358
359
360
361
362
363







-
+






-
+








	array set new $prev

	foreach {name value} $settings {
		switch -exact -- $name {
			-cflags - -includes {
				# These are given as lists
				lappend new($name) {*}$value
				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 {*}$value {*}$new($name)]
				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
371
372
373
374
375
376
377
378
379
380



381
382
383

384
385
386
387
388
389
390
391
392
393
394
395

396
397
398
399
400
401
402
389
390
391
392
393
394
395



396
397
398
399
400

401
402
403
404
405
406
407
408
409
410
411
412

413
414
415
416
417
418
419
420







-
-
-
+
+
+


-
+











-
+







	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.
# 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.
# 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.
# 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
##   }
## }
413
414
415
416
417
418
419
420
421


422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437

438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
431
432
433
434
435
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







-
-
+
+















-
+













-







			return -code [dict get $info -code] $result
		}
		return $result
	}
}

# @cctest ?settings?
# 
# Low level C compiler checker. Compiles and or links a small C program
#
# 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:
# 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 src conftest__.c
	set tmp conftest__

	# Easiest way to merge in the settings
	cc-with $args {
		array set opts [cc-get-settings]
	}

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







+



+







	}

	# 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)"
		}
	}

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
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







-
+








-
+

-
+

-
+

-
+


-
+


-
+







	set ::cc_cache($cmdline,$lines) $ok

	return $ok
}

# @make-autoconf-h outfile ?auto-patterns=HAVE_*? ?bare-patterns=SIZEOF_*?
#
# Deprecated - see make-config-header
# 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.
# 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 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"
# - 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 '-str' are defined as a string, e.g. '"value"'
# Variables which match '-none' are omitted.
#
# Note that order is important. The first pattern which matches is selected
# Note that order is important. The first pattern that matches is selected.
# Default behaviour is:
#
#  -bare {SIZEOF_* HAVE_DECL_*} -auto HAVE_* -none *
##  -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"
675
676
677
678
679
680
681









682
683
684
685
686
687
688
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716







+
+
+
+
+
+
+
+
+







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"} {

Deleted 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













































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
#! /bin/sh
# Attempt to guess a canonical system name.
#   Copyright 1992-2014 Free Software Foundation, Inc.

timestamp='2014-11-04'

# This file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.
#
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that
# program.  This Exception is an additional permission under section 7
# of the GNU General Public License, version 3 ("GPLv3").
#
# Originally written by Per Bothner; maintained since 2000 by Ben Elliston.
#
# You can get the latest version of this script from:
# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
#
# Please send patches to <config-patches@gnu.org>.


me=`echo "$0" | sed -e 's,.*/,,'`

usage="\
Usage: $0 [OPTION]

Output the configuration name of the system \`$me' is run on.

Operation modes:
  -h, --help         print this help, then exit
  -t, --time-stamp   print date of last modification, then exit
  -v, --version      print version number, then exit

Report bugs and patches to <config-patches@gnu.org>."

version="\
GNU config.guess ($timestamp)

Originally written by Per Bothner.
Copyright 1992-2014 Free Software Foundation, Inc.

This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."

help="
Try \`$me --help' for more information."

# 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'`
	;;
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=`(/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 ;;
	    *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
	esac
	# The Operating System including object format, if it has switched
	# to ELF recently, or will in the future.
	case "${UNAME_MACHINE_ARCH}" in
	    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
	# The OS release
	# Debian GNU/NetBSD machines have a different userland, and
	# thus, need a distinct triplet. However, they do not need
	# kernel version information, so it can be replaced with a
	# suitable tag, in the style of linux-gnu.
	case "${UNAME_VERSION}" in
	    Debian*)
		release='-gnu'
		;;
	    *)
		release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
		;;
	esac
	# Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
	# contains redundant information, the shorter form:
	# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
	echo "${machine}-${os}${release}"
	exit ;;
    *:Bitrig:*:*)
	UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'`
	echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE}
	exit ;;
    *:OpenBSD:*:*)
	UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
	echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE}
	exit ;;
    *:ekkoBSD:*:*)
	echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE}
	exit ;;
    *: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 ;;
    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 ;;
    Alpha\ *:Windows_NT*:*)
	# How do we know it's Interix rather than the generic POSIX subsystem?
	# Should we change UNAME_MACHINE based on the output of uname instead
	# of the specific Alpha model?
	echo alpha-pc-interix
	exit ;;
    21064:Windows_NT:50:3)
	echo alpha-dec-winnt3.5
	exit ;;
    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:BSD:*)
	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)
		echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
	    *)
		echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
	esac
	exit ;;
    i*:CYGWIN*:*)
	echo ${UNAME_MACHINE}-pc-cygwin
	exit ;;
    *:MINGW64*:*)
	echo ${UNAME_MACHINE}-pc-mingw64
	exit ;;
    *:MINGW*:*)
	echo ${UNAME_MACHINE}-pc-mingw32
	exit ;;
    *:MSYS*:*)
	echo ${UNAME_MACHINE}-pc-msys
	exit ;;
    i*:windows32*:*)
	# uname -m includes "-pc" on this system.
	echo ${UNAME_MACHINE}-mingw32
	exit ;;
    i*:PW*:*)
	echo ${UNAME_MACHINE}-pc-pw32
	exit ;;
    *:Interix*:*)
	case ${UNAME_MACHINE} in
	    x86)
		echo i586-pc-interix${UNAME_RELEASE}
		exit ;;
	    authenticamd | genuineintel | EM64T)
		echo x86_64-unknown-interix${UNAME_RELEASE}
		exit ;;
	    IA64)
		echo ia64-unknown-interix${UNAME_RELEASE}
		exit ;;
	esac ;;
    [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
	echo i${UNAME_MACHINE}-pc-mks
	exit ;;
    8664:Windows_NT:*)
	echo x86_64-pc-mks
	exit ;;
    i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
	# How do we know it's Interix rather than the generic POSIX subsystem?
	# It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
	# UNAME_MACHINE based on the output of uname instead of i386?
	echo i586-pc-interix
	exit ;;
    i*:UWIN*:*)
	echo ${UNAME_MACHINE}-pc-uwin
	exit ;;
    amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
	echo x86_64-unknown-cygwin
	exit ;;
    p*:CYGWIN*:*)
	echo powerpcle-unknown-cygwin
	exit ;;
    prep*:SunOS:5.*:*)
	echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
	exit ;;
    *:GNU:*:*)
	# the GNU system
	echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
	exit ;;
    *:GNU/*:*:*)
	# other systems with GNU libc and userland
	echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC}
	exit ;;
    i*86:Minix:*:*)
	echo ${UNAME_MACHINE}-pc-minix
	exit ;;
    aarch64:Linux:*:*)
	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
	exit ;;
    aarch64_be:Linux:*:*)
	UNAME_MACHINE=aarch64_be
	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
	exit ;;
    alpha:Linux:*:*)
	case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
	  EV5)   UNAME_MACHINE=alphaev5 ;;
	  EV56)  UNAME_MACHINE=alphaev56 ;;
	  PCA56) UNAME_MACHINE=alphapca56 ;;
	  PCA57) UNAME_MACHINE=alphapca56 ;;
	  EV6)   UNAME_MACHINE=alphaev6 ;;
	  EV67)  UNAME_MACHINE=alphaev67 ;;
	  EV68*) UNAME_MACHINE=alphaev68 ;;
	esac
	objdump --private-headers /bin/sh | grep -q ld.so.1
	if test "$?" = 0 ; then LIBC="gnulibc1" ; fi
	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
	exit ;;
    arc:Linux:*:* | arceb:Linux:*:*)
	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
	exit ;;
    arm*:Linux:*:*)
	eval $set_cc_for_build
	if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
	    | grep -q __ARM_EABI__
	then
	    echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
	else
	    if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
		| grep -q __ARM_PCS_VFP
	    then
		echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi
	    else
		echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf
	    fi
	fi
	exit ;;
    avr32*:Linux:*:*)
	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
	exit ;;
    cris:Linux:*:*)
	echo ${UNAME_MACHINE}-axis-linux-${LIBC}
	exit ;;
    crisv32:Linux:*:*)
	echo ${UNAME_MACHINE}-axis-linux-${LIBC}
	exit ;;
    frv:Linux:*:*)
	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
	exit ;;
    hexagon:Linux:*:*)
	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
	exit ;;
    i*86:Linux:*:*)
	echo ${UNAME_MACHINE}-pc-linux-${LIBC}
	exit ;;
    ia64:Linux:*:*)
	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
	exit ;;
    m32r*:Linux:*:*)
	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
	exit ;;
    m68*:Linux:*:*)
	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
	exit ;;
    mips:Linux:*:* | mips64:Linux:*:*)
	eval $set_cc_for_build
	sed 's/^	//' << EOF >$dummy.c
	#undef CPU
	#undef ${UNAME_MACHINE}
	#undef ${UNAME_MACHINE}el
	#if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
	CPU=${UNAME_MACHINE}el
	#else
	#if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
	CPU=${UNAME_MACHINE}
	#else
	CPU=
	#endif
	#endif
EOF
	eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'`
	test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; }
	;;
    openrisc*:Linux:*:*)
	echo or1k-unknown-linux-${LIBC}
	exit ;;
    or32:Linux:*:* | or1k*:Linux:*:*)
	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
	exit ;;
    padre:Linux:*:*)
	echo sparc-unknown-linux-${LIBC}
	exit ;;
    parisc64:Linux:*:* | hppa64:Linux:*:*)
	echo hppa64-unknown-linux-${LIBC}
	exit ;;
    parisc:Linux:*:* | hppa:Linux:*:*)
	# Look for CPU level
	case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
	  PA7*) echo hppa1.1-unknown-linux-${LIBC} ;;
	  PA8*) echo hppa2.0-unknown-linux-${LIBC} ;;
	  *)    echo hppa-unknown-linux-${LIBC} ;;
	esac
	exit ;;
    ppc64:Linux:*:*)
	echo powerpc64-unknown-linux-${LIBC}
	exit ;;
    ppc:Linux:*:*)
	echo powerpc-unknown-linux-${LIBC}
	exit ;;
    ppc64le:Linux:*:*)
	echo powerpc64le-unknown-linux-${LIBC}
	exit ;;
    ppcle:Linux:*:*)
	echo powerpcle-unknown-linux-${LIBC}
	exit ;;
    s390:Linux:*:* | s390x:Linux:*:*)
	echo ${UNAME_MACHINE}-ibm-linux-${LIBC}
	exit ;;
    sh64*:Linux:*:*)
	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
	exit ;;
    sh*:Linux:*:*)
	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
	exit ;;
    sparc:Linux:*:* | sparc64:Linux:*:*)
	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
	exit ;;
    tile*:Linux:*:*)
	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
	exit ;;
    vax:Linux:*:*)
	echo ${UNAME_MACHINE}-dec-linux-${LIBC}
	exit ;;
    x86_64:Linux:*:*)
	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
	exit ;;
    xtensa*:Linux:*:*)
	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
	exit ;;
    i*86:DYNIX/ptx:4*:*)
	# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
	# earlier versions are messed up and put the nodename in both
	# sysname and nodename.
	echo i386-sequent-sysv4
	exit ;;
    i*86:UNIX_SV:4.2MP:2.*)
	# Unixware is an offshoot of SVR4, but it has its own version
	# number series starting with 2...
	# I am not positive that other SVR4 systems won't match this,
	# I just have to hope.  -- rms.
	# Use sysv4.2uw... so that sysv4* matches it.
	echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
	exit ;;
    i*86:OS/2:*:*)
	# If we were able to find `uname', then EMX Unix compatibility
	# is probably installed.
	echo ${UNAME_MACHINE}-pc-os2-emx
	exit ;;
    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.*:* | i*86:SYSTEM_V: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 configury will decide that
	# this is a cross-build.
	echo i586-pc-msdosdjgpp
	exit ;;
    Intel:Mach:3*:*)
	echo i386-pc-mach3
	exit ;;
    paragon:*:*:*)
	echo i860-intel-osf1
	exit ;;
    i860:*:4.*:*) # i860-SVR4
	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 ;;
    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
	    fi
	elif test "$UNAME_PROCESSOR" = i386 ; then
	    # Avoid executing cc on OS X 10.9, as it ships with a stub
	    # that puts up a graphical alert prompting to install
	    # developer tools.  Any system running Mac OS X 10.7 or
	    # later (Darwin 11 and later) is required to have a 64-bit
	    # processor. This is not true of the ARM version of Darwin
	    # that Apple uses in portable devices.
	    UNAME_PROCESSOR=x86_64
	fi
	echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
	exit ;;
    *:procnto*:*:* | *:QNX:[0123456789]*:*)
	UNAME_PROCESSOR=`uname -p`
	if test "$UNAME_PROCESSOR" = "x86"; then
		UNAME_PROCESSOR=i386
		UNAME_MACHINE=pc
	fi
	echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}
	exit ;;
    *:QNX:*:4*)
	echo i386-pc-qnx
	exit ;;
    NEO-?:NONSTOP_KERNEL:*:*)
	echo neo-tandem-nsk${UNAME_RELEASE}
	exit ;;
    NSE-*:NONSTOP_KERNEL:*:*)
	echo nse-tandem-nsk${UNAME_RELEASE}
	exit ;;
    NSR-?:NONSTOP_KERNEL:*:*)
	echo nsr-tandem-nsk${UNAME_RELEASE}
	exit ;;
    *:NonStop-UX:*:*)
	echo mips-compaq-nonstopux
	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 ;;
esac

cat >&2 <<EOF
$0: unable to guess system type

This script, last modified $timestamp, has failed to recognize
the operating system you are using. It is advised that you
download the most up to date version of the config scripts from

  http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
and
  http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD

If the version you run ($0) is already up to date, please
send the following data and any information you think might be
pertinent to <config-patches@gnu.org> in order to provide the needed
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 'write-file-hooks 'time-stamp)
# time-stamp-start: "timestamp='"
# time-stamp-format: "%:y-%02m-%02d"
# time-stamp-end: "'"
# End:

Deleted 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
1802
1803
1804
1805
1806
1807















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
#! /bin/sh
# Configuration validation subroutine script.
#   Copyright 1992-2014 Free Software Foundation, Inc.

timestamp='2014-12-03'

# This file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.
#
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that
# program.  This Exception is an additional permission under section 7
# of the GNU General Public License, version 3 ("GPLv3").


# Please send patches 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:
# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD

# 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
       $0 [OPTION] ALIAS

Canonicalize a configuration name.

Operation modes:
  -h, --help         print this help, then exit
  -t, --time-stamp   print date of last modification, then exit
  -v, --version      print version number, then exit

Report bugs and patches to <config-patches@gnu.org>."

version="\
GNU config.sub ($timestamp)

Copyright 1992-2014 Free Software Foundation, Inc.

This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."

help="
Try \`$me --help' for more information."

# 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* | \
  kopensolaris*-gnu* | \
  storm-chaos* | os2-emx* | rtmk-nova*)
    os=-$maybe_os
    basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
    ;;
  android-linux)
    os=-linux-android
    basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown
    ;;
  *)
    basic_machine=`echo $1 | sed 's/-[^-]*$//'`
    if [ $basic_machine != $1 ]
    then os=`echo $1 | sed 's/.*-/-/'`
    else os=; fi
    ;;
esac

### 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/'`
		;;
	-windowsnt*)
		os=`echo $os | sed -e 's/windowsnt/winnt/'`
		;;
	-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 \
	| be32 | be64 \
	| bfin \
	| c4x | c8051 | clipper \
	| d10v | d30v | dlx | dsp16xx \
	| epiphany \
	| fido | fr30 | frv \
	| h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
	| hexagon \
	| i370 | i860 | i960 | ia64 \
	| ip2k | iq2000 \
	| k1om \
	| le32 | le64 \
	| lm32 \
	| m32c | m32r | m32rle | m68000 | m68k | m88k \
	| maxq | mb | microblaze | microblazeel | mcore | mep | metag \
	| mips | mipsbe | mipseb | mipsel | mipsle \
	| mips16 \
	| mips64 | mips64el \
	| mips64octeon | mips64octeonel \
	| mips64orion | mips64orionel \
	| mips64r5900 | mips64r5900el \
	| mips64vr | mips64vrel \
	| mips64vr4100 | mips64vr4100el \
	| mips64vr4300 | mips64vr4300el \
	| mips64vr5000 | mips64vr5000el \
	| mips64vr5900 | mips64vr5900el \
	| mipsisa32 | mipsisa32el \
	| mipsisa32r2 | mipsisa32r2el \
	| mipsisa32r6 | mipsisa32r6el \
	| mipsisa64 | mipsisa64el \
	| mipsisa64r2 | mipsisa64r2el \
	| mipsisa64r6 | mipsisa64r6el \
	| mipsisa64sb1 | mipsisa64sb1el \
	| mipsisa64sr71k | mipsisa64sr71kel \
	| mipsr5900 | mipsr5900el \
	| mipstx39 | mipstx39el \
	| mn10200 | mn10300 \
	| moxie \
	| mt \
	| msp430 \
	| nds32 | nds32le | nds32be \
	| nios | nios2 | nios2eb | nios2el \
	| ns16k | ns32k \
	| open8 | or1k | or1knd | or32 \
	| pdp10 | pdp11 | pj | pjl \
	| powerpc | powerpc64 | powerpc64le | powerpcle \
	| pyramid \
	| riscv32 | riscv64 \
	| rl78 | rx \
	| score \
	| sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
	| sh64 | sh64le \
	| sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \
	| sparcv8 | sparcv9 | sparcv9b | sparcv9v \
	| spu \
	| tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \
	| ubicom32 \
	| v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \
	| visium \
	| we32k \
	| x86 | xc16x | xstormy16 | xtensa \
	| z8k | z80)
		basic_machine=$basic_machine-unknown
		;;
	c54x)
		basic_machine=tic54x-unknown
		;;
	c55x)
		basic_machine=tic55x-unknown
		;;
	c6x)
		basic_machine=tic6x-unknown
		;;
	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 | z8k)
		;;
	ms1)
		basic_machine=mt-unknown
		;;

	strongarm | thumb | xscale)
		basic_machine=arm-unknown
		;;
	xgate)
		basic_machine=$basic_machine-unknown
		os=-none
		;;
	xscaleeb)
		basic_machine=armeb-unknown
		;;

	xscaleel)
		basic_machine=armel-unknown
		;;

	# We use `pc' rather than `unknown'
	# because (1) that's what they normally are, and
	# (2) the word "unknown" tends to confuse beginning users.
	i*86 | x86_64)
	  basic_machine=$basic_machine-pc
	  ;;
	# Object if more than one company name word.
	*-*-*)
		echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
		exit 1
		;;
	# Recognize the basic CPU types with company name.
	580-* \
	| a29k-* \
	| aarch64-* | aarch64_be-* \
	| alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
	| alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
	| alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \
	| arm-*  | armbe-* | armle-* | armeb-* | armv*-* \
	| avr-* | avr32-* \
	| be32-* | be64-* \
	| bfin-* | bs2000-* \
	| c[123]* | c30-* | [cjt]90-* | c4x-* \
	| c8051-* | clipper-* | craynv-* | cydra-* \
	| d10v-* | d30v-* | dlx-* \
	| elxsi-* \
	| f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \
	| h8300-* | h8500-* \
	| hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
	| hexagon-* \
	| i*86-* | i860-* | i960-* | ia64-* \
	| ip2k-* | iq2000-* \
	| k1om-* \
	| le32-* | le64-* \
	| lm32-* \
	| m32c-* | m32r-* | m32rle-* \
	| m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
	| m88110-* | m88k-* | maxq-* | mcore-* | metag-* \
	| microblaze-* | microblazeel-* \
	| mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
	| mips16-* \
	| mips64-* | mips64el-* \
	| mips64octeon-* | mips64octeonel-* \
	| mips64orion-* | mips64orionel-* \
	| mips64r5900-* | mips64r5900el-* \
	| mips64vr-* | mips64vrel-* \
	| mips64vr4100-* | mips64vr4100el-* \
	| mips64vr4300-* | mips64vr4300el-* \
	| mips64vr5000-* | mips64vr5000el-* \
	| mips64vr5900-* | mips64vr5900el-* \
	| mipsisa32-* | mipsisa32el-* \
	| mipsisa32r2-* | mipsisa32r2el-* \
	| mipsisa32r6-* | mipsisa32r6el-* \
	| mipsisa64-* | mipsisa64el-* \
	| mipsisa64r2-* | mipsisa64r2el-* \
	| mipsisa64r6-* | mipsisa64r6el-* \
	| mipsisa64sb1-* | mipsisa64sb1el-* \
	| mipsisa64sr71k-* | mipsisa64sr71kel-* \
	| mipsr5900-* | mipsr5900el-* \
	| mipstx39-* | mipstx39el-* \
	| mmix-* \
	| mt-* \
	| msp430-* \
	| nds32-* | nds32le-* | nds32be-* \
	| nios-* | nios2-* | nios2eb-* | nios2el-* \
	| none-* | np1-* | ns16k-* | ns32k-* \
	| open8-* \
	| or1k*-* \
	| orion-* \
	| pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
	| powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \
	| pyramid-* \
	| rl78-* | romp-* | rs6000-* | rx-* \
	| sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
	| shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
	| sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \
	| sparclite-* \
	| sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \
	| tahoe-* \
	| tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
	| tile*-* \
	| tron-* \
	| ubicom32-* \
	| v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \
	| vax-* \
	| visium-* \
	| 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-unknown
		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
		;;
	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* | dpx2*-bull)
		basic_machine=m68k-bull
		os=-sysv3
		;;
	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
		;;
	hppa-next)
		os=-nextstep3
		;;
	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
		;;
	i386-vsta | 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
		;;
	m88k-omron*)
		basic_machine=m88k-omron
		;;
	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
		;;
	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 | ppc-le | powerpc-little)
		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 | ppc64-le | powerpc64-little)
		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
		;;
	sh)
		basic_machine=sh-hitachi
		os=-hms
		;;
	sh5el)
		basic_machine=sh5le-unknown
		;;
	sh64)
		basic_machine=sh64-unknown
		;;
	sparclite-wrs | 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
		;;
	xbox)
		basic_machine=i686-pc
		os=-mingw32
		;;
	xps | xps100)
		basic_machine=xps100-honeywell
		;;
	xscale-* | xscalee[bl]-*)
		basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'`
		;;
	ymp)
		basic_machine=ymp-cray
		os=-unicos
		;;
	z8k-*-coff)
		basic_machine=z8k-unknown
		os=-sim
		;;
	z80-*-coff)
		basic_machine=z80-unknown
		os=-sim
		;;
	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
		;;
	pdp10)
		# there are many clones, so DEC is not a safe bet
		basic_machine=pdp10-unknown
		;;
	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
		;;
	sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v)
		basic_machine=sparc-sun
		;;
	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
		;;
	-svr4*)
		os=-sysv4
		;;
	-unixware*)
		os=-sysv4.2uw
		;;
	-gnu/linux*)
		os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
		;;
	# First accept the basic system types.
	# The portable systems comes first.
	# Each alternative MUST END IN A *, to match a version number.
	# -sysv* is not here because it comes later, after sysvr4.
	-gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
	      | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\
	      | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \
	      | -sym* | -kopensolaris* | -plan9* \
	      | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
	      | -aos* | -aros* \
	      | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
	      | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
	      | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \
	      | -bitrig* | -openbsd* | -solidbsd* \
	      | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
	      | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
	      | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
	      | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
	      | -chorusos* | -chorusrdb* | -cegcc* \
	      | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
	      | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \
	      | -linux-newlib* | -linux-musl* | -linux-uclibc* \
	      | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \
	      | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
	      | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
	      | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
	      | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
	      | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
	      | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
	      | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* | -tirtos*)
	# Remember, each alternative MUST END IN *, to match a version number.
		;;
	-qnx*)
		case $basic_machine in
		    x86-* | i*86-*)
			;;
		    *)
			os=-nto$os
			;;
		esac
		;;
	-nto-qnx*)
		;;
	-nto*)
		os=`echo $os | sed -e 's|nto|nto-qnx|'`
		;;
	-sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
	      | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \
	      | -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
		;;
	-osfrose*)
		os=-osfrose
		;;
	-osf*)
		os=-osf
		;;
	-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
		;;
	-es1800*)
		os=-ose
		;;
	-xenix)
		os=-xenix
		;;
	-*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
		os=-mint
		;;
	-aros*)
		os=-aros
		;;
	-zvmoe)
		os=-zvmoe
		;;
	-dicos*)
		os=-dicos
		;;
	-nacl*)
		;;
	-none)
		;;
	*)
		# Get rid of the `-' at the beginning of $os.
		os=`echo $os | sed 's/[^-]*-//'`
		echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
		exit 1
		;;
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
		;;
	*-be)
		os=-beos
		;;
	*-haiku)
		os=-haiku
		;;
	*-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
		;;
	*-next)
		os=-nextstep3
		;;
	*-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 'write-file-hooks 'time-stamp)
# time-stamp-start: "timestamp='"
# time-stamp-format: "%:y-%02m-%02d"
# time-stamp-end: "'"
# End:

Deleted autosetup/find-tclsh.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
















-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
#!/bin/sh
# Looks for a suitable tclsh or jimsh in the PATH
# If not found, builds a bootstrap jimsh from source
d=`dirname "$0"`
{ "$d/jimsh0" "$d/test-tclsh"; } 2>/dev/null && exit 0
PATH="$PATH:$d"; export PATH
for tclsh in jimsh tclsh tclsh8.5 tclsh8.6; do
	{ $tclsh "$d/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"; } 2>/dev/null || continue
	"$d/jimsh0" "$d/test-tclsh" && exit 0
done
echo 1>&2 "No working C compiler found. Tried ${CC_FOR_BUILD:-cc} and gcc."
echo false

Changes to autosetup/jimsh0.c.

1
2
3
4
5
6
7
8
9
10
11
1

2

3
4
5
6
7
8
9

-

-







/* This is single source file, bootstrap version of Jim Tcl. See http://jim.tcl.tk/ */
#define _GNU_SOURCE
#define JIM_TCL_COMPAT
#define JIM_REFERENCES
#define JIM_ANSIC
#define JIM_REGEXP
#define HAVE_NO_AUTOCONF
#define _JIMAUTOCONF_H
#define TCL_LIBRARY "."
#define jim_ext_bootstrap
#define jim_ext_aio
29
30
31
32
33
34
35








36
37
38
39






40
41
42
43
44
45
46
47

48
49

50
51
52
53
54
55
56
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61

62
63
64
65
66
67
68
69







+
+
+
+
+
+
+
+




+
+
+
+
+
+








+

-
+







#define TCL_PLATFORM_PLATFORM "windows"
#define TCL_PLATFORM_PATH_SEPARATOR ";"
#define HAVE_MKDIR_ONE_ARG
#define HAVE_SYSTEM
#define HAVE_SYS_TIME_H
#define HAVE_DIRENT_H
#define HAVE_UNISTD_H
#define HAVE_UMASK
#include <sys/stat.h>
#ifndef S_IRWXG
#define S_IRWXG 0
#endif
#ifndef S_IRWXO
#define S_IRWXO 0
#endif
#else
#define TCL_PLATFORM_OS "unknown"
#define TCL_PLATFORM_PLATFORM "unix"
#define TCL_PLATFORM_PATH_SEPARATOR ":"
#ifdef _MINIX
#define vfork fork
#define _POSIX_SOURCE
#else
#define _GNU_SOURCE
#endif
#define HAVE_VFORK
#define HAVE_WAITPID
#define HAVE_ISATTY
#define HAVE_MKSTEMP
#define HAVE_LINK
#define HAVE_SYS_TIME_H
#define HAVE_DIRENT_H
#define HAVE_UNISTD_H
#define HAVE_UMASK
#endif
#define JIM_VERSION 76
#define JIM_VERSION 78
#ifndef JIM_WIN32COMPAT_H
#define JIM_WIN32COMPAT_H



#ifdef __cplusplus
extern "C" {
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110

111
112
113


114
115
116
117
118
119
120
121

122
123
124
125
126
127
128
129
130
131
132
133
134
135
99
100
101
102
103
104
105

106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121

122
123


124
125
126
127
128
129
130
131


132


133
134
135


136
137
138
139
140
141
142







-
















-
+

-
-
+
+






-
-
+
-
-



-
-







	#define LLONG_MIN    (-LLONG_MAX - 1I64)
#endif
#define JIM_WIDE_MIN LLONG_MIN
#define JIM_WIDE_MAX LLONG_MAX
#define JIM_WIDE_MODIFIER "I64d"
#define strcasecmp _stricmp
#define strtoull _strtoui64
#define snprintf _snprintf

#include <io.h>

struct timeval {
	long tv_sec;
	long tv_usec;
};

int gettimeofday(struct timeval *tv, void *unused);

#define HAVE_OPENDIR
struct dirent {
	char *d_name;
};

typedef struct DIR {
	long                handle; 
	long                handle;
	struct _finddata_t  info;
	struct dirent       result; 
	char                *name;  
	struct dirent       result;
	char                *name;
} DIR;

DIR *opendir(const char *name);
int closedir(DIR *dir);
struct dirent *readdir(DIR *dir);

#elif defined(__MINGW32__)

#endif
#include <stdlib.h>
#define strtod __strtod

#endif

#endif 

#ifdef __cplusplus
}
#endif

#endif
#ifndef UTF8_UTIL_H
#define UTF8_UTIL_H
144
145
146
147
148
149
150
151


152
153
154
155
156
157
158
159

160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182



183
184
185
186
187
188
189
151
152
153
154
155
156
157

158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188



189
190
191
192
193
194
195
196
197
198







-
+
+








+




















-
-
-
+
+
+








int utf8_fromunicode(char *p, unsigned uc);

#ifndef JIM_UTF8
#include <ctype.h>


#define utf8_strlen(S, B) ((B) < 0 ? strlen(S) : (B))
#define utf8_strlen(S, B) ((B) < 0 ? (int)strlen(S) : (B))
#define utf8_strwidth(S, B) utf8_strlen((S), (B))
#define utf8_tounicode(S, CP) (*(CP) = (unsigned char)*(S), 1)
#define utf8_getchars(CP, C) (*(CP) = (C), 1)
#define utf8_upper(C) toupper(C)
#define utf8_title(C) toupper(C)
#define utf8_lower(C) tolower(C)
#define utf8_index(C, I) (I)
#define utf8_charlen(C) 1
#define utf8_prev_len(S, L) 1
#define utf8_width(C) 1

#else

#endif

#ifdef __cplusplus
}
#endif

#endif

#ifndef __JIM__H
#define __JIM__H

#ifdef __cplusplus
extern "C" {
#endif

#include <time.h>
#include <limits.h>
#include <stdio.h>  
#include <stdlib.h> 
#include <stdarg.h> 
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>


#ifndef HAVE_NO_AUTOCONF
#endif



222
223
224
225
226
227
228
229
230


231
232
233
234
235
236
237
238
239





240
241
242
243
244
245




246
247
248
249


250
251
252
253
254
255
256
231
232
233
234
235
236
237


238
239
240
241
242
243





244
245
246
247
248
249
250




251
252
253
254
255
256


257
258
259
260
261
262
263
264
265







-
-
+
+




-
-
-
-
-
+
+
+
+
+


-
-
-
-
+
+
+
+


-
-
+
+







#define JIM_BREAK 3
#define JIM_CONTINUE 4
#define JIM_SIGNAL 5
#define JIM_EXIT 6

#define JIM_EVAL 7

#define JIM_MAX_CALLFRAME_DEPTH 1000 
#define JIM_MAX_EVAL_DEPTH 2000 
#define JIM_MAX_CALLFRAME_DEPTH 1000
#define JIM_MAX_EVAL_DEPTH 2000


#define JIM_PRIV_FLAG_SHIFT 20

#define JIM_NONE 0              
#define JIM_ERRMSG 1            
#define JIM_ENUM_ABBREV 2       
#define JIM_UNSHARED 4          
#define JIM_MUSTEXIST 8         
#define JIM_NONE 0
#define JIM_ERRMSG 1
#define JIM_ENUM_ABBREV 2
#define JIM_UNSHARED 4
#define JIM_MUSTEXIST 8


#define JIM_SUBST_NOVAR 1 
#define JIM_SUBST_NOCMD 2 
#define JIM_SUBST_NOESC 4 
#define JIM_SUBST_FLAG 128 
#define JIM_SUBST_NOVAR 1
#define JIM_SUBST_NOCMD 2
#define JIM_SUBST_NOESC 4
#define JIM_SUBST_FLAG 128


#define JIM_CASESENS    0   
#define JIM_NOCASE      1   
#define JIM_CASESENS    0
#define JIM_NOCASE      1


#define JIM_PATH_LEN 1024


#define JIM_NOTUSED(V) ((void) V)

337
338
339
340
341
342
343
344
345
346
347
348





349
350

351
352

353
354

355
356

357
358

359
360
361
362
363

364






365
366
367


368
369

370
371
372
373

374
375

376
377
378
379



380
381

382
383
384

385
386

387
388
389
390
391

392
393
394
395
396

397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412


413
414
415
416
417
418
419
346
347
348
349
350
351
352





353
354
355
356
357
358

359
360

361
362

363
364

365
366

367
368
369
370
371

372
373
374
375
376
377
378
379
380


381
382
383

384
385
386
387

388
389

390
391



392
393
394
395

396
397
398

399
400

401
402
403
404
405

406
407
408
409
410

411
412
413
414
415





416
417
418
419
420


421
422
423
424
425
426
427
428
429







-
-
-
-
-
+
+
+
+
+

-
+

-
+

-
+

-
+

-
+




-
+

+
+
+
+
+
+

-
-
+
+

-
+



-
+

-
+

-
-
-
+
+
+

-
+


-
+

-
+




-
+




-
+




-
-
-
-
-





-
-
+
+







#define Jim_GetHashEntryVal(he) ((he)->u.val)
#define Jim_GetHashTableCollisions(ht) ((ht)->collisions)
#define Jim_GetHashTableSize(ht) ((ht)->size)
#define Jim_GetHashTableUsed(ht) ((ht)->used)


typedef struct Jim_Obj {
    char *bytes; 
    const struct Jim_ObjType *typePtr; 
    int refCount; 
    int length; 
    
    char *bytes;
    const struct Jim_ObjType *typePtr;
    int refCount;
    int length;

    union {
        

        jim_wide wideValue;
        

        int intValue;
        

        double doubleValue;
        

        void *ptr;
        

        struct {
            void *ptr1;
            void *ptr2;
        } twoPtrValue;
        

        struct {
            void *ptr;
            int int1;
            int int2;
        } ptrIntValue;

        struct {
            struct Jim_Var *varPtr;
            unsigned long callFrameId; 
            int global; 
            unsigned long callFrameId;
            int global;
        } varValue;
        

        struct {
            struct Jim_Obj *nsObj;
            struct Jim_Cmd *cmdPtr;
            unsigned long procEpoch; 
            unsigned long procEpoch;
        } cmdValue;
        

        struct {
            struct Jim_Obj **ele;    
            int len;        
            int maxLen;        
            struct Jim_Obj **ele;
            int len;
            int maxLen;
        } listValue;
        

        struct {
            int maxLength;
            int charLength;     
            int charLength;
        } strValue;
        

        struct {
            unsigned long id;
            struct Jim_Reference *refPtr;
        } refValue;
        

        struct {
            struct Jim_Obj *fileNameObj;
            int lineNumber;
        } sourceValue;
        

        struct {
            struct Jim_Obj *varNameObjPtr;
            struct Jim_Obj *indexObjPtr;
        } dictSubstValue;
        
        struct {
            void *compre;       
            unsigned flags;
        } regexpValue;
        struct {
            int line;
            int argc;
        } scriptLineValue;
    } internalRep;
    struct Jim_Obj *prevObjPtr; 
    struct Jim_Obj *nextObjPtr; 
    struct Jim_Obj *prevObjPtr;
    struct Jim_Obj *nextObjPtr;
} Jim_Obj;


#define Jim_IncrRefCount(objPtr) \
    ++(objPtr)->refCount
#define Jim_DecrRefCount(interp, objPtr) \
    if (--(objPtr)->refCount <= 0) Jim_FreeObj(interp, objPtr)
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
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







-
+







-
-
+
+




-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
+
+
+















-
-
-
+
+
+


-
-
-
-
+
+
+
+


-
+


-
-
-
-
-
-
+
+
+
+
+
+

-
-
+
+

-
+











-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+

-
+



-
-
-
-
-
-
+
+
+
+
+
+




-
-
-
-
-
+
+
+
+
+







typedef void (Jim_FreeInternalRepProc)(struct Jim_Interp *interp,
        struct Jim_Obj *objPtr);
typedef void (Jim_DupInternalRepProc)(struct Jim_Interp *interp,
        struct Jim_Obj *srcPtr, Jim_Obj *dupPtr);
typedef void (Jim_UpdateStringProc)(struct Jim_Obj *objPtr);

typedef struct Jim_ObjType {
    const char *name; 
    const char *name;
    Jim_FreeInternalRepProc *freeIntRepProc;
    Jim_DupInternalRepProc *dupIntRepProc;
    Jim_UpdateStringProc *updateStringProc;
    int flags;
} Jim_ObjType;


#define JIM_TYPE_NONE 0        
#define JIM_TYPE_REFERENCES 1    
#define JIM_TYPE_NONE 0
#define JIM_TYPE_REFERENCES 1



typedef struct Jim_CallFrame {
    unsigned long id; 
    int level; 
    struct Jim_HashTable vars; 
    struct Jim_HashTable *staticVars; 
    struct Jim_CallFrame *parent; 
    Jim_Obj *const *argv; 
    int argc; 
    Jim_Obj *procArgsObjPtr; 
    Jim_Obj *procBodyObjPtr; 
    struct Jim_CallFrame *next; 
    Jim_Obj *nsObj;             
    Jim_Obj *fileNameObj;       
    unsigned long id;
    int level;
    struct Jim_HashTable vars;
    struct Jim_HashTable *staticVars;
    struct Jim_CallFrame *parent;
    Jim_Obj *const *argv;
    int argc;
    Jim_Obj *procArgsObjPtr;
    Jim_Obj *procBodyObjPtr;
    struct Jim_CallFrame *next;
    Jim_Obj *nsObj;
    Jim_Obj *fileNameObj;
    int line;
    Jim_Stack *localCommands; 
    struct Jim_Obj *tailcallObj;  
    struct Jim_Cmd *tailcallCmd;  
    Jim_Stack *localCommands;
    struct Jim_Obj *tailcallObj;
    struct Jim_Cmd *tailcallCmd;
} Jim_CallFrame;

typedef struct Jim_Var {
    Jim_Obj *objPtr;
    struct Jim_CallFrame *linkFramePtr;
} Jim_Var;


typedef int Jim_CmdProc(struct Jim_Interp *interp, int argc,
    Jim_Obj *const *argv);
typedef void Jim_DelCmdProc(struct Jim_Interp *interp, void *privData);



typedef struct Jim_Cmd {
    int inUse;           
    int isproc;          
    struct Jim_Cmd *prevCmd;    
    int inUse;
    int isproc;
    struct Jim_Cmd *prevCmd;
    union {
        struct {
            
            Jim_CmdProc *cmdProc; 
            Jim_DelCmdProc *delProc; 
            void *privData; 

            Jim_CmdProc *cmdProc;
            Jim_DelCmdProc *delProc;
            void *privData;
        } native;
        struct {
            

            Jim_Obj *argListObjPtr;
            Jim_Obj *bodyObjPtr;
            Jim_HashTable *staticVars;  
            int argListLen;             
            int reqArity;               
            int optArity;               
            int argsPos;                
            int upcall;                 
            Jim_HashTable *staticVars;
            int argListLen;
            int reqArity;
            int optArity;
            int argsPos;
            int upcall;
            struct Jim_ProcArg {
                Jim_Obj *nameObjPtr;    
                Jim_Obj *defaultObjPtr; 
                Jim_Obj *nameObjPtr;
                Jim_Obj *defaultObjPtr;
            } *arglist;
            Jim_Obj *nsObj;             
            Jim_Obj *nsObj;
        } proc;
    } u;
} Jim_Cmd;


typedef struct Jim_PrngState {
    unsigned char sbox[256];
    unsigned int i, j;
} Jim_PrngState;

typedef struct Jim_Interp {
    Jim_Obj *result; 
    int errorLine; 
    Jim_Obj *errorFileNameObj; 
    int addStackTrace; 
    int maxCallFrameDepth; 
    int maxEvalDepth; 
    int evalDepth;  
    int returnCode; 
    int returnLevel; 
    int exitCode; 
    long id; 
    int signal_level; 
    jim_wide sigmask;  
    int (*signal_set_result)(struct Jim_Interp *interp, jim_wide sigmask); 
    Jim_CallFrame *framePtr; 
    Jim_CallFrame *topFramePtr; 
    struct Jim_HashTable commands; 
    Jim_Obj *result;
    int errorLine;
    Jim_Obj *errorFileNameObj;
    int addStackTrace;
    int maxCallFrameDepth;
    int maxEvalDepth;
    int evalDepth;
    int returnCode;
    int returnLevel;
    int exitCode;
    long id;
    int signal_level;
    jim_wide sigmask;
    int (*signal_set_result)(struct Jim_Interp *interp, jim_wide sigmask);
    Jim_CallFrame *framePtr;
    Jim_CallFrame *topFramePtr;
    struct Jim_HashTable commands;
    unsigned long procEpoch; /* Incremented every time the result
                of procedures names lookup caching
                may no longer be valid. */
    unsigned long callFrameEpoch; /* Incremented every time a new
                callframe is created. This id is used for the
                'ID' field contained in the Jim_CallFrame
                structure. */
    int local; 
    Jim_Obj *liveList; 
    Jim_Obj *freeList; 
    Jim_Obj *currentScriptObj; 
    Jim_Obj *nullScriptObj; 
    Jim_Obj *emptyObj; 
    Jim_Obj *trueObj; 
    Jim_Obj *falseObj; 
    unsigned long referenceNextId; 
    struct Jim_HashTable references; 
    int local;
    Jim_Obj *liveList;
    Jim_Obj *freeList;
    Jim_Obj *currentScriptObj;
    Jim_Obj *nullScriptObj;
    Jim_Obj *emptyObj;
    Jim_Obj *trueObj;
    Jim_Obj *falseObj;
    unsigned long referenceNextId;
    struct Jim_HashTable references;
    unsigned long lastCollectId; /* reference max Id of the last GC
                execution. It's set to -1 while the collection
                execution. It's set to ~0 while the collection
                is running as sentinel to avoid to recursive
                calls via the [collect] command inside
                finalizers. */
    time_t lastCollectTime; 
    Jim_Obj *stackTrace; 
    Jim_Obj *errorProc; 
    Jim_Obj *unknown; 
    int unknown_called; 
    int errorFlag; 
    time_t lastCollectTime;
    Jim_Obj *stackTrace;
    Jim_Obj *errorProc;
    Jim_Obj *unknown;
    int unknown_called;
    int errorFlag;
    void *cmdPrivData; /* Used to pass the private data pointer to
                  a command. It is set to what the user specified
                  via Jim_CreateCommand(). */

    struct Jim_CallFrame *freeFramesList; 
    struct Jim_HashTable assocData; 
    Jim_PrngState *prngState; 
    struct Jim_HashTable packages; 
    Jim_Stack *loadHandles; 
    struct Jim_CallFrame *freeFramesList;
    struct Jim_HashTable assocData;
    Jim_PrngState *prngState;
    struct Jim_HashTable packages;
    Jim_Stack *loadHandles;
} Jim_Interp;

#define Jim_InterpIncrProcEpoch(i) (i)->procEpoch++
#define Jim_SetResultString(i,s,l) Jim_SetResult(i, Jim_NewStringObj(i,s,l))
#define Jim_SetResultInt(i,intval) Jim_SetResult(i, Jim_NewIntObj(i,intval))

#define Jim_SetResultBool(i,b) Jim_SetResultInt(i, b)
621
622
623
624
625
626
627
628

629
630
631
632
633
634
635
631
632
633
634
635
636
637

638
639
640
641
642
643
644
645







-
+







JIM_EXPORT void Jim_Free (void *ptr);
JIM_EXPORT char * Jim_StrDup (const char *s);
JIM_EXPORT char *Jim_StrDupLen(const char *s, int l);


JIM_EXPORT char **Jim_GetEnviron(void);
JIM_EXPORT void Jim_SetEnviron(char **env);
JIM_EXPORT int Jim_MakeTempFile(Jim_Interp *interp, const char *template);
JIM_EXPORT int Jim_MakeTempFile(Jim_Interp *interp, const char *filename_template, int unlink_file);


JIM_EXPORT int Jim_Eval(Jim_Interp *interp, const char *script);


JIM_EXPORT int Jim_EvalSource(Jim_Interp *interp, const char *filename, int lineno, const char *script);

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
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







-
-
+
+
+
+
+


+







-
+



+
+
+
+







JIM_EXPORT int Jim_SetDictKeysVector (Jim_Interp *interp,
        Jim_Obj *varNamePtr, Jim_Obj *const *keyv, int keyc,
        Jim_Obj *newObjPtr, int flags);
JIM_EXPORT int Jim_DictPairs(Jim_Interp *interp,
        Jim_Obj *dictPtr, Jim_Obj ***objPtrPtr, int *len);
JIM_EXPORT int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
        Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr);
JIM_EXPORT int Jim_DictKeys(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *patternObj);
JIM_EXPORT int Jim_DictValues(Jim_Interp *interp, Jim_Obj *dictObjPtr, Jim_Obj *patternObjPtr);

#define JIM_DICTMATCH_KEYS 0x0001
#define JIM_DICTMATCH_VALUES 0x002

JIM_EXPORT int Jim_DictMatchTypes(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *patternObj, int match_type, int return_types);
JIM_EXPORT int Jim_DictSize(Jim_Interp *interp, Jim_Obj *objPtr);
JIM_EXPORT int Jim_DictInfo(Jim_Interp *interp, Jim_Obj *objPtr);
JIM_EXPORT Jim_Obj *Jim_DictMerge(Jim_Interp *interp, int objc, Jim_Obj *const *objv);


JIM_EXPORT int Jim_GetReturnCode (Jim_Interp *interp, Jim_Obj *objPtr,
        int *intPtr);


JIM_EXPORT int Jim_EvalExpression (Jim_Interp *interp,
        Jim_Obj *exprObjPtr, Jim_Obj **exprResultPtrPtr);
        Jim_Obj *exprObjPtr);
JIM_EXPORT int Jim_GetBoolFromExpr (Jim_Interp *interp,
        Jim_Obj *exprObjPtr, int *boolPtr);


JIM_EXPORT int Jim_GetBoolean(Jim_Interp *interp, Jim_Obj *objPtr,
        int *booleanPtr);


JIM_EXPORT int Jim_GetWide (Jim_Interp *interp, Jim_Obj *objPtr,
        jim_wide *widePtr);
JIM_EXPORT int Jim_GetLong (Jim_Interp *interp, Jim_Obj *objPtr,
        long *longPtr);
#define Jim_NewWideObj  Jim_NewIntObj
JIM_EXPORT Jim_Obj * Jim_NewIntObj (Jim_Interp *interp,
850
851
852
853
854
855
856


857
858
859
860
861
862
863
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883







+
+







JIM_EXPORT Jim_Obj * Jim_NewDoubleObj(Jim_Interp *interp, double doubleValue);


JIM_EXPORT void Jim_WrongNumArgs (Jim_Interp *interp, int argc,
        Jim_Obj *const *argv, const char *msg);
JIM_EXPORT int Jim_GetEnum (Jim_Interp *interp, Jim_Obj *objPtr,
        const char * const *tablePtr, int *indexPtr, const char *name, int flags);
JIM_EXPORT int Jim_CheckShowCommands(Jim_Interp *interp, Jim_Obj *objPtr,
        const char *const *tablePtr);
JIM_EXPORT int Jim_ScriptIsComplete(Jim_Interp *interp,
        Jim_Obj *scriptObj, char *stateCharPtr);

JIM_EXPORT int Jim_FindByName(const char *name, const char * const array[], size_t len);


typedef void (Jim_InterpDeleteProc)(Jim_Interp *interp, void *data);
876
877
878
879
880
881
882
883


884
885
886
887
888
889
890
896
897
898
899
900
901
902

903
904
905
906
907
908
909
910
911







-
+
+








JIM_EXPORT void Jim_MakeErrorMessage (Jim_Interp *interp);


JIM_EXPORT int Jim_InteractivePrompt (Jim_Interp *interp);
JIM_EXPORT void Jim_HistoryLoad(const char *filename);
JIM_EXPORT void Jim_HistorySave(const char *filename);
JIM_EXPORT char *Jim_HistoryGetline(const char *prompt);
JIM_EXPORT char *Jim_HistoryGetline(Jim_Interp *interp, const char *prompt);
JIM_EXPORT void Jim_HistorySetCompletion(Jim_Interp *interp, Jim_Obj *commandObj);
JIM_EXPORT void Jim_HistoryAdd(const char *line);
JIM_EXPORT void Jim_HistoryShow(void);


JIM_EXPORT int Jim_InitStaticExtensions(Jim_Interp *interp);
JIM_EXPORT int Jim_StringToWide(const char *str, jim_wide *widePtr, int base);
JIM_EXPORT int Jim_IsBigEndian(void);
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
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







-
+










-
-
+
+






-
-
-
-
-
-
+
+
+
+
+
+







JIM_EXPORT int Jim_IsDict(Jim_Obj *objPtr);
JIM_EXPORT int Jim_IsList(Jim_Obj *objPtr);

#ifdef __cplusplus
}
#endif

#endif 
#endif

#ifndef JIM_SUBCMD_H
#define JIM_SUBCMD_H


#ifdef __cplusplus
extern "C" {
#endif


#define JIM_MODFLAG_HIDDEN   0x0001		
#define JIM_MODFLAG_FULLARGV 0x0002		
#define JIM_MODFLAG_HIDDEN   0x0001
#define JIM_MODFLAG_FULLARGV 0x0002



typedef int jim_subcmd_function(Jim_Interp *interp, int argc, Jim_Obj *const *argv);

typedef struct {
	const char *cmd;				
	const char *args;				
	jim_subcmd_function *function;	
	short minargs;					
	short maxargs;					
	unsigned short flags;			
	const char *cmd;
	const char *args;
	jim_subcmd_function *function;
	short minargs;
	short maxargs;
	unsigned short flags;
} jim_subcmd_type;

const jim_subcmd_type *
Jim_ParseSubCmd(Jim_Interp *interp, const jim_subcmd_type *command_table, int argc, Jim_Obj *const *argv);

int Jim_SubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv);

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
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







-
-
+
+

-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+

-
-
-
-
+
+
+
+

-
-
-
-
-
+
+
+
+
+

-
-
-
+
+
+











-
-
-
+
+
+







typedef struct {
	int rm_so;
	int rm_eo;
} regmatch_t;


typedef struct regexp {
	
	int re_nsub;		

	int re_nsub;

	
	int cflags;			
	int err;			
	int regstart;		
	int reganch;		
	int regmust;		
	int regmlen;		
	int *program;		

	int cflags;
	int err;
	int regstart;
	int reganch;
	int regmust;
	int regmlen;
	int *program;

	
	const char *regparse;		
	int p;				
	int proglen;		

	const char *regparse;
	int p;
	int proglen;

	
	int eflags;				
	const char *start;		
	const char *reginput;	
	const char *regbol;		

	int eflags;
	const char *start;
	const char *reginput;
	const char *regbol;

	
	regmatch_t *pmatch;		
	int nmatch;				

	regmatch_t *pmatch;
	int nmatch;
} regexp;

typedef regexp regex_t;

#define REG_EXTENDED 0
#define REG_NEWLINE 1
#define REG_ICASE 2

#define REG_NOTBOL 16

enum {
	REG_NOERROR,      
	REG_NOMATCH,      
	REG_BADPAT,		  
	REG_NOERROR,
	REG_NOMATCH,
	REG_BADPAT,
	REG_ERR_NULL_ARGUMENT,
	REG_ERR_UNKNOWN,
	REG_ERR_TOO_BIG,
	REG_ERR_NOMEM,
	REG_ERR_TOO_MANY_PAREN,
	REG_ERR_UNMATCHED_PAREN,
	REG_ERR_UNMATCHED_BRACES,
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
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








+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+








-
+


+
+
+
+
-
-
+
+







size_t regerror(int errcode, const regex_t *preg, char *errbuf,  size_t errbuf_size);
void regfree(regex_t *preg);

#ifdef __cplusplus
}
#endif

#endif
#ifndef JIM_SIGNAL_H
#define JIM_SIGNAL_H

#ifdef __cplusplus
extern "C" {
#endif

const char *Jim_SignalId(int sig);

#ifdef __cplusplus
}
#endif

#endif
#ifndef JIMIOCOMPAT_H
#define JIMIOCOMPAT_H


#include <stdio.h>
#include <errno.h>


void Jim_SetResultErrno(Jim_Interp *interp, const char *msg);

int Jim_OpenForWrite(const char *filename, int append);

int Jim_OpenForRead(const char *filename);

#if defined(__MINGW32__)
    #ifndef STRICT
    #define STRICT
    #endif
    #define WIN32_LEAN_AND_MEAN
    #include <windows.h>
    #include <fcntl.h>
    #include <io.h>
    #include <process.h>

    typedef HANDLE pidtype;
    #define JIM_BAD_PID INVALID_HANDLE_VALUE

    #define JIM_NO_PID INVALID_HANDLE_VALUE


    #define WIFEXITED(STATUS) (((STATUS) & 0xff00) == 0)
    #define WEXITSTATUS(STATUS) ((STATUS) & 0x00ff)
    #define WIFSIGNALED(STATUS) (((STATUS) & 0xff00) != 0)
    #define WTERMSIG(STATUS) (((STATUS) >> 8) & 0xff)
    #define WNOHANG 1

    int Jim_Errno(void);
    pidtype waitpid(pidtype pid, int *status, int nohang);

    #define HAVE_PIPE
    #define pipe(P) _pipe((P), 0, O_NOINHERIT)

#elif defined(HAVE_UNISTD_H)
    #include <unistd.h>
    #include <fcntl.h>
    #include <sys/wait.h>
    #include <sys/stat.h>

    typedef int pidtype;
    #define Jim_Errno() errno
    #define JIM_BAD_PID -1
    #define JIM_NO_PID 0

    #ifndef HAVE_EXECVPE
        #define execvpe(ARG0, ARGV, ENV) execvp(ARG0, ARGV)
    #endif
#endif

#endif
int Jim_bootstrapInit(Jim_Interp *interp)
{
	if (Jim_PackageProvide(interp, "bootstrap", "1.0", JIM_ERRMSG))
		return JIM_ERR;

	return Jim_EvalSource(interp, "bootstrap.tcl", 1,
"\n"
"\n"
"proc package {cmd pkg} {\n"
"proc package {cmd pkg args} {\n"
"	if {$cmd eq \"require\"} {\n"
"		foreach path $::auto_path {\n"
"			set pkgpath $path/$pkg.tcl\n"
"			if {$path eq \".\"} {\n"
"				set pkgpath $pkg.tcl\n"
"			}\n"
"			if {[file exists $path/$pkg.tcl]} {\n"
"				uplevel #0 [list source $path/$pkg.tcl]\n"
"			if {[file exists $pkgpath]} {\n"
"				uplevel #0 [list source $pkgpath]\n"
"				return\n"
"			}\n"
"		}\n"
"	}\n"
"}\n"
);
}
1098
1099
1100
1101
1102
1103
1104

































1105
1106
1107
1108
1109
1110
1111
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







"	return \"\"\n"
"}\n"
"\n"
"if {$tcl_platform(platform) eq \"windows\"} {\n"
"	set jim::argv0 [string map {\\\\ /} $jim::argv0]\n"
"}\n"
"\n"
"\n"
"set tcl::autocomplete_commands {info tcl::prefix socket namespace array clock file package string dict signal history}\n"
"\n"
"\n"
"\n"
"proc tcl::autocomplete {prefix} {\n"
"	if {[set space [string first \" \" $prefix]] != -1} {\n"
"		set cmd [string range $prefix 0 $space-1]\n"
"		if {$cmd in $::tcl::autocomplete_commands || [info channel $cmd] ne \"\"} {\n"
"			set arg [string range $prefix $space+1 end]\n"
"\n"
"			return [lmap p [$cmd -commands] {\n"
"				if {![string match \"${arg}*\" $p]} continue\n"
"				function \"$cmd $p\"\n"
"			}]\n"
"		}\n"
"	}\n"
"\n"
"	if {[string match \"source *\" $prefix]} {\n"
"		set path [string range $prefix 7 end]\n"
"		return [lmap p [glob -nocomplain \"${path}*\"] {\n"
"			function \"source $p\"\n"
"		}]\n"
"	}\n"
"\n"
"	return [lmap p [lsort [info commands $prefix*]] {\n"
"		if {[string match \"* *\" $p]} {\n"
"			continue\n"
"		}\n"
"		function $p\n"
"	}]\n"
"}\n"
"\n"
"_jimsh_init\n"
);
}
int Jim_globInit(Jim_Interp *interp)
{
	if (Jim_PackageProvide(interp, "glob", "1.0", JIM_ERRMSG))
		return JIM_ERR;
1313
1314
1315
1316
1317
1318
1319







1320
1321
1322
1323
1324
1325
1326
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464







+
+
+
+
+
+
+







{
	if (Jim_PackageProvide(interp, "stdlib", "1.0", JIM_ERRMSG))
		return JIM_ERR;

	return Jim_EvalSource(interp, "stdlib.tcl", 1,
"\n"
"\n"
"if {![exists -command ref]} {\n"
"\n"
"	proc ref {args} {{count 0}} {\n"
"		format %08x [incr count]\n"
"	}\n"
"}\n"
"\n"
"\n"
"proc lambda {arglist args} {\n"
"	tailcall proc [ref {} function lambda.finalizer] $arglist {*}$args\n"
"}\n"
"\n"
"proc lambda.finalizer {name val} {\n"
"	rename $name {}\n"
1373
1374
1375
1376
1377
1378
1379







1380
1381
1382
1383
1384
1385
1386
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531







+
+
+
+
+
+
+







"			lappend lines $line\n"
"		}\n"
"	}\n"
"	join $lines \\n\n"
"}\n"
"\n"
"\n"
"\n"
"proc defer {script} {\n"
"	upvar jim::defer v\n"
"	lappend v $script\n"
"}\n"
"\n"
"\n"
"\n"
"proc errorInfo {msg {stacktrace \"\"}} {\n"
"	if {$stacktrace eq \"\"} {\n"
"\n"
"		set stacktrace [info stacktrace]\n"
"\n"
"		lappend stacktrace {*}[stacktrace 1]\n"
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
1545
1546
1547
1548
1549
1550
1551





















1552
1553
1554
1555
1556
1557
1558







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-







"\n"
"proc {info nameofexecutable} {} {\n"
"	if {[exists ::jim::exe]} {\n"
"		return $::jim::exe\n"
"	}\n"
"}\n"
"\n"
"\n"
"proc {dict with} {&dictVar {args key} script} {\n"
"	set keys {}\n"
"	foreach {n v} [dict get $dictVar {*}$key] {\n"
"		upvar $n var_$n\n"
"		set var_$n $v\n"
"		lappend keys $n\n"
"	}\n"
"	catch {uplevel 1 $script} msg opts\n"
"	if {[info exists dictVar] && ([llength $key] == 0 || [dict exists $dictVar {*}$key])} {\n"
"		foreach n $keys {\n"
"			if {[info exists var_$n]} {\n"
"				dict set dictVar {*}$key $n [set var_$n]\n"
"			} else {\n"
"				dict unset dictVar {*}$key $n\n"
"			}\n"
"		}\n"
"	}\n"
"	return {*}$opts $msg\n"
"}\n"
"\n"
"\n"
"proc {dict update} {&varName args script} {\n"
"	set keys {}\n"
"	foreach {n v} $args {\n"
"		upvar $v var_$v\n"
"		if {[dict exists $varName $n]} {\n"
"			set var_$v [dict get $varName $n]\n"
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
1567
1568
1569
1570
1571
1572
1573













1574
1575
1576
1577
1578
1579
1580







-
-
-
-
-
-
-
-
-
-
-
-
-







"				dict unset varName $n\n"
"			}\n"
"		}\n"
"	}\n"
"	return {*}$opts $msg\n"
"}\n"
"\n"
"\n"
"\n"
"proc {dict merge} {dict args} {\n"
"	foreach d $args {\n"
"\n"
"		dict size $d\n"
"		foreach {k v} $d {\n"
"			dict set dict $k $v\n"
"		}\n"
"	}\n"
"	return $dict\n"
"}\n"
"\n"
"proc {dict replace} {dictionary {args {key value}}} {\n"
"	if {[llength ${key value}] % 2} {\n"
"		tailcall {dict replace}\n"
"	}\n"
"	tailcall dict merge $dictionary ${key value}\n"
"}\n"
"\n"
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1612
1613
1614
1615
1616
1617
1618





1619
1620
1621
1622
1623
1624
1625







-
-
-
-
-







"proc {dict remove} {dictionary {args key}} {\n"
"	foreach k $key {\n"
"		dict unset dictionary $k\n"
"	}\n"
"	return $dictionary\n"
"}\n"
"\n"
"\n"
"proc {dict values} {dictionary {pattern *}} {\n"
"	dict keys [lreverse $dictionary] $pattern\n"
"}\n"
"\n"
"\n"
"proc {dict for} {vars dictionary script} {\n"
"	if {[llength $vars] != 2} {\n"
"		return -code error \"must have exactly two variable names\"\n"
"	}\n"
"	dict size $dictionary\n"
"	tailcall foreach $vars $dictionary $script\n"
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1695
1696
1697
1698
1699
1700
1701

1702
1703
1704
1705
1706
1707
1708







-







"\n"
"proc fileevent {args} {\n"
"	tailcall {*}$args\n"
"}\n"
"\n"
"\n"
"\n"
"\n"
"proc parray {arrayname {pattern *} {puts puts}} {\n"
"	upvar $arrayname a\n"
"\n"
"	set max 0\n"
"	foreach name [array names a $pattern]] {\n"
"		if {[string length $name] > $max} {\n"
"			set max [string length $name]\n"
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
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







-
+















+
+
+




+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+







"		catch {$in close}\n"
"	}\n"
"}\n"
"\n"
"\n"
"\n"
"proc popen {cmd {mode r}} {\n"
"	lassign [socket pipe] r w\n"
"	lassign [pipe] r w\n"
"	try {\n"
"		if {[string match \"w*\" $mode]} {\n"
"			lappend cmd <@$r &\n"
"			set pids [exec {*}$cmd]\n"
"			$r close\n"
"			set f $w\n"
"		} else {\n"
"			lappend cmd >@$w &\n"
"			set pids [exec {*}$cmd]\n"
"			$w close\n"
"			set f $r\n"
"		}\n"
"		lambda {cmd args} {f pids} {\n"
"			if {$cmd eq \"pid\"} {\n"
"				return $pids\n"
"			}\n"
"			if {$cmd eq \"getfd\"} {\n"
"				$f getfd\n"
"			}\n"
"			if {$cmd eq \"close\"} {\n"
"				$f close\n"
"\n"
"				set retopts {}\n"
"				foreach p $pids { os.wait $p }\n"
"				return\n"
"				foreach p $pids {\n"
"					lassign [wait $p] status - rc\n"
"					if {$status eq \"CHILDSTATUS\"} {\n"
"						if {$rc == 0} {\n"
"							continue\n"
"						}\n"
"						set msg \"child process exited abnormally\"\n"
"					} else {\n"
"						set msg \"child killed: received signal\"\n"
"					}\n"
"					set retopts [list -code error -errorcode [list $status $p $rc] $msg]\n"
"				}\n"
"				return {*}$retopts\n"
"			}\n"
"			tailcall $f $cmd {*}$args\n"
"		}\n"
"	} on error {error opts} {\n"
"		$r close\n"
"		$w close\n"
"		error $error\n"
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1810
1811
1812
1813
1814
1815
1816




1817
1818
1819
1820
1821
1822
1823







-
-
-
-







"		return -code error \"can not find channel named \\\"$channelId\\\"\"\n"
"	}\n"
"	if {[catch {$channelId pid} pids]} {\n"
"		return \"\"\n"
"	}\n"
"	return $pids\n"
"}\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"\n"
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
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







+
+
+













+





+
+
+









+
+

+
-
-
+
+



















+
-
+
+
+
+


















-
+







"	}\n"
"	file delete $path\n"
"}\n"
);
}


#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#include <sys/stat.h>
#endif


#if defined(HAVE_SYS_SOCKET_H) && defined(HAVE_SELECT) && defined(HAVE_NETINET_IN_H) && defined(HAVE_NETDB_H) && defined(HAVE_ARPA_INET_H)
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netdb.h>
#ifdef HAVE_SYS_UN_H
#include <sys/un.h>
#endif
#define HAVE_SOCKETS
#elif defined (__MINGW32__)

#else
#define JIM_ANSIC
#endif

#if defined(JIM_SSL)
#include <openssl/ssl.h>
#include <openssl/err.h>
#endif

#ifdef HAVE_TERMIOS_H
#endif


#define AIO_CMD_LEN 32      
#define AIO_BUF_LEN 256     
#define AIO_CMD_LEN 32
#define AIO_BUF_LEN 256

#ifndef HAVE_FTELLO
    #define ftello ftell
#endif
#ifndef HAVE_FSEEKO
    #define fseeko fseek
#endif

#define AIO_KEEPOPEN 1

#if defined(JIM_IPV6)
#define IPV6 1
#else
#define IPV6 0
#ifndef PF_INET6
#define PF_INET6 0
#endif
#endif

#ifdef JIM_ANSIC
#define JimCheckStreamError(interp, af) af->fops->error(af)

#undef HAVE_PIPE
#undef HAVE_SOCKETPAIR
#endif


struct AioFile;

typedef struct {
    int (*writer)(struct AioFile *af, const char *buf, int len);
    int (*reader)(struct AioFile *af, char *buf, int len);
    const char *(*getline)(struct AioFile *af, char *buf, int len);
    int (*error)(const struct AioFile *af);
    const char *(*strerror)(struct AioFile *af);
    int (*verify)(struct AioFile *af);
} JimAioFopsType;

typedef struct AioFile
{
    FILE *fp;
    Jim_Obj *filename;
    int type;
    int openFlags;              
    int openFlags;
    int fd;
    Jim_Obj *rEvent;
    Jim_Obj *wEvent;
    Jim_Obj *eEvent;
    int addr_family;
    void *ssl;
    const JimAioFopsType *fops;
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
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







-
+









-
+








static int stdio_error(const AioFile *af)
{
    if (!ferror(af->fp)) {
        return JIM_OK;
    }
    clearerr(af->fp);
    

    if (feof(af->fp) || errno == EAGAIN || errno == EINTR) {
        return JIM_OK;
    }
#ifdef ECONNRESET
    if (errno == ECONNRESET) {
        return JIM_OK;
    }
#endif
#ifdef ECONNABORTED
    if (errno != ECONNABORTED) {
    if (errno == ECONNABORTED) {
        return JIM_OK;
    }
#endif
    return JIM_ERR;
}

static const char *stdio_strerror(struct AioFile *af)
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
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







+
+
+
+
+
+
+
+
+










-
-
+
+







-













-
+







    if (name) {
        Jim_SetResultFormatted(interp, "%#s: %s", name, JimAioErrorString(af));
    }
    else {
        Jim_SetResultString(interp, JimAioErrorString(af), -1);
    }
}

static int JimCheckStreamError(Jim_Interp *interp, AioFile *af)
{
	int ret = af->fops->error(af);
	if (ret) {
		JimAioSetError(interp, af->filename);
	}
	return ret;
}

static void JimAioDelProc(Jim_Interp *interp, void *privData)
{
    AioFile *af = privData;

    JIM_NOTUSED(interp);

    Jim_DecrRefCount(interp, af->filename);

#ifdef jim_ext_eventloop
    
    Jim_DeleteFileHandler(interp, af->fp, JIM_EVENT_READABLE | JIM_EVENT_WRITABLE | JIM_EVENT_EXCEPTION);

    Jim_DeleteFileHandler(interp, af->fd, JIM_EVENT_READABLE | JIM_EVENT_WRITABLE | JIM_EVENT_EXCEPTION);
#endif

#if defined(JIM_SSL)
    if (af->ssl != NULL) {
        SSL_free(af->ssl);
    }
#endif

    if (!(af->openFlags & AIO_KEEPOPEN)) {
        fclose(af->fp);
    }

    Jim_Free(af);
}

static int aio_cmd_read(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
    AioFile *af = Jim_CmdPrivData(interp);
    char buf[AIO_BUF_LEN];
    Jim_Obj *objPtr;
    int nonewline = 0;
    jim_wide neededLen = -1;         
    jim_wide neededLen = -1;

    if (argc && Jim_CompareStringImmediate(interp, argv[0], "-nonewline")) {
        nonewline = 1;
        argv++;
        argc--;
    }
    if (argc == 1) {
2005
2006
2007
2008
2009
2010
2011
2012

2013
2014
2015
2016
2017
2018
2019
2143
2144
2145
2146
2147
2148
2149

2150
2151
2152
2153
2154
2155
2156
2157







-
+







            if (neededLen != -1) {
                neededLen -= retval;
            }
        }
        if (retval != readlen)
            break;
    }
    

    if (JimCheckStreamError(interp, af)) {
        Jim_FreeNewObj(interp, objPtr);
        return JIM_ERR;
    }
    if (nonewline) {
        int len;
        const char *s = Jim_GetString(objPtr, &len);
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
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







-
+


















+
+
+
+
+
+
+
+
+
+







    return JIM_OK;
}

AioFile *Jim_AioFile(Jim_Interp *interp, Jim_Obj *command)
{
    Jim_Cmd *cmdPtr = Jim_GetCommand(interp, command, JIM_ERRMSG);

    

    if (cmdPtr && !cmdPtr->isproc && cmdPtr->u.native.cmdProc == JimAioSubCmdProc) {
        return (AioFile *) cmdPtr->u.native.privData;
    }
    Jim_SetResultFormatted(interp, "Not a filehandle: \"%#s\"", command);
    return NULL;
}

FILE *Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command)
{
    AioFile *af;

    af = Jim_AioFile(interp, command);
    if (af == NULL) {
        return NULL;
    }

    return af->fp;
}

static int aio_cmd_getfd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
    AioFile *af = Jim_CmdPrivData(interp);

    fflush(af->fp);
    Jim_SetResultInt(interp, fileno(af->fp));

    return JIM_OK;
}

static int aio_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
    AioFile *af = Jim_CmdPrivData(interp);
    jim_wide count = 0;
    jim_wide maxlen = JIM_WIDE_MAX;
    AioFile *outf = Jim_AioFile(interp, argv[0]);
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
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







-
+









-
+













-
+







        if (buf[AIO_BUF_LEN - 1] == '\0' && buf[AIO_BUF_LEN - 2] != '\n') {
            Jim_AppendString(interp, objPtr, buf, AIO_BUF_LEN - 1);
        }
        else {
            len = strlen(buf);

            if (len && (buf[len - 1] == '\n')) {
                

                len--;
            }

            Jim_AppendString(interp, objPtr, buf, len);
            break;
        }
    }

    if (JimCheckStreamError(interp, af)) {
        

        Jim_FreeNewObj(interp, objPtr);
        return JIM_ERR;
    }

    if (argc) {
        if (Jim_SetVariable(interp, argv[0], objPtr) != JIM_OK) {
            Jim_FreeNewObj(interp, objPtr);
            return JIM_ERR;
        }

        len = Jim_Length(objPtr);

        if (len == 0 && feof(af->fp)) {
            

            len = -1;
        }
        Jim_SetResultInt(interp, len);
    }
    else {
        Jim_SetResult(interp, objPtr);
    }
2205
2206
2207
2208
2209
2210
2211
2212

2213
2214
2215
2216
2217
2218
2219
2353
2354
2355
2356
2357
2358
2359

2360
2361
2362
2363
2364
2365
2366
2367







-
+







    Jim_SetResultInt(interp, feof(af->fp));
    return JIM_OK;
}

static int aio_cmd_close(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
    if (argc == 3) {
#if !defined(JIM_ANSIC) && defined(HAVE_SHUTDOWN)
#if defined(HAVE_SOCKETS) && defined(HAVE_SHUTDOWN)
        static const char * const options[] = { "r", "w", NULL };
        enum { OPT_R, OPT_W, };
        int option;
        AioFile *af = Jim_CmdPrivData(interp);

        if (Jim_GetEnum(interp, argv[2], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
            return JIM_ERR;
2295
2296
2297
2298
2299
2300
2301

2302
2303
2304
2305
2306
2307
2308
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457







+







        }
        (void)fcntl(af->fd, F_SETFL, fmode);
    }
    Jim_SetResultInt(interp, (fmode & O_NONBLOCK) ? 1 : 0);
    return JIM_OK;
}
#endif


#ifdef HAVE_FSYNC
static int aio_cmd_sync(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
    AioFile *af = Jim_CmdPrivData(interp);

    fflush(af->fp);
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
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







-
+







-
-
+
+


-
+

-
+



-
+



-
+







    return Jim_EvalObjBackground(interp, *objPtrPtr);
}

static int aio_eventinfo(Jim_Interp *interp, AioFile * af, unsigned mask, Jim_Obj **scriptHandlerObj,
    int argc, Jim_Obj * const *argv)
{
    if (argc == 0) {
        

        if (*scriptHandlerObj) {
            Jim_SetResult(interp, *scriptHandlerObj);
        }
        return JIM_OK;
    }

    if (*scriptHandlerObj) {
        
        Jim_DeleteFileHandler(interp, af->fp, mask);

        Jim_DeleteFileHandler(interp, af->fd, mask);
    }

    

    if (Jim_Length(argv[0]) == 0) {
        

        return JIM_OK;
    }

    

    Jim_IncrRefCount(argv[0]);
    *scriptHandlerObj = argv[0];

    Jim_CreateFileHandler(interp, af->fp, mask,
    Jim_CreateFileHandler(interp, af->fd, mask,
        JimAioFileEventHandler, scriptHandlerObj, JimAioFileEventFinalizer);

    return JIM_OK;
}

static int aio_cmd_readable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
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
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







+
+







-
+






-
+
+
+
+
+
+
+
+






-
+






-
+






-
+






-
+






-
+







-
+






-
+






-
+






-
+







-
+








-
+







-
+







-
+






-
+






-
+







{
    AioFile *af = Jim_CmdPrivData(interp);

    return aio_eventinfo(interp, af, JIM_EVENT_EXCEPTION, &af->eEvent, argc, argv);
}
#endif




static const jim_subcmd_type aio_command_table[] = {
    {   "read",
        "?-nonewline? ?len?",
        aio_cmd_read,
        0,
        2,
        

    },
    {   "copyto",
        "handle ?size?",
        aio_cmd_copy,
        1,
        2,
        

    },
    {   "getfd",
        NULL,
        aio_cmd_getfd,
        0,
        0,

    },
    {   "gets",
        "?var?",
        aio_cmd_gets,
        0,
        1,
        

    },
    {   "puts",
        "?-nonewline? str",
        aio_cmd_puts,
        1,
        2,
        

    },
    {   "isatty",
        NULL,
        aio_cmd_isatty,
        0,
        0,
        

    },
    {   "flush",
        NULL,
        aio_cmd_flush,
        0,
        0,
        

    },
    {   "eof",
        NULL,
        aio_cmd_eof,
        0,
        0,
        

    },
    {   "close",
        "?r(ead)|w(rite)?",
        aio_cmd_close,
        0,
        1,
        JIM_MODFLAG_FULLARGV,
        

    },
    {   "seek",
        "offset ?start|current|end",
        aio_cmd_seek,
        1,
        2,
        

    },
    {   "tell",
        NULL,
        aio_cmd_tell,
        0,
        0,
        

    },
    {   "filename",
        NULL,
        aio_cmd_filename,
        0,
        0,
        

    },
#ifdef O_NDELAY
    {   "ndelay",
        "?0|1?",
        aio_cmd_ndelay,
        0,
        1,
        

    },
#endif
#ifdef HAVE_FSYNC
    {   "sync",
        NULL,
        aio_cmd_sync,
        0,
        0,
        

    },
#endif
    {   "buffering",
        "none|line|full",
        aio_cmd_buffering,
        1,
        1,
        

    },
#ifdef jim_ext_eventloop
    {   "readable",
        "?readable-script?",
        aio_cmd_readable,
        0,
        1,
        

    },
    {   "writable",
        "?writable-script?",
        aio_cmd_writable,
        0,
        1,
        

    },
    {   "onexception",
        "?exception-script?",
        aio_cmd_onexception,
        0,
        1,
        

    },
#endif
    { NULL }
};

static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
2564
2565
2566
2567
2568
2569
2570
2571

2572
2573
2574
2575
2576
2577
2578
2722
2723
2724
2725
2726
2727
2728

2729
2730
2731
2732
2733
2734
2735
2736







-
+








    mode = (argc == 3) ? Jim_String(argv[2]) : "r";

#ifdef jim_ext_tclcompat
    {
        const char *filename = Jim_String(argv[1]);

        

        if (*filename == '|') {
            Jim_Obj *evalObj[3];

            evalObj[0] = Jim_NewStringObj(interp, "::popen", -1);
            evalObj[1] = Jim_NewStringObj(interp, filename + 1, -1);
            evalObj[2] = Jim_NewStringObj(interp, mode, -1);

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
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







-

+

+


-




-
+









-
+



-

+
+
+





-
+


















-







-
+







-
-
+
+

-
-
+
-
-
-
+
-
-
-
-
-
+
-
-
+
-
-
-
-
-
-
-
-
+
-
-
-
-
+
-
-
-
-
+
+
-
-
-
-
+
+
-
-
+


-
-
+
-
-
-
+

-
+












-
+


+
+
+

-
+







    if (!filename) {
        filename = Jim_NewStringObj(interp, buf, -1);
    }

    Jim_IncrRefCount(filename);

    if (fh == NULL) {
#if !defined(JIM_ANSIC)
        if (fd >= 0) {
#ifndef JIM_ANSIC
            fh = fdopen(fd, mode);
#endif
        }
        else
#endif
            fh = fopen(Jim_String(filename), mode);

        if (fh == NULL) {
            JimAioSetError(interp, filename);
#if !defined(JIM_ANSIC)
#ifndef JIM_ANSIC
            if (fd >= 0) {
                close(fd);
            }
#endif
            Jim_DecrRefCount(interp, filename);
            return NULL;
        }
    }

    

    af = Jim_Alloc(sizeof(*af));
    memset(af, 0, sizeof(*af));
    af->fp = fh;
    af->fd = fileno(fh);
    af->filename = filename;
    af->openFlags = openFlags;
#ifndef JIM_ANSIC
    af->fd = fileno(fh);
#ifdef FD_CLOEXEC
    if ((openFlags & AIO_KEEPOPEN) == 0) {
        (void)fcntl(af->fd, F_SETFD, FD_CLOEXEC);
    }
#endif
    af->openFlags = openFlags;
#endif
    af->addr_family = family;
    af->fops = &stdio_fops;
    af->ssl = NULL;

    Jim_CreateCommand(interp, buf, JimAioSubCmdProc, af, JimAioDelProc);

    Jim_SetResult(interp, Jim_MakeGlobalNamespaceName(interp, Jim_NewStringObj(interp, buf, -1)));

    return af;
}

#if defined(HAVE_PIPE) || (defined(HAVE_SOCKETPAIR) && defined(HAVE_SYS_UN_H))
static int JimMakeChannelPair(Jim_Interp *interp, int p[2], Jim_Obj *filename,
    const char *hdlfmt, int family, const char *mode[2])
{
    if (JimMakeChannel(interp, NULL, p[0], filename, hdlfmt, family, mode[0])) {
        Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
        Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));

        if (JimMakeChannel(interp, NULL, p[1], filename, hdlfmt, family, mode[1])) {
            Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
            Jim_SetResult(interp, objPtr);
            return JIM_OK;
        }
    }

    

    close(p[0]);
    close(p[1]);
    JimAioSetError(interp, NULL);
    return JIM_ERR;
}
#endif


int Jim_MakeTempFile(Jim_Interp *interp, const char *template)
#ifdef HAVE_PIPE
static int JimAioPipeCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
#ifdef HAVE_MKSTEMP
    int fd;
    int p[2];
    mode_t mask;
    Jim_Obj *filenameObj;

    static const char *mode[2] = { "r", "w" };
    if (template == NULL) {
        const char *tmpdir = getenv("TMPDIR");
        if (tmpdir == NULL || *tmpdir == '\0' || access(tmpdir, W_OK) != 0) {
            tmpdir = "/tmp/";
        }

        filenameObj = Jim_NewStringObj(interp, tmpdir, -1);
        if (tmpdir[0] && tmpdir[strlen(tmpdir) - 1] != '/') {
    if (argc != 1) {
            Jim_AppendString(interp, filenameObj, "/", 1);
        }
        Jim_AppendString(interp, filenameObj, "tcl.tmp.XXXXXX", -1);
    }
    else {
        filenameObj = Jim_NewStringObj(interp, template, -1);
    }

        Jim_WrongNumArgs(interp, 1, argv, "");
#if defined(S_IRWXG) && defined(S_IRWXO)
    mask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
#else
    
        return JIM_ERR;
    mask = umask(S_IXUSR);
#endif

    
    }

    fd = mkstemp(filenameObj->bytes);
    umask(mask);
    if (fd < 0) {
        JimAioSetError(interp, filenameObj);
    if (pipe(p) != 0) {
        JimAioSetError(interp, NULL);
        Jim_FreeNewObj(interp, filenameObj);
        return -1;
        return JIM_ERR;
    }

    Jim_SetResult(interp, filenameObj);
    return fd;
    return JimMakeChannelPair(interp, p, argv[0], "aio.pipe%ld", 0, mode);
#else
    Jim_SetResultString(interp, "platform has no tempfile support", -1);
    return -1;
}
#endif
}



int Jim_aioInit(Jim_Interp *interp)
{
    if (Jim_PackageProvide(interp, "aio", "1.0", JIM_ERRMSG))
        return JIM_ERR;

#if defined(JIM_SSL)
    Jim_CreateCommand(interp, "load_ssl_certs", JimAioLoadSSLCertsCommand, NULL, NULL);
#endif

    Jim_CreateCommand(interp, "open", JimAioOpenCommand, NULL, NULL);
#ifndef JIM_ANSIC
#ifdef HAVE_SOCKETS
    Jim_CreateCommand(interp, "socket", JimAioSockCommand, NULL, NULL);
#endif
#ifdef HAVE_PIPE
    Jim_CreateCommand(interp, "pipe", JimAioPipeCommand, NULL, NULL);
#endif

    

    JimMakeChannel(interp, stdin, -1, NULL, "stdin", 0, "r");
    JimMakeChannel(interp, stdout, -1, NULL, "stdout", 0, "w");
    JimMakeChannel(interp, stderr, -1, NULL, "stderr", 0, "w");

    return JIM_OK;
}

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
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







-
-
+
+
















-
+

-
-
-
+
+
+


-
+

-
+
















-
-
+
+







#if defined(JIM_REGEXP)
#else
    #include <regex.h>
#endif

static void FreeRegexpInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
{
    regfree(objPtr->internalRep.regexpValue.compre);
    Jim_Free(objPtr->internalRep.regexpValue.compre);
    regfree(objPtr->internalRep.ptrIntValue.ptr);
    Jim_Free(objPtr->internalRep.ptrIntValue.ptr);
}

static const Jim_ObjType regexpObjType = {
    "regexp",
    FreeRegexpInternalRep,
    NULL,
    NULL,
    JIM_TYPE_NONE
};

static regex_t *SetRegexpFromAny(Jim_Interp *interp, Jim_Obj *objPtr, unsigned flags)
{
    regex_t *compre;
    const char *pattern;
    int ret;

    

    if (objPtr->typePtr == &regexpObjType &&
        objPtr->internalRep.regexpValue.compre && objPtr->internalRep.regexpValue.flags == flags) {
        
        return objPtr->internalRep.regexpValue.compre;
        objPtr->internalRep.ptrIntValue.ptr && objPtr->internalRep.ptrIntValue.int1 == flags) {

        return objPtr->internalRep.ptrIntValue.ptr;
    }

    


    

    pattern = Jim_String(objPtr);
    compre = Jim_Alloc(sizeof(regex_t));

    if ((ret = regcomp(compre, pattern, REG_EXTENDED | flags)) != 0) {
        char buf[100];

        regerror(ret, compre, buf, sizeof(buf));
        Jim_SetResultFormatted(interp, "couldn't compile regular expression pattern: %s", buf);
        regfree(compre);
        Jim_Free(compre);
        return NULL;
    }

    Jim_FreeIntRep(interp, objPtr);

    objPtr->typePtr = &regexpObjType;
    objPtr->internalRep.regexpValue.flags = flags;
    objPtr->internalRep.regexpValue.compre = compre;
    objPtr->internalRep.ptrIntValue.int1 = flags;
    objPtr->internalRep.ptrIntValue.ptr = compre;

    return compre;
}

int Jim_RegexpCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
    int opt_indices = 0;
2999
3000
3001
3002
3003
3004
3005
3006

3007
3008
3009
3010
3011
3012
3013
3135
3136
3137
3138
3139
3140
3141

3142
3143
3144
3145
3146
3147
3148
3149







-
+







    if (match == REG_NOMATCH) {
        goto done;
    }

    num_matches++;

    if (opt_all && !opt_inline) {
        

        goto try_next_match;
    }


    j = 0;
    for (i += 2; opt_inline ? j < num_vars : i < argc; i++, j++) {
        Jim_Obj *resultObj;
3039
3040
3041
3042
3043
3044
3045
3046

3047
3048
3049
3050
3051
3052
3053
3175
3176
3177
3178
3179
3180
3181

3182
3183
3184
3185
3186
3187
3188
3189







-
+







            }
        }

        if (opt_inline) {
            Jim_ListAppendElement(interp, resultListObj, resultObj);
        }
        else {
            

            result = Jim_SetVariable(interp, argv[i], resultObj);

            if (result != JIM_OK) {
                Jim_FreeObj(interp, resultObj);
                break;
            }
        }
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
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







-
+














-
+







    }
    pattern = Jim_String(argv[i]);

    source_str = Jim_GetString(argv[i + 1], &source_len);
    replace_str = Jim_GetString(argv[i + 2], &replace_len);
    varname = argv[i + 3];

    

    resultObj = Jim_NewStringObj(interp, "", 0);

    if (offset) {
        if (offset < 0) {
            offset += source_len + 1;
        }
        if (offset > source_len) {
            offset = source_len;
        }
        else if (offset < 0) {
            offset = 0;
        }
    }

    

    Jim_AppendString(interp, resultObj, source_str, offset);


    n = source_len - offset;
    p = source_str + offset;
    do {
        int match = regexec(regex, p, MAX_SUB_MATCHES, pmatch, regexec_flags);
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
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







-
+




-
+




-
+

-
+










-
+







                    pmatch[idx].rm_eo - pmatch[idx].rm_so);
            }
        }

        p += pmatch[0].rm_eo;
        n -= pmatch[0].rm_eo;

        

        if (!opt_all || n == 0) {
            break;
        }

        

        if ((regcomp_flags & REG_NEWLINE) == 0 && pattern[0] == '^') {
            break;
        }

        

        if (pattern[0] == '\0' && n) {
            

            Jim_AppendString(interp, resultObj, p, 1);
            p++;
            n--;
        }

        regexec_flags |= REG_NOTBOL;
    } while (n);

    Jim_AppendString(interp, resultObj, p, -1);

    

    if (argc - i == 4) {
        result = Jim_SetVariable(interp, varname, resultObj);

        if (result == JIM_OK) {
            Jim_SetResultInt(interp, num_matches);
        }
        else {
3318
3319
3320
3321
3322
3323
3324
3325

3326
3327
3328
3329
3330







3331
3332
3333
3334
3335
3336
3337
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







-
+





+
+
+
+
+
+
+







#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#endif

# ifndef MAXPATHLEN
# define MAXPATHLEN JIM_PATH_LEN
# endif

#if defined(__MINGW32__) || defined(_MSC_VER)
#if defined(__MINGW32__) || defined(__MSYS__) || defined(_MSC_VER)
#define ISWINDOWS 1
#else
#define ISWINDOWS 0
#endif


#if defined(HAVE_STRUCT_STAT_ST_MTIMESPEC)
    #define STAT_MTIME_US(STAT) ((STAT).st_mtimespec.tv_sec * 1000000ll + (STAT).st_mtimespec.tv_nsec / 1000)
#elif defined(HAVE_STRUCT_STAT_ST_MTIM)
    #define STAT_MTIME_US(STAT) ((STAT).st_mtim.tv_sec * 1000000ll + (STAT).st_mtim.tv_nsec / 1000)
#endif


static const char *JimGetFileType(int mode)
{
    if (S_ISREG(mode)) {
        return "file";
    }
    else if (S_ISDIR(mode)) {
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
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







-
+












+
+
+



-
+

+
-
+
+

+
-
-
+
+
+
+
+
+
+





-
-
-
-
-
-








-
+



















-
+







{
    Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, key, -1));
    Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, value));
}

static int StoreStatData(Jim_Interp *interp, Jim_Obj *varName, const struct stat *sb)
{
    

    Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);

    AppendStatElement(interp, listObj, "dev", sb->st_dev);
    AppendStatElement(interp, listObj, "ino", sb->st_ino);
    AppendStatElement(interp, listObj, "mode", sb->st_mode);
    AppendStatElement(interp, listObj, "nlink", sb->st_nlink);
    AppendStatElement(interp, listObj, "uid", sb->st_uid);
    AppendStatElement(interp, listObj, "gid", sb->st_gid);
    AppendStatElement(interp, listObj, "size", sb->st_size);
    AppendStatElement(interp, listObj, "atime", sb->st_atime);
    AppendStatElement(interp, listObj, "mtime", sb->st_mtime);
    AppendStatElement(interp, listObj, "ctime", sb->st_ctime);
#ifdef STAT_MTIME_US
    AppendStatElement(interp, listObj, "mtimeus", STAT_MTIME_US(*sb));
#endif
    Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "type", -1));
    Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, JimGetFileType((int)sb->st_mode), -1));

    

    if (varName) {
        Jim_Obj *objPtr;
        Jim_Obj *objPtr = Jim_GetVariable(interp, varName, JIM_NONE);
        objPtr = Jim_GetVariable(interp, varName, JIM_NONE);

        if (objPtr) {
            Jim_Obj *objv[2];
            if (Jim_DictSize(interp, objPtr) < 0) {
                

            objv[0] = objPtr;
            objv[1] = listObj;

            objPtr = Jim_DictMerge(interp, 2, objv);
            if (objPtr == NULL) {

                Jim_SetResultFormatted(interp, "can't set \"%#s(dev)\": variable isn't array", varName);
                Jim_FreeNewObj(interp, listObj);
                return JIM_ERR;
            }

            if (Jim_IsShared(objPtr))
                objPtr = Jim_DuplicateObj(interp, objPtr);

            
            Jim_ListAppendList(interp, objPtr, listObj);
            Jim_DictSize(interp, objPtr);
            Jim_InvalidateStringRep(objPtr);

            Jim_FreeNewObj(interp, listObj);
            listObj = objPtr;
        }
        Jim_SetVariable(interp, varName, listObj);
    }

    

    Jim_SetResult(interp, listObj);

    return JIM_OK;
}

static int file_cmd_dirname(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
    const char *path = Jim_String(argv[0]);
    const char *p = strrchr(path, '/');

    if (!p && path[0] == '.' && path[1] == '.' && path[2] == '\0') {
        Jim_SetResultString(interp, "..", -1);
    } else if (!p) {
        Jim_SetResultString(interp, ".", -1);
    }
    else if (p == path) {
        Jim_SetResultString(interp, "/", -1);
    }
    else if (ISWINDOWS && p[-1] == ':') {
        

        Jim_SetResultString(interp, path, p - path + 1);
    }
    else {
        Jim_SetResultString(interp, path, p - path);
    }
    return JIM_OK;
}
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
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







-
+





-
+



-
+








-
+




-
+














-
+

-
+








-
+







{
    int i;
    char *newname = Jim_Alloc(MAXPATHLEN + 1);
    char *last = newname;

    *newname = 0;

    

    for (i = 0; i < argc; i++) {
        int len;
        const char *part = Jim_GetString(argv[i], &len);

        if (*part == '/') {
            

            last = newname;
        }
        else if (ISWINDOWS && strchr(part, ':')) {
            

            last = newname;
        }
        else if (part[0] == '.') {
            if (part[1] == '/') {
                part += 2;
                len -= 2;
            }
            else if (part[1] == 0 && last != newname) {
                

                continue;
            }
        }

        

        if (last != newname && last[-1] != '/') {
            *last++ = '/';
        }

        if (len) {
            if (last + len - newname >= MAXPATHLEN) {
                Jim_Free(newname);
                Jim_SetResultString(interp, "Path too long", -1);
                return JIM_ERR;
            }
            memcpy(last, part, len);
            last += len;
        }

        

        if (last > newname + 1 && last[-1] == '/') {
            

            if (!ISWINDOWS || !(last > newname + 2 && last[-2] == ':')) {
                *--last = 0;
            }
        }
    }

    *last = 0;

    


    Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, newname, last - newname));

    return JIM_OK;
}

static int file_access(Jim_Interp *interp, Jim_Obj *filename, int mode)
3589
3590
3591
3592
3593
3594
3595
3596

3597
3598
3599
3600
3601
3602
3603
3737
3738
3739
3740
3741
3742
3743

3744
3745
3746
3747
3748
3749
3750
3751







-
+







}

static int file_cmd_executable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
#ifdef X_OK
    return file_access(interp, argv[0], X_OK);
#else
    

    Jim_SetResultBool(interp, 1);
    return JIM_OK;
#endif
}

static int file_cmd_exists(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
3614
3615
3616
3617
3618
3619
3620
3621

3622
3623
3624
3625
3626
3627
3628
3762
3763
3764
3765
3766
3767
3768

3769
3770
3771
3772
3773
3774
3775
3776







-
+







    }

    while (argc--) {
        const char *path = Jim_String(argv[0]);

        if (unlink(path) == -1 && errno != ENOENT) {
            if (rmdir(path) == -1) {
                

                if (!force || Jim_EvalPrefix(interp, "file delete force", 1, argv) != JIM_OK) {
                    Jim_SetResultFormatted(interp, "couldn't delete file \"%s\": %s", path,
                        strerror(errno));
                    return JIM_ERR;
                }
            }
        }
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
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







-
+



-
+
















-
+


-
+






-
+


-
+







#define MKDIR_DEFAULT(PATHNAME) mkdir(PATHNAME, 0755)
#endif

static int mkdir_all(char *path)
{
    int ok = 1;

    

    goto first;

    while (ok--) {
        

        {
            char *slash = strrchr(path, '/');

            if (slash && slash != path) {
                *slash = 0;
                if (mkdir_all(path) != 0) {
                    return -1;
                }
                *slash = '/';
            }
        }
      first:
        if (MKDIR_DEFAULT(path) == 0) {
            return 0;
        }
        if (errno == ENOENT) {
            

            continue;
        }
        

        if (errno == EEXIST) {
            struct stat sb;

            if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) {
                return 0;
            }
            

            errno = EEXIST;
        }
        

        break;
    }
    return -1;
}

static int file_cmd_mkdir(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
3696
3697
3698
3699
3700
3701
3702
3703

3704
3705
3706
3707
3708
3709
3710
3844
3845
3846
3847
3848
3849
3850

3851
3852
3853
3854
3855
3856
3857
3858







-
+







        argv++;
    }
    return JIM_OK;
}

static int file_cmd_tempfile(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
    int fd = Jim_MakeTempFile(interp, (argc >= 1) ? Jim_String(argv[0]) : NULL);
    int fd = Jim_MakeTempFile(interp, (argc >= 1) ? Jim_String(argv[0]) : NULL, 0);

    if (fd < 0) {
        return JIM_ERR;
    }
    close(fd);

    return JIM_OK;
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
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+






-
-
+
-
-
-
+


-
-
-
-
-
+
-
-
-
-
-
-
-







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+








    if (file_stat(interp, argv[0], &sb) != JIM_OK) {
        return JIM_ERR;
    }
    Jim_SetResultInt(interp, sb.st_atime);
    return JIM_OK;
}

static int JimSetFileTimes(Jim_Interp *interp, const char *filename, jim_wide us)
{
#ifdef HAVE_UTIMES
    struct timeval times[2];

    times[1].tv_sec = times[0].tv_sec = us / 1000000;
    times[1].tv_usec = times[0].tv_usec = us % 1000000;

    if (utimes(filename, times) != 0) {
        Jim_SetResultFormatted(interp, "can't set time on \"%s\": %s", filename, strerror(errno));
        return JIM_ERR;
    }
    return JIM_OK;
#else
    Jim_SetResultString(interp, "Not implemented", -1);
    return JIM_ERR;
#endif
}

static int file_cmd_mtime(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
    struct stat sb;

    if (argc == 2) {
#ifdef HAVE_UTIMES
        jim_wide newtime;
        jim_wide secs;
        struct timeval times[2];

        if (Jim_GetWide(interp, argv[1], &newtime) != JIM_OK) {
        if (Jim_GetWide(interp, argv[1], &secs) != JIM_OK) {
            return JIM_ERR;
        }

        times[1].tv_sec = times[0].tv_sec = newtime;
        times[1].tv_usec = times[0].tv_usec = 0;

        if (utimes(Jim_String(argv[0]), times) != 0) {
        return JimSetFileTimes(interp, Jim_String(argv[0]), secs * 1000000);
            Jim_SetResultFormatted(interp, "can't set time on \"%#s\": %s", argv[0], strerror(errno));
            return JIM_ERR;
        }
#else
        Jim_SetResultString(interp, "Not implemented", -1);
        return JIM_ERR;
#endif
    }
    if (file_stat(interp, argv[0], &sb) != JIM_OK) {
        return JIM_ERR;
    }
    Jim_SetResultInt(interp, sb.st_mtime);
    return JIM_OK;
}

#ifdef STAT_MTIME_US
static int file_cmd_mtimeus(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
    struct stat sb;

    if (argc == 2) {
        jim_wide us;
        if (Jim_GetWide(interp, argv[1], &us) != JIM_OK) {
            return JIM_ERR;
        }
        return JimSetFileTimes(interp, Jim_String(argv[0]), us);
    }
    if (file_stat(interp, argv[0], &sb) != JIM_OK) {
        return JIM_ERR;
    }
    Jim_SetResultInt(interp, STAT_MTIME_US(sb));
    return JIM_OK;
}
#endif

static int file_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
    return Jim_EvalPrefix(interp, "file copy", argc, argv);
}

static int file_cmd_size(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
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
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







-
+






-
+

+
+
+
+
+
+
+
+
+





-
+






-
+






-
+






-
+






-
+






-
+






-
+






-
+






-
+






-
+






-
+






-
+






-
+






-
+






-
+







-
+








-
+







-
+






-
+






-
+






-
+







-
+







-
+






-
+








static const jim_subcmd_type file_command_table[] = {
    {   "atime",
        "name",
        file_cmd_atime,
        1,
        1,
        

    },
    {   "mtime",
        "name ?time?",
        file_cmd_mtime,
        1,
        2,
        

    },
#ifdef STAT_MTIME_US
    {   "mtimeus",
        "name ?time?",
        file_cmd_mtimeus,
        1,
        2,

    },
#endif
    {   "copy",
        "?-force? source dest",
        file_cmd_copy,
        2,
        3,
        

    },
    {   "dirname",
        "name",
        file_cmd_dirname,
        1,
        1,
        

    },
    {   "rootname",
        "name",
        file_cmd_rootname,
        1,
        1,
        

    },
    {   "extension",
        "name",
        file_cmd_extension,
        1,
        1,
        

    },
    {   "tail",
        "name",
        file_cmd_tail,
        1,
        1,
        

    },
    {   "normalize",
        "name",
        file_cmd_normalize,
        1,
        1,
        

    },
    {   "join",
        "name ?name ...?",
        file_cmd_join,
        1,
        -1,
        

    },
    {   "readable",
        "name",
        file_cmd_readable,
        1,
        1,
        

    },
    {   "writable",
        "name",
        file_cmd_writable,
        1,
        1,
        

    },
    {   "executable",
        "name",
        file_cmd_executable,
        1,
        1,
        

    },
    {   "exists",
        "name",
        file_cmd_exists,
        1,
        1,
        

    },
    {   "delete",
        "?-force|--? name ...",
        file_cmd_delete,
        1,
        -1,
        

    },
    {   "mkdir",
        "dir ...",
        file_cmd_mkdir,
        1,
        -1,
        

    },
    {   "tempfile",
        "?template?",
        file_cmd_tempfile,
        0,
        1,
        

    },
    {   "rename",
        "?-force? source dest",
        file_cmd_rename,
        2,
        3,
        

    },
#if defined(HAVE_LINK) && defined(HAVE_SYMLINK)
    {   "link",
        "?-symbolic|-hard? newname target",
        file_cmd_link,
        2,
        3,
        

    },
#endif
#if defined(HAVE_READLINK)
    {   "readlink",
        "name",
        file_cmd_readlink,
        1,
        1,
        

    },
#endif
    {   "size",
        "name",
        file_cmd_size,
        1,
        1,
        

    },
    {   "stat",
        "name ?var?",
        file_cmd_stat,
        1,
        2,
        

    },
    {   "lstat",
        "name ?var?",
        file_cmd_lstat,
        1,
        2,
        

    },
    {   "type",
        "name",
        file_cmd_type,
        1,
        1,
        

    },
#ifdef HAVE_GETEUID
    {   "owned",
        "name",
        file_cmd_owned,
        1,
        1,
        

    },
#endif
    {   "isdirectory",
        "name",
        file_cmd_isdirectory,
        1,
        1,
        

    },
    {   "isfile",
        "name",
        file_cmd_isfile,
        1,
        1,
        

    },
    {
        NULL
    }
};

static int Jim_CdCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4177
4178
4179
4180
4181
4182
4183
4184

4185
4186
4187
4188
4189
4190
4191
4359
4360
4361
4362
4363
4364
4365

4366
4367
4368
4369
4370
4371
4372
4373







-
+








    if (getcwd(cwd, MAXPATHLEN) == NULL) {
        Jim_SetResultString(interp, "Failed to get pwd", -1);
        Jim_Free(cwd);
        return JIM_ERR;
    }
    else if (ISWINDOWS) {
        

        char *p = cwd;
        while ((p = strchr(p, '\\')) != NULL) {
            *p++ = '/';
        }
    }

    Jim_SetResultString(interp, cwd, -1);
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
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







+
+
+











-
+








-
+








    Jim_CreateCommand(interp, "file", Jim_SubCmdProc, (void *)file_command_table, NULL);
    Jim_CreateCommand(interp, "pwd", Jim_PwdCmd, NULL, NULL);
    Jim_CreateCommand(interp, "cd", Jim_CdCmd, NULL, NULL);
    return JIM_OK;
}

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <string.h>
#include <ctype.h>


#if (!defined(HAVE_VFORK) || !defined(HAVE_WAITPID)) && !defined(__MINGW32__)
static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
    Jim_Obj *cmdlineObj = Jim_NewEmptyStringObj(interp);
    int i, j;
    int rc;

    

    for (i = 1; i < argc; i++) {
        int len;
        const char *arg = Jim_GetString(argv[i], &len);

        if (i > 1) {
            Jim_AppendString(interp, cmdlineObj, " ", 1);
        }
        if (strpbrk(arg, "\\\" ") == NULL) {
            

            Jim_AppendString(interp, cmdlineObj, arg, len);
            continue;
        }

        Jim_AppendString(interp, cmdlineObj, "\"", 1);
        for (j = 0; j < len; j++) {
            if (arg[j] == '\\' || arg[j] == '"') {
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
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







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+

-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-

-
+



-
-
+
+

-
+
-
-

+
-
+
-
-
-
-
+
-
-
-
-












-
+


-
+







    return JIM_OK;
}
#else


#include <errno.h>
#include <signal.h>

#if defined(__MINGW32__)
    
    #ifndef STRICT
    #define STRICT
    #endif
    #define WIN32_LEAN_AND_MEAN
    #include <windows.h>
    #include <fcntl.h>

    typedef HANDLE fdtype;
    typedef HANDLE pidtype;
    #define JIM_BAD_FD INVALID_HANDLE_VALUE
    #define JIM_BAD_PID INVALID_HANDLE_VALUE
    #define JimCloseFd CloseHandle

    #define WIFEXITED(STATUS) 1
    #define WEXITSTATUS(STATUS) (STATUS)
    #define WIFSIGNALED(STATUS) 0
    #define WTERMSIG(STATUS) 0
    #define WNOHANG 1

    static fdtype JimFileno(FILE *fh);
    static pidtype JimWaitPid(pidtype pid, int *status, int nohang);
    static fdtype JimDupFd(fdtype infd);
    static fdtype JimOpenForRead(const char *filename);
    static FILE *JimFdOpenForRead(fdtype fd);
    static int JimPipe(fdtype pipefd[2]);
    static pidtype JimStartWinProcess(Jim_Interp *interp, char **argv, char *env,
        fdtype inputId, fdtype outputId, fdtype errorId);
    static int JimErrno(void);
#else
    #include <unistd.h>
    #include <fcntl.h>
    #include <sys/wait.h>
    #include <sys/stat.h>
#include <sys/stat.h>

    typedef int fdtype;
    typedef int pidtype;
    #define JimPipe pipe
    #define JimErrno() errno
    #define JIM_BAD_FD -1
    #define JIM_BAD_PID -1
    #define JimFileno fileno
    #define JimReadFd read
    #define JimCloseFd close
    #define JimWaitPid waitpid
    #define JimDupFd dup
    #define JimFdOpenForRead(FD) fdopen((FD), "r")
    #define JimOpenForRead(NAME) open((NAME), O_RDONLY, 0)

struct WaitInfoTable;
    #ifndef HAVE_EXECVPE
        #define execvpe(ARG0, ARGV, ENV) execvp(ARG0, ARGV)
    #endif
#endif

static const char *JimStrError(void);
static char **JimOriginalEnviron(void);
static char **JimSaveEnv(char **env);
static void JimRestoreEnv(char **env);
static int JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv,
    pidtype **pidArrayPtr, fdtype *inPipePtr, fdtype *outPipePtr, fdtype *errFilePtr);
static void JimDetachPids(Jim_Interp *interp, int numPids, const pidtype *pidPtr);
    pidtype **pidArrayPtr, int *inPipePtr, int *outPipePtr, int *errFilePtr);
static void JimDetachPids(struct WaitInfoTable *table, int numPids, const pidtype *pidPtr);
static int JimCleanupChildren(Jim_Interp *interp, int numPids, pidtype *pidPtr, Jim_Obj *errStrObj);
static fdtype JimCreateTemp(Jim_Interp *interp, const char *contents, int len);
static int Jim_WaitCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
static fdtype JimOpenForWrite(const char *filename, int append);
static int JimRewindFd(fdtype fd);

#if defined(__MINGW32__)
static void Jim_SetResultErrno(Jim_Interp *interp, const char *msg)
static pidtype JimStartWinProcess(Jim_Interp *interp, char **argv, char **env, int inputId, int outputId, int errorId);
{
    Jim_SetResultFormatted(interp, "%s: %s", msg, JimStrError());
}

#endif
static const char *JimStrError(void)
{
    return strerror(JimErrno());
}

static void Jim_RemoveTrailingNewline(Jim_Obj *objPtr)
{
    int len;
    const char *s = Jim_GetString(objPtr, &len);

    if (len > 0 && s[len - 1] == '\n') {
        objPtr->length--;
        objPtr->bytes[objPtr->length] = '\0';
    }
}

static int JimAppendStreamToString(Jim_Interp *interp, fdtype fd, Jim_Obj *strObj)
static int JimAppendStreamToString(Jim_Interp *interp, int fd, Jim_Obj *strObj)
{
    char buf[256];
    FILE *fh = JimFdOpenForRead(fd);
    FILE *fh = fdopen(fd, "r");
    int ret = 0;

    if (fh == NULL) {
        return -1;
    }

    while (1) {
4388
4389
4390
4391
4392
4393
4394
4395

4396
4397
4398
4399

4400
4401
4402

4403
4404
4405
4406
4407
4408
4409
4513
4514
4515
4516
4517
4518
4519

4520
4521
4522
4523

4524
4525
4526

4527
4528
4529
4530
4531
4532
4533
4534







-
+



-
+


-
+







    int n;
    char **envptr;
    char *envdata;

    Jim_Obj *objPtr = Jim_GetGlobalVariableStr(interp, "env", JIM_NONE);

    if (!objPtr) {
        return Jim_GetEnviron();
        return JimOriginalEnviron();
    }


    

    num = Jim_ListLength(interp, objPtr);
    if (num % 2) {
        

        num--;
    }
    size = Jim_Length(objPtr) + 2;

    envptr = Jim_Alloc(sizeof(*envptr) * (num / 2 + 1) + size);
    envdata = (char *)&envptr[num / 2 + 1];

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
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







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+

-
+

-
-
+
-
-
-
-
+
+
+
+
+







+




+




+









-
-
+
+
+
+
+
+
+
+
+

-
+







-
-
-
+
+
+


+

-
-
-
+
+
+
+











+
-
-
+
+
+







+



+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



-
-
+
+





+










-
+





-
+















-
-
+
+






-
+







-
+

-
+






-
+





-
+




-
+


-
+




+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
















-
+

-
+











-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

+
+
+
+
+

-
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+

+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+




-
+










-
+

-
-
-
-
+
+
+
+




















-
-
-
-
-
+
+
+
+
+






+
+
+


-
+



-
-

-
+


-
+


-
+

-
+







static void JimFreeEnv(char **env, char **original_environ)
{
    if (env != original_environ) {
        Jim_Free(env);
    }
}

#ifndef jim_ext_signal

const char *Jim_SignalId(int sig)
{
    static char buf[10];
    snprintf(buf, sizeof(buf), "%d", sig);
    return buf;
}

const char *Jim_SignalName(int sig)
{
    return Jim_SignalId(sig);
}
#endif

static int JimCheckWaitStatus(Jim_Interp *interp, pidtype pid, int waitStatus, Jim_Obj *errStrObj)
static Jim_Obj *JimMakeErrorCode(Jim_Interp *interp, pidtype pid, int waitStatus, Jim_Obj *errStrObj)
{
    Jim_Obj *errorCode;
    Jim_Obj *errorCode = Jim_NewListObj(interp, NULL, 0);

    if (WIFEXITED(waitStatus) && WEXITSTATUS(waitStatus) == 0) {
        return JIM_OK;
    if (pid == JIM_BAD_PID || pid == JIM_NO_PID) {
    }
    errorCode = Jim_NewListObj(interp, NULL, 0);

    if (WIFEXITED(waitStatus)) {
        Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "NONE", -1));
        Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, (long)pid));
        Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, -1));
    }
    else if (WIFEXITED(waitStatus)) {
        Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "CHILDSTATUS", -1));
        Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, (long)pid));
        Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, WEXITSTATUS(waitStatus)));
    }
    else {
        const char *type;
        const char *action;
        const char *signame;

        if (WIFSIGNALED(waitStatus)) {
            type = "CHILDKILLED";
            action = "killed";
            signame = Jim_SignalId(WTERMSIG(waitStatus));
        }
        else {
            type = "CHILDSUSP";
            action = "suspended";
            signame = "none";
        }

        Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, type, -1));

        if (errStrObj) {
            Jim_AppendStrings(interp, errStrObj, "child ", action, " by signal ", Jim_SignalId(WTERMSIG(waitStatus)), "\n", NULL);
        }

        Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, (long)pid));
        Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, Jim_SignalId(WTERMSIG(waitStatus)), -1));
        Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, Jim_SignalName(WTERMSIG(waitStatus)), -1));
        Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, signame, -1));
    }
    return errorCode;
}

static int JimCheckWaitStatus(Jim_Interp *interp, pidtype pid, int waitStatus, Jim_Obj *errStrObj)
{
    if (WIFEXITED(waitStatus) && WEXITSTATUS(waitStatus) == 0) {
        return JIM_OK;
    }
    Jim_SetGlobalVariableStr(interp, "errorCode", errorCode);
    Jim_SetGlobalVariableStr(interp, "errorCode", JimMakeErrorCode(interp, pid, waitStatus, errStrObj));

    return JIM_ERR;
}


struct WaitInfo
{
    pidtype pid;                
    int status;                 
    int flags;                  
    pidtype pid;
    int status;
    int flags;
};


struct WaitInfoTable {
    struct WaitInfo *info;      
    int size;                   
    int used;                   
    struct WaitInfo *info;
    int size;
    int used;
    int refcount;
};


#define WI_DETACHED 2

#define WAIT_TABLE_GROW_BY 4

static void JimFreeWaitInfoTable(struct Jim_Interp *interp, void *privData)
{
    struct WaitInfoTable *table = privData;

    if (--table->refcount == 0) {
    Jim_Free(table->info);
    Jim_Free(table);
        Jim_Free(table->info);
        Jim_Free(table);
    }
}

static struct WaitInfoTable *JimAllocWaitInfoTable(void)
{
    struct WaitInfoTable *table = Jim_Alloc(sizeof(*table));
    table->info = NULL;
    table->size = table->used = 0;
    table->refcount = 1;

    return table;
}

static int JimWaitRemove(struct WaitInfoTable *table, pidtype pid)
{
    int i;


    for (i = 0; i < table->used; i++) {
        if (pid == table->info[i].pid) {
            if (i != table->used - 1) {
                table->info[i] = table->info[table->used - 1];
            }
            table->used--;
            return 0;
        }
    }
    return -1;
}

static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
    fdtype outputId;    
    fdtype errorId;     
    int outputId;
    int errorId;
    pidtype *pidPtr;
    int numPids, result;
    int child_siginfo = 1;
    Jim_Obj *childErrObj;
    Jim_Obj *errStrObj;
    struct WaitInfoTable *table = Jim_CmdPrivData(interp);

    if (argc > 1 && Jim_CompareStringImmediate(interp, argv[argc - 1], "&")) {
        Jim_Obj *listObj;
        int i;

        argc--;
        numPids = JimCreatePipeline(interp, argc - 1, argv + 1, &pidPtr, NULL, NULL, NULL);
        if (numPids < 0) {
            return JIM_ERR;
        }
        

        listObj = Jim_NewListObj(interp, NULL, 0);
        for (i = 0; i < numPids; i++) {
            Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, (long)pidPtr[i]));
        }
        Jim_SetResult(interp, listObj);
        JimDetachPids(interp, numPids, pidPtr);
        JimDetachPids(table, numPids, pidPtr);
        Jim_Free(pidPtr);
        return JIM_OK;
    }

    numPids =
        JimCreatePipeline(interp, argc - 1, argv + 1, &pidPtr, NULL, &outputId, &errorId);

    if (numPids < 0) {
        return JIM_ERR;
    }

    result = JIM_OK;

    errStrObj = Jim_NewStringObj(interp, "", 0);

    
    if (outputId != JIM_BAD_FD) {

    if (outputId != -1) {
        if (JimAppendStreamToString(interp, outputId, errStrObj) < 0) {
            result = JIM_ERR;
            Jim_SetResultErrno(interp, "error reading from output pipe");
        }
    }

    

    childErrObj = Jim_NewStringObj(interp, "", 0);
    Jim_IncrRefCount(childErrObj);

    if (JimCleanupChildren(interp, numPids, pidPtr, childErrObj) != JIM_OK) {
        result = JIM_ERR;
    }

    if (errorId != JIM_BAD_FD) {
    if (errorId != -1) {
        int ret;
        JimRewindFd(errorId);
        lseek(errorId, 0, SEEK_SET);
        ret = JimAppendStreamToString(interp, errorId, errStrObj);
        if (ret < 0) {
            Jim_SetResultErrno(interp, "error reading from error pipe");
            result = JIM_ERR;
        }
        else if (ret > 0) {
            

            child_siginfo = 0;
        }
    }

    if (child_siginfo) {
        

        Jim_AppendObj(interp, errStrObj, childErrObj);
    }
    Jim_DecrRefCount(interp, childErrObj);

    

    Jim_RemoveTrailingNewline(errStrObj);

    

    Jim_SetResult(interp, errStrObj);

    return result;
}

static pidtype JimWaitForProcess(struct WaitInfoTable *table, pidtype pid, int *statusPtr)
{
    if (JimWaitRemove(table, pid) == 0) {

         waitpid(pid, statusPtr, 0);
         return pid;
    }


    return JIM_BAD_PID;
}

static void JimDetachPids(struct WaitInfoTable *table, int numPids, const pidtype *pidPtr)
{
    int j;

    for (j = 0; j < numPids; j++) {

        int i;
        for (i = 0; i < table->used; i++) {
            if (pidPtr[j] == table->info[i].pid) {
                table->info[i].flags |= WI_DETACHED;
                break;
            }
        }
    }
}

static int JimGetChannelFd(Jim_Interp *interp, const char *name)
{
    Jim_Obj *objv[2];

    objv[0] = Jim_NewStringObj(interp, name, -1);
    objv[1] = Jim_NewStringObj(interp, "getfd", -1);

    if (Jim_EvalObjVector(interp, 2, objv) == JIM_OK) {
        jim_wide fd;
        if (Jim_GetWide(interp, Jim_GetResult(interp), &fd) == JIM_OK) {
            return fd;
        }
    }
    return -1;
}

static void JimReapDetachedPids(struct WaitInfoTable *table)
{
    struct WaitInfo *waitPtr;
    int count;
    int dest;

    if (!table) {
        return;
    }

    waitPtr = table->info;
    dest = 0;
    for (count = table->used; count > 0; waitPtr++, count--) {
        if (waitPtr->flags & WI_DETACHED) {
            int status;
            pidtype pid = JimWaitPid(waitPtr->pid, &status, WNOHANG);
            pidtype pid = waitpid(waitPtr->pid, &status, WNOHANG);
            if (pid == waitPtr->pid) {
                

                table->used--;
                continue;
            }
        }
        if (waitPtr != &table->info[dest]) {
            table->info[dest] = *waitPtr;
        }
        dest++;
    }
}

static pidtype JimWaitForProcess(struct WaitInfoTable *table, pidtype pid, int *statusPtr)
{
    int i;

    
static int Jim_WaitCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
    for (i = 0; i < table->used; i++) {
        if (pid == table->info[i].pid) {
            
            JimWaitPid(pid, statusPtr, 0);

            
            if (i != table->used - 1) {
                table->info[i] = table->info[table->used - 1];
            }
            table->used--;
            return pid;
        }
    }

    
    return JIM_BAD_PID;
}

static void JimDetachPids(Jim_Interp *interp, int numPids, const pidtype *pidPtr)
{
    int j;
    struct WaitInfoTable *table = Jim_CmdPrivData(interp);
    int nohang = 0;
    pidtype pid;
    long pidarg;
    int status;
    Jim_Obj *errCodeObj;

    for (j = 0; j < numPids; j++) {
        

        int i;
        for (i = 0; i < table->used; i++) {
            if (pidPtr[j] == table->info[i].pid) {
                table->info[i].flags |= WI_DETACHED;
                break;
            }
        }
    if (argc == 1) {
        JimReapDetachedPids(table);
        return JIM_OK;
    }

    if (argc > 1 && Jim_CompareStringImmediate(interp, argv[1], "-nohang")) {
        nohang = 1;
    }
    if (argc != nohang + 2) {
        Jim_WrongNumArgs(interp, 1, argv, "?-nohang? ?pid?");
        return JIM_ERR;
}

static FILE *JimGetAioFilehandle(Jim_Interp *interp, const char *name)
{
    FILE *fh;
    Jim_Obj *fhObj;

    fhObj = Jim_NewStringObj(interp, name, -1);
    Jim_IncrRefCount(fhObj);
    }
    if (Jim_GetLong(interp, argv[nohang + 1], &pidarg) != JIM_OK) {
        return JIM_ERR;
    }

    pid = waitpid((pidtype)pidarg, &status, nohang ? WNOHANG : 0);

    errCodeObj = JimMakeErrorCode(interp, pid, status, NULL);

    if (pid != JIM_BAD_PID && (WIFEXITED(status) || WIFSIGNALED(status))) {

        JimWaitRemove(table, pid);
    }
    Jim_SetResult(interp, errCodeObj);
    return JIM_OK;
}

    fh = Jim_AioFilehandle(interp, fhObj);
    Jim_DecrRefCount(interp, fhObj);

    return fh;
static int Jim_PidCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
    if (argc != 1) {
        Jim_WrongNumArgs(interp, 1, argv, "");
        return JIM_ERR;
    }

    Jim_SetResultInt(interp, (jim_wide)getpid());
    return JIM_OK;
}

static int
JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, pidtype **pidArrayPtr,
    fdtype *inPipePtr, fdtype *outPipePtr, fdtype *errFilePtr)
    int *inPipePtr, int *outPipePtr, int *errFilePtr)
{
    pidtype *pidPtr = NULL;         /* Points to malloc-ed array holding all
                                 * the pids of child processes. */
    int numPids = 0;            /* Actual number of processes that exist
                                 * at *pidPtr right now. */
    int cmdCount;               /* Count of number of distinct commands
                                 * found in argc/argv. */
    const char *input = NULL;   /* Describes input for pipeline, depending
                                 * on "inputFile".  NULL means take input
                                 * from stdin/pipe. */
    int input_len = 0;          
    int input_len = 0;

#define FILE_NAME   0           
#define FILE_APPEND 1           
#define FILE_HANDLE 2           
#define FILE_TEXT   3           
#define FILE_NAME   0
#define FILE_APPEND 1
#define FILE_HANDLE 2
#define FILE_TEXT   3

    int inputFile = FILE_NAME;  /* 1 means input is name of input file.
                                 * 2 means input is filehandle name.
                                 * 0 means input holds actual
                                 * text to be input to command. */

    int outputFile = FILE_NAME; /* 0 means output is the name of output file.
                                 * 1 means output is the name of output file, and append.
                                 * 2 means output is filehandle name.
                                 * All this is ignored if output is NULL
                                 */
    int errorFile = FILE_NAME;  /* 0 means error is the name of error file.
                                 * 1 means error is the name of error file, and append.
                                 * 2 means error is filehandle name.
                                 * All this is ignored if error is NULL
                                 */
    const char *output = NULL;  /* Holds name of output file to pipe to,
                                 * or NULL if output goes to stdout/pipe. */
    const char *error = NULL;   /* Holds name of stderr file to pipe to,
                                 * or NULL if stderr goes to stderr/pipe. */
    fdtype inputId = JIM_BAD_FD;
    fdtype outputId = JIM_BAD_FD;
    fdtype errorId = JIM_BAD_FD;
    fdtype lastOutputId = JIM_BAD_FD;
    fdtype pipeIds[2];           
    int inputId = -1;
    int outputId = -1;
    int errorId = -1;
    int lastOutputId = -1;
    int pipeIds[2];
    int firstArg, lastArg;      /* Indexes of first and last arguments in
                                 * current command. */
    int lastBar;
    int i;
    pidtype pid;
    char **save_environ;
#ifndef __MINGW32__
    char **child_environ;
#endif
    struct WaitInfoTable *table = Jim_CmdPrivData(interp);

    

    char **arg_array = Jim_Alloc(sizeof(*arg_array) * (argc + 1));
    int arg_count = 0;

    JimReapDetachedPids(table);

    if (inPipePtr != NULL) {
        *inPipePtr = JIM_BAD_FD;
        *inPipePtr = -1;
    }
    if (outPipePtr != NULL) {
        *outPipePtr = JIM_BAD_FD;
        *outPipePtr = -1;
    }
    if (errFilePtr != NULL) {
        *errFilePtr = JIM_BAD_FD;
        *errFilePtr = -1;
    }
    pipeIds[0] = pipeIds[1] = JIM_BAD_FD;
    pipeIds[0] = pipeIds[1] = -1;

    cmdCount = 1;
    lastBar = -1;
    for (i = 0; i < argc; i++) {
        const char *arg = Jim_String(argv[i]);

        if (arg[0] == '<') {
4794
4795
4796
4797
4798
4799
4800
4801

4802
4803
4804
4805
4806
4807
4808
4978
4979
4980
4981
4982
4983
4984

4985
4986
4987
4988
4989
4990
4991
4992







-
+








            output = arg + 1;
            if (*output == '>') {
                outputFile = FILE_APPEND;
                output++;
            }
            if (*output == '&') {
                

                output++;
                dup_error = 1;
            }
            if (*output == '@') {
                outputFile = FILE_HANDLE;
                output++;
            }
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
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







-
+

















-
+




-
-
+
+


+
+
+
+
-
-
-
-
+
+
+
+
+

-
+


-
+


-
-
-
+
+
+





-
+





-
+




-
-
+
+


-
-
+


-
-
-
+
+
+





-
+





-
+

-
+



-
-
-
+
+
+


-
+



-
-
-
+
+
+


-
-
+



-
-
-
+
+
+





-
-
+
+


-
+









-
+


-
-
-
+
+
+
+
+
-



-
+
+
+
+
+
+
+



+


-
+






-
+




-
+


-
+





+
+
+






-
+

-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
-
-
+
+
+


-
+

+
+
+
-
-
+
+
+
+
+
+
+
+
+




-
+












-
+



-
-
+
+

-
-
+
+


-
+





-
-
+
+

-
-
+
+

-
-
+
+









-
-
-
+
+
+

-
-
-
+
+
+

-
-
-
+
+
+

-
-
+
+

-
-
+
+




-
+















-
+















+







+
-
+
+
+
+
+






-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-







                if (i == lastBar + 1 || i == argc - 1) {
                    Jim_SetResultString(interp, "illegal use of | or |& in command", -1);
                    goto badargs;
                }
                lastBar = i;
                cmdCount++;
            }
            

            arg_array[arg_count++] = (char *)arg;
            continue;
        }

        if (i >= argc) {
            Jim_SetResultFormatted(interp, "can't specify \"%s\" as last word in command", arg);
            goto badargs;
        }
    }

    if (arg_count == 0) {
        Jim_SetResultString(interp, "didn't specify command to execute", -1);
badargs:
        Jim_Free(arg_array);
        return -1;
    }

    

    save_environ = JimSaveEnv(JimBuildEnv(interp));

    if (input != NULL) {
        if (inputFile == FILE_TEXT) {
            inputId = JimCreateTemp(interp, input, input_len);
            if (inputId == JIM_BAD_FD) {
            inputId = Jim_MakeTempFile(interp, NULL, 1);
            if (inputId == -1) {
                goto error;
            }
            if (write(inputId, input, input_len) != input_len) {
                Jim_SetResultErrno(interp, "couldn't write temp file");
                close(inputId);
                goto error;
        }
        else if (inputFile == FILE_HANDLE) {
            
            FILE *fh = JimGetAioFilehandle(interp, input);
            }
            lseek(inputId, 0L, SEEK_SET);
        }
        else if (inputFile == FILE_HANDLE) {
            int fd = JimGetChannelFd(interp, input);

            if (fh == NULL) {
            if (fd < 0) {
                goto error;
            }
            inputId = JimDupFd(JimFileno(fh));
            inputId = dup(fd);
        }
        else {
            inputId = JimOpenForRead(input);
            if (inputId == JIM_BAD_FD) {
                Jim_SetResultFormatted(interp, "couldn't read file \"%s\": %s", input, JimStrError());
            inputId = Jim_OpenForRead(input);
            if (inputId == -1) {
                Jim_SetResultFormatted(interp, "couldn't read file \"%s\": %s", input, strerror(Jim_Errno()));
                goto error;
            }
        }
    }
    else if (inPipePtr != NULL) {
        if (JimPipe(pipeIds) != 0) {
        if (pipe(pipeIds) != 0) {
            Jim_SetResultErrno(interp, "couldn't create input pipe for command");
            goto error;
        }
        inputId = pipeIds[0];
        *inPipePtr = pipeIds[1];
        pipeIds[0] = pipeIds[1] = JIM_BAD_FD;
        pipeIds[0] = pipeIds[1] = -1;
    }

    if (output != NULL) {
        if (outputFile == FILE_HANDLE) {
            FILE *fh = JimGetAioFilehandle(interp, output);
            if (fh == NULL) {
            int fd = JimGetChannelFd(interp, output);
            if (fd < 0) {
                goto error;
            }
            fflush(fh);
            lastOutputId = JimDupFd(JimFileno(fh));
            lastOutputId = dup(fd);
        }
        else {
            lastOutputId = JimOpenForWrite(output, outputFile == FILE_APPEND);
            if (lastOutputId == JIM_BAD_FD) {
                Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", output, JimStrError());
            lastOutputId = Jim_OpenForWrite(output, outputFile == FILE_APPEND);
            if (lastOutputId == -1) {
                Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", output, strerror(Jim_Errno()));
                goto error;
            }
        }
    }
    else if (outPipePtr != NULL) {
        if (JimPipe(pipeIds) != 0) {
        if (pipe(pipeIds) != 0) {
            Jim_SetResultErrno(interp, "couldn't create output pipe");
            goto error;
        }
        lastOutputId = pipeIds[1];
        *outPipePtr = pipeIds[0];
        pipeIds[0] = pipeIds[1] = JIM_BAD_FD;
        pipeIds[0] = pipeIds[1] = -1;
    }
    

    if (error != NULL) {
        if (errorFile == FILE_HANDLE) {
            if (strcmp(error, "1") == 0) {
                
                if (lastOutputId != JIM_BAD_FD) {
                    errorId = JimDupFd(lastOutputId);

                if (lastOutputId != -1) {
                    errorId = dup(lastOutputId);
                }
                else {
                    

                    error = "stdout";
                }
            }
            if (errorId == JIM_BAD_FD) {
                FILE *fh = JimGetAioFilehandle(interp, error);
                if (fh == NULL) {
            if (errorId == -1) {
                int fd = JimGetChannelFd(interp, error);
                if (fd < 0) {
                    goto error;
                }
                fflush(fh);
                errorId = JimDupFd(JimFileno(fh));
                errorId = dup(fd);
            }
        }
        else {
            errorId = JimOpenForWrite(error, errorFile == FILE_APPEND);
            if (errorId == JIM_BAD_FD) {
                Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", error, JimStrError());
            errorId = Jim_OpenForWrite(error, errorFile == FILE_APPEND);
            if (errorId == -1) {
                Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", error, strerror(Jim_Errno()));
                goto error;
            }
        }
    }
    else if (errFilePtr != NULL) {
        errorId = JimCreateTemp(interp, NULL, 0);
        if (errorId == JIM_BAD_FD) {
        errorId = Jim_MakeTempFile(interp, NULL, 1);
        if (errorId == -1) {
            goto error;
        }
        *errFilePtr = JimDupFd(errorId);
        *errFilePtr = dup(errorId);
    }


    pidPtr = Jim_Alloc(cmdCount * sizeof(*pidPtr));
    for (i = 0; i < numPids; i++) {
        pidPtr[i] = JIM_BAD_PID;
    }
    for (firstArg = 0; firstArg < arg_count; numPids++, firstArg = lastArg + 1) {
        int pipe_dup_err = 0;
        fdtype origErrorId = errorId;
        int origErrorId = errorId;

        for (lastArg = firstArg; lastArg < arg_count; lastArg++) {
            if (arg_array[lastArg][0] == '|') {
                if (arg_array[lastArg][1] == '&') {
                    pipe_dup_err = 1;
            if (strcmp(arg_array[lastArg], "|") == 0) {
                break;
            }
            if (strcmp(arg_array[lastArg], "|&") == 0) {
                pipe_dup_err = 1;
                }
                break;
            }
        }
        

        if (lastArg == firstArg) {
            Jim_SetResultString(interp, "missing command to exec", -1);
            goto error;
        }


        arg_array[lastArg] = NULL;
        if (lastArg == arg_count) {
            outputId = lastOutputId;
            lastOutputId = -1;
        }
        else {
            if (JimPipe(pipeIds) != 0) {
            if (pipe(pipeIds) != 0) {
                Jim_SetResultErrno(interp, "couldn't create pipe");
                goto error;
            }
            outputId = pipeIds[1];
        }

        

        if (pipe_dup_err) {
            errorId = outputId;
        }

        


#ifdef __MINGW32__
        pid = JimStartWinProcess(interp, &arg_array[firstArg], save_environ ? save_environ[0] : NULL, inputId, outputId, errorId);
        pid = JimStartWinProcess(interp, &arg_array[firstArg], save_environ, inputId, outputId, errorId);
        if (pid == JIM_BAD_PID) {
            Jim_SetResultFormatted(interp, "couldn't exec \"%s\"", arg_array[firstArg]);
            goto error;
        }
#else
        i = strlen(arg_array[firstArg]);

        child_environ = Jim_GetEnviron();
        pid = vfork();
        if (pid < 0) {
            Jim_SetResultErrno(interp, "couldn't fork child process");
            goto error;
        }
        if (pid == 0) {
            


            if (inputId != -1) dup2(inputId, 0);
            if (outputId != -1) dup2(outputId, 1);
            if (errorId != -1) dup2(errorId, 2);

            for (i = 3; (i <= outputId) || (i <= inputId) || (i <= errorId); i++) {
                close(i);
            if (inputId != -1) {
                dup2(inputId, fileno(stdin));
                close(inputId);
            }
            if (outputId != -1) {
                dup2(outputId, fileno(stdout));
                if (outputId != errorId) {
                    close(outputId);
                }
            }
            if (errorId != -1) {
                dup2(errorId, fileno(stderr));
                close(errorId);
            }

            if (outPipePtr) {
                close(*outPipePtr);
            }
            if (errFilePtr) {
                close(*errFilePtr);
            }
            if (pipeIds[0] != -1) {
                close(pipeIds[0]);
            }
            if (lastOutputId != -1) {
                close(lastOutputId);

            
            }


            (void)signal(SIGPIPE, SIG_DFL);

            execvpe(arg_array[firstArg], &arg_array[firstArg], Jim_GetEnviron());
            execvpe(arg_array[firstArg], &arg_array[firstArg], child_environ);

            if (write(fileno(stderr), "couldn't exec \"", 15) &&
                write(fileno(stderr), arg_array[firstArg], i) &&
                write(fileno(stderr), "\"\n", 2)) {
            
            fprintf(stderr, "couldn't exec \"%s\"\n", arg_array[firstArg]);

            }
#ifdef JIM_MAINTAINER
            {

                static char *const false_argv[2] = {"false", NULL};
                execvp(false_argv[0],false_argv);
            }
#endif
            _exit(127);
        }
#endif

        


        if (table->used == table->size) {
            table->size += WAIT_TABLE_GROW_BY;
            table->info = Jim_Realloc(table->info, table->size * sizeof(*table->info));
        }

        table->info[table->used].pid = pid;
        table->info[table->used].flags = 0;
        table->used++;

        pidPtr[numPids] = pid;

        

        errorId = origErrorId;


        if (inputId != JIM_BAD_FD) {
            JimCloseFd(inputId);
        if (inputId != -1) {
            close(inputId);
        }
        if (outputId != JIM_BAD_FD) {
            JimCloseFd(outputId);
        if (outputId != -1) {
            close(outputId);
        }
        inputId = pipeIds[0];
        pipeIds[0] = pipeIds[1] = JIM_BAD_FD;
        pipeIds[0] = pipeIds[1] = -1;
    }
    *pidArrayPtr = pidPtr;


  cleanup:
    if (inputId != JIM_BAD_FD) {
        JimCloseFd(inputId);
    if (inputId != -1) {
        close(inputId);
    }
    if (lastOutputId != JIM_BAD_FD) {
        JimCloseFd(lastOutputId);
    if (lastOutputId != -1) {
        close(lastOutputId);
    }
    if (errorId != JIM_BAD_FD) {
        JimCloseFd(errorId);
    if (errorId != -1) {
        close(errorId);
    }
    Jim_Free(arg_array);

    JimRestoreEnv(save_environ);

    return numPids;


  error:
    if ((inPipePtr != NULL) && (*inPipePtr != JIM_BAD_FD)) {
        JimCloseFd(*inPipePtr);
        *inPipePtr = JIM_BAD_FD;
    if ((inPipePtr != NULL) && (*inPipePtr != -1)) {
        close(*inPipePtr);
        *inPipePtr = -1;
    }
    if ((outPipePtr != NULL) && (*outPipePtr != JIM_BAD_FD)) {
        JimCloseFd(*outPipePtr);
        *outPipePtr = JIM_BAD_FD;
    if ((outPipePtr != NULL) && (*outPipePtr != -1)) {
        close(*outPipePtr);
        *outPipePtr = -1;
    }
    if ((errFilePtr != NULL) && (*errFilePtr != JIM_BAD_FD)) {
        JimCloseFd(*errFilePtr);
        *errFilePtr = JIM_BAD_FD;
    if ((errFilePtr != NULL) && (*errFilePtr != -1)) {
        close(*errFilePtr);
        *errFilePtr = -1;
    }
    if (pipeIds[0] != JIM_BAD_FD) {
        JimCloseFd(pipeIds[0]);
    if (pipeIds[0] != -1) {
        close(pipeIds[0]);
    }
    if (pipeIds[1] != JIM_BAD_FD) {
        JimCloseFd(pipeIds[1]);
    if (pipeIds[1] != -1) {
        close(pipeIds[1]);
    }
    if (pidPtr != NULL) {
        for (i = 0; i < numPids; i++) {
            if (pidPtr[i] != JIM_BAD_PID) {
                JimDetachPids(interp, 1, &pidPtr[i]);
                JimDetachPids(table, 1, &pidPtr[i]);
            }
        }
        Jim_Free(pidPtr);
    }
    numPids = -1;
    goto cleanup;
}


static int JimCleanupChildren(Jim_Interp *interp, int numPids, pidtype *pidPtr, Jim_Obj *errStrObj)
{
    struct WaitInfoTable *table = Jim_CmdPrivData(interp);
    int result = JIM_OK;
    int i;

    

    for (i = 0; i < numPids; i++) {
        int waitStatus = 0;
        if (JimWaitForProcess(table, pidPtr[i], &waitStatus) != JIM_BAD_PID) {
            if (JimCheckWaitStatus(interp, pidPtr[i], waitStatus, errStrObj) != JIM_OK) {
                result = JIM_ERR;
            }
        }
    }
    Jim_Free(pidPtr);

    return result;
}

int Jim_execInit(Jim_Interp *interp)
{
    struct WaitInfoTable *waitinfo;
    if (Jim_PackageProvide(interp, "exec", "1.0", JIM_ERRMSG))
        return JIM_ERR;

#ifdef SIGPIPE
    (void)signal(SIGPIPE, SIG_IGN);
#endif

    waitinfo = JimAllocWaitInfoTable();
    Jim_CreateCommand(interp, "exec", Jim_ExecCmd, JimAllocWaitInfoTable(), JimFreeWaitInfoTable);
    Jim_CreateCommand(interp, "exec", Jim_ExecCmd, waitinfo, JimFreeWaitInfoTable);
    waitinfo->refcount++;
    Jim_CreateCommand(interp, "wait", Jim_WaitCommand, waitinfo, JimFreeWaitInfoTable);
    Jim_CreateCommand(interp, "pid", Jim_PidCommand, 0, 0);

    return JIM_OK;
}

#if defined(__MINGW32__)


static SECURITY_ATTRIBUTES *JimStdSecAttrs(void)
{
    static SECURITY_ATTRIBUTES secAtts;

    secAtts.nLength = sizeof(SECURITY_ATTRIBUTES);
    secAtts.lpSecurityDescriptor = NULL;
    secAtts.bInheritHandle = TRUE;
    return &secAtts;
}

static int JimErrno(void)
{
    switch (GetLastError()) {
    case ERROR_FILE_NOT_FOUND: return ENOENT;
    case ERROR_PATH_NOT_FOUND: return ENOENT;
    case ERROR_TOO_MANY_OPEN_FILES: return EMFILE;
    case ERROR_ACCESS_DENIED: return EACCES;
    case ERROR_INVALID_HANDLE: return EBADF;
    case ERROR_BAD_ENVIRONMENT: return E2BIG;
    case ERROR_BAD_FORMAT: return ENOEXEC;
    case ERROR_INVALID_ACCESS: return EACCES;
    case ERROR_INVALID_DRIVE: return ENOENT;
    case ERROR_CURRENT_DIRECTORY: return EACCES;
    case ERROR_NOT_SAME_DEVICE: return EXDEV;
    case ERROR_NO_MORE_FILES: return ENOENT;
    case ERROR_WRITE_PROTECT: return EROFS;
    case ERROR_BAD_UNIT: return ENXIO;
    case ERROR_NOT_READY: return EBUSY;
    case ERROR_BAD_COMMAND: return EIO;
    case ERROR_CRC: return EIO;
    case ERROR_BAD_LENGTH: return EIO;
    case ERROR_SEEK: return EIO;
    case ERROR_WRITE_FAULT: return EIO;
    case ERROR_READ_FAULT: return EIO;
    case ERROR_GEN_FAILURE: return EIO;
    case ERROR_SHARING_VIOLATION: return EACCES;
    case ERROR_LOCK_VIOLATION: return EACCES;
    case ERROR_SHARING_BUFFER_EXCEEDED: return ENFILE;
    case ERROR_HANDLE_DISK_FULL: return ENOSPC;
    case ERROR_NOT_SUPPORTED: return ENODEV;
    case ERROR_REM_NOT_LIST: return EBUSY;
    case ERROR_DUP_NAME: return EEXIST;
    case ERROR_BAD_NETPATH: return ENOENT;
    case ERROR_NETWORK_BUSY: return EBUSY;
    case ERROR_DEV_NOT_EXIST: return ENODEV;
    case ERROR_TOO_MANY_CMDS: return EAGAIN;
    case ERROR_ADAP_HDW_ERR: return EIO;
    case ERROR_BAD_NET_RESP: return EIO;
    case ERROR_UNEXP_NET_ERR: return EIO;
    case ERROR_NETNAME_DELETED: return ENOENT;
    case ERROR_NETWORK_ACCESS_DENIED: return EACCES;
    case ERROR_BAD_DEV_TYPE: return ENODEV;
    case ERROR_BAD_NET_NAME: return ENOENT;
    case ERROR_TOO_MANY_NAMES: return ENFILE;
    case ERROR_TOO_MANY_SESS: return EIO;
    case ERROR_SHARING_PAUSED: return EAGAIN;
    case ERROR_REDIR_PAUSED: return EAGAIN;
    case ERROR_FILE_EXISTS: return EEXIST;
    case ERROR_CANNOT_MAKE: return ENOSPC;
    case ERROR_OUT_OF_STRUCTURES: return ENFILE;
    case ERROR_ALREADY_ASSIGNED: return EEXIST;
    case ERROR_INVALID_PASSWORD: return EPERM;
    case ERROR_NET_WRITE_FAULT: return EIO;
    case ERROR_NO_PROC_SLOTS: return EAGAIN;
    case ERROR_DISK_CHANGE: return EXDEV;
    case ERROR_BROKEN_PIPE: return EPIPE;
    case ERROR_OPEN_FAILED: return ENOENT;
    case ERROR_DISK_FULL: return ENOSPC;
    case ERROR_NO_MORE_SEARCH_HANDLES: return EMFILE;
    case ERROR_INVALID_TARGET_HANDLE: return EBADF;
    case ERROR_INVALID_NAME: return ENOENT;
    case ERROR_PROC_NOT_FOUND: return ESRCH;
    case ERROR_WAIT_NO_CHILDREN: return ECHILD;
    case ERROR_CHILD_NOT_COMPLETE: return ECHILD;
    case ERROR_DIRECT_ACCESS_HANDLE: return EBADF;
    case ERROR_SEEK_ON_DEVICE: return ESPIPE;
    case ERROR_BUSY_DRIVE: return EAGAIN;
    case ERROR_DIR_NOT_EMPTY: return EEXIST;
    case ERROR_NOT_LOCKED: return EACCES;
    case ERROR_BAD_PATHNAME: return ENOENT;
    case ERROR_LOCK_FAILED: return EACCES;
    case ERROR_ALREADY_EXISTS: return EEXIST;
    case ERROR_FILENAME_EXCED_RANGE: return ENAMETOOLONG;
    case ERROR_BAD_PIPE: return EPIPE;
    case ERROR_PIPE_BUSY: return EAGAIN;
    case ERROR_PIPE_NOT_CONNECTED: return EPIPE;
    case ERROR_DIRECTORY: return ENOTDIR;
    }
    return EINVAL;
}

static int JimPipe(fdtype pipefd[2])
{
    if (CreatePipe(&pipefd[0], &pipefd[1], NULL, 0)) {
        return 0;
    }
    return -1;
}

static fdtype JimDupFd(fdtype infd)
{
    fdtype dupfd;
    pidtype pid = GetCurrentProcess();

    if (DuplicateHandle(pid, infd, pid, &dupfd, 0, TRUE, DUPLICATE_SAME_ACCESS)) {
        return dupfd;
    }
    return JIM_BAD_FD;
}

static int JimRewindFd(fdtype fd)
{
    return SetFilePointer(fd, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER ? -1 : 0;
}

#if 0
static int JimReadFd(fdtype fd, char *buffer, size_t len)
{
    DWORD num;

    if (ReadFile(fd, buffer, len, &num, NULL)) {
        return num;
    }
    if (GetLastError() == ERROR_HANDLE_EOF || GetLastError() == ERROR_BROKEN_PIPE) {
        return 0;
    }
    return -1;
}
#endif

static FILE *JimFdOpenForRead(fdtype fd)
{
    return _fdopen(_open_osfhandle((int)fd, _O_RDONLY | _O_TEXT), "r");
}

static fdtype JimFileno(FILE *fh)
{
    return (fdtype)_get_osfhandle(_fileno(fh));
}

static fdtype JimOpenForRead(const char *filename)
{
    return CreateFile(filename, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
        JimStdSecAttrs(), OPEN_EXISTING, 0, NULL);
}

static fdtype JimOpenForWrite(const char *filename, int append)
{
    return CreateFile(filename, append ? FILE_APPEND_DATA : GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
        JimStdSecAttrs(), append ? OPEN_ALWAYS : CREATE_ALWAYS, 0, (HANDLE) NULL);
}

static FILE *JimFdOpenForWrite(fdtype fd)
{
    return _fdopen(_open_osfhandle((int)fd, _O_TEXT), "w");
}

static pidtype JimWaitPid(pidtype pid, int *status, int nohang)
{
    DWORD ret = WaitForSingleObject(pid, nohang ? 0 : INFINITE);
    if (ret == WAIT_TIMEOUT || ret == WAIT_FAILED) {
        
        return JIM_BAD_PID;
    }
    GetExitCodeProcess(pid, &ret);
    *status = ret;
    CloseHandle(pid);
    return pid;
}

static HANDLE JimCreateTemp(Jim_Interp *interp, const char *contents, int len)
{
    char name[MAX_PATH];
    HANDLE handle;

    if (!GetTempPath(MAX_PATH, name) || !GetTempFileName(name, "JIM", 0, name)) {
        return JIM_BAD_FD;
    }

    handle = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, JimStdSecAttrs(),
            CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE,
            NULL);

    if (handle == INVALID_HANDLE_VALUE) {
        goto error;
    }

    if (contents != NULL) {
        
        FILE *fh = JimFdOpenForWrite(JimDupFd(handle));
        if (fh == NULL) {
            goto error;
        }

        if (fwrite(contents, len, 1, fh) != 1) {
            fclose(fh);
            goto error;
        }
        fseek(fh, 0, SEEK_SET);
        fclose(fh);
    }
    return handle;

  error:
    Jim_SetResultErrno(interp, "failed to create temp file");
    CloseHandle(handle);
    DeleteFile(name);
    return JIM_BAD_FD;
}

static int
JimWinFindExecutable(const char *originalName, char fullPath[MAX_PATH])
{
    int i;
    static char extensions[][5] = {".exe", "", ".bat"};

    for (i = 0; i < (int) (sizeof(extensions) / sizeof(extensions[0])); i++) {
5379
5380
5381
5382
5383
5384
5385





5386
5387
5388
5389
5390
5391
5392
5403
5404
5405
5406
5407
5408
5409
5410
5411
5412
5413
5414
5415
5416
5417
5418
5419
5420
5421







+
+
+
+
+







    return env;
}

static void JimRestoreEnv(char **env)
{
    JimFreeEnv(env, Jim_GetEnviron());
}

static char **JimOriginalEnviron(void)
{
    return NULL;
}

static Jim_Obj *
JimWinBuildCommandLine(Jim_Interp *interp, char **argv)
{
    char *start, *special;
    int quote, i;

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
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







-
+



-
+



+

















-
+
-
-
-
+
+
-
-
-
+
+
-
-
+



-
-
+
+
-
-
-
-
+
+
+
-
-
+


+
+
+
-
+
-
-
+
-
-
-
-
-
+
+
-
-
+



+
+
+
+
+
+
+
+
+
+
+

-
+











-
+


-
+


-
+




-

-
-
-
-
-
+
-
-
+
-
-
-
+
-

-
-
-
-
-
-
-
-
-
+
-
-
-
-
-


















+
+



+
+
+
+
+
+











+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


-
+


-
+
-
-
+
+

-
-
+
+

-
-
+
-
+

-
+
-


+

+

-
-
+
+









+
+
+
+
+
+
+
+
+
+
+
+
+
+




-
+

+
-
+
+
+
+
+
+
+
+
+



-


-
+





-
-
+
+







            Jim_AppendString(interp, strObj, "\"", 1);
        }
    }
    return strObj;
}

static pidtype
JimStartWinProcess(Jim_Interp *interp, char **argv, char *env, fdtype inputId, fdtype outputId, fdtype errorId)
JimStartWinProcess(Jim_Interp *interp, char **argv, char **env, int inputId, int outputId, int errorId)
{
    STARTUPINFO startInfo;
    PROCESS_INFORMATION procInfo;
    HANDLE hProcess, h;
    HANDLE hProcess;
    char execPath[MAX_PATH];
    pidtype pid = JIM_BAD_PID;
    Jim_Obj *cmdLineObj;
    char *winenv;

    if (JimWinFindExecutable(argv[0], execPath) < 0) {
        return JIM_BAD_PID;
    }
    argv[0] = execPath;

    hProcess = GetCurrentProcess();
    cmdLineObj = JimWinBuildCommandLine(interp, argv);


    ZeroMemory(&startInfo, sizeof(startInfo));
    startInfo.cb = sizeof(startInfo);
    startInfo.dwFlags   = STARTF_USESTDHANDLES;
    startInfo.hStdInput = INVALID_HANDLE_VALUE;
    startInfo.hStdOutput= INVALID_HANDLE_VALUE;
    startInfo.hStdError = INVALID_HANDLE_VALUE;

    if (inputId == JIM_BAD_FD) {
    if (inputId == -1) {
        if (CreatePipe(&startInfo.hStdInput, &h, JimStdSecAttrs(), 0) != FALSE) {
            CloseHandle(h);
        }
        inputId = _fileno(stdin);
    }
    } else {
        DuplicateHandle(hProcess, inputId, hProcess, &startInfo.hStdInput,
                0, TRUE, DUPLICATE_SAME_ACCESS);
    DuplicateHandle(hProcess, (HANDLE)_get_osfhandle(inputId), hProcess, &startInfo.hStdInput,
            0, TRUE, DUPLICATE_SAME_ACCESS);
    }
    if (startInfo.hStdInput == JIM_BAD_FD) {
    if (startInfo.hStdInput == INVALID_HANDLE_VALUE) {
        goto end;
    }

    if (outputId == JIM_BAD_FD) {
        startInfo.hStdOutput = CreateFile("NUL:", GENERIC_WRITE, 0,
    if (outputId == -1) {
        outputId = _fileno(stdout);
                JimStdSecAttrs(), OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    } else {
        DuplicateHandle(hProcess, outputId, hProcess, &startInfo.hStdOutput,
                0, TRUE, DUPLICATE_SAME_ACCESS);
    }
    DuplicateHandle(hProcess, (HANDLE)_get_osfhandle(outputId), hProcess, &startInfo.hStdOutput,
            0, TRUE, DUPLICATE_SAME_ACCESS);
    }
    if (startInfo.hStdOutput == JIM_BAD_FD) {
    if (startInfo.hStdOutput == INVALID_HANDLE_VALUE) {
        goto end;
    }


    if (errorId == -1) {

        errorId = _fileno(stderr);
    if (errorId == JIM_BAD_FD) {

    }
        startInfo.hStdError = CreateFile("NUL:", GENERIC_WRITE, 0,
                JimStdSecAttrs(), OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    } else {
        DuplicateHandle(hProcess, errorId, hProcess, &startInfo.hStdError,
                0, TRUE, DUPLICATE_SAME_ACCESS);
    DuplicateHandle(hProcess, (HANDLE)_get_osfhandle(errorId), hProcess, &startInfo.hStdError,
            0, TRUE, DUPLICATE_SAME_ACCESS);
    }
    if (startInfo.hStdError == JIM_BAD_FD) {
    if (startInfo.hStdError == INVALID_HANDLE_VALUE) {
        goto end;
    }

    if (env == NULL) {

        winenv = NULL;
    }
    else if (env[0] == NULL) {
        winenv = (char *)"\0";
    }
    else {
        winenv = env[0];
    }

    if (!CreateProcess(NULL, (char *)Jim_String(cmdLineObj), NULL, NULL, TRUE,
            0, env, NULL, &startInfo, &procInfo)) {
            0, winenv, NULL, &startInfo, &procInfo)) {
        goto end;
    }


    WaitForInputIdle(procInfo.hProcess, 5000);
    CloseHandle(procInfo.hThread);

    pid = procInfo.hProcess;

    end:
    Jim_FreeNewObj(interp, cmdLineObj);
    if (startInfo.hStdInput != JIM_BAD_FD) {
    if (startInfo.hStdInput != INVALID_HANDLE_VALUE) {
        CloseHandle(startInfo.hStdInput);
    }
    if (startInfo.hStdOutput != JIM_BAD_FD) {
    if (startInfo.hStdOutput != INVALID_HANDLE_VALUE) {
        CloseHandle(startInfo.hStdOutput);
    }
    if (startInfo.hStdError != JIM_BAD_FD) {
    if (startInfo.hStdError != INVALID_HANDLE_VALUE) {
        CloseHandle(startInfo.hStdError);
    }
    return pid;
}
#else

static int JimOpenForWrite(const char *filename, int append)
{
    return open(filename, O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC), 0666);
}

#else
static int JimRewindFd(int fd)
{

    return lseek(fd, 0L, SEEK_SET);
}

static char **JimOriginalEnviron(void)
static int JimCreateTemp(Jim_Interp *interp, const char *contents, int len)
{
    int fd = Jim_MakeTempFile(interp, NULL);

    if (fd != JIM_BAD_FD) {
        unlink(Jim_String(Jim_GetResult(interp)));
        if (contents) {
            if (write(fd, contents, len) != len) {
                Jim_SetResultErrno(interp, "couldn't write temp file");
                close(fd);
                return -1;
    return Jim_GetEnviron();
            }
            lseek(fd, 0L, SEEK_SET);
        }
    }
    return fd;
}

static char **JimSaveEnv(char **env)
{
    char **saveenv = Jim_GetEnviron();
    Jim_SetEnviron(env);
    return saveenv;
}

static void JimRestoreEnv(char **env)
{
    JimFreeEnv(Jim_GetEnviron(), env);
    Jim_SetEnviron(env);
}
#endif
#endif



#ifdef STRPTIME_NEEDS_XOPEN_SOURCE
#ifndef _XOPEN_SOURCE
#define _XOPEN_SOURCE 500
#endif
#endif


#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <time.h>


#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif

struct clock_options {
    int gmt;
    const char *format;
};

static int parse_clock_options(Jim_Interp *interp, int argc, Jim_Obj *const *argv, struct clock_options *opts)
{
    static const char * const options[] = { "-gmt", "-format", NULL };
    enum { OPT_GMT, OPT_FORMAT, };
    int i;

    for (i = 0; i < argc; i += 2) {
        int option;
        if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
            return JIM_ERR;
        }
        switch (option) {
            case OPT_GMT:
                if (Jim_GetBoolean(interp, argv[i + 1], &opts->gmt) != JIM_OK) {
                    return JIM_ERR;
                }
                break;
            case OPT_FORMAT:
                opts->format = Jim_String(argv[i + 1]);
                break;
        }
    }
    return JIM_OK;
}

static int clock_cmd_format(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
    

    char buf[100];
    time_t t;
    long seconds;
    jim_wide seconds;

    const char *format = "%a %b %d %H:%M:%S %Z %Y";
    struct clock_options options = { 0, "%a %b %d %H:%M:%S %Z %Y" };
    struct tm *tm;

    if (argc == 2 || (argc == 3 && !Jim_CompareStringImmediate(interp, argv[1], "-format"))) {
        return -1;
    if (Jim_GetWide(interp, argv[0], &seconds) != JIM_OK) {
        return JIM_ERR;
    }

    if (argc == 3) {
    if (argc % 2 == 0) {
        format = Jim_String(argv[2]);
        return -1;
    }

    if (parse_clock_options(interp, argc - 1, argv + 1, &options) == JIM_ERR) {
    if (Jim_GetLong(interp, argv[0], &seconds) != JIM_OK) {
        return JIM_ERR;
    }

    t = seconds;
    tm = options.gmt ? gmtime(&t) : localtime(&t);

    if (strftime(buf, sizeof(buf), format, localtime(&t)) == 0) {
        Jim_SetResultString(interp, "format string too long", -1);
    if (tm == NULL || strftime(buf, sizeof(buf), options.format, tm) == 0) {
        Jim_SetResultString(interp, "format string too long or invalid time", -1);
        return JIM_ERR;
    }

    Jim_SetResultString(interp, buf, -1);

    return JIM_OK;
}

#ifdef HAVE_STRPTIME
static time_t jim_timegm(const struct tm *tm)
{
    int m = tm->tm_mon + 1;
    int y = 1900 + tm->tm_year - (m <= 2);
    int era = (y >= 0 ? y : y - 399) / 400;
    unsigned yoe = (unsigned)(y - era * 400);
    unsigned doy = (153 * (m + (m > 2 ? -3 : 9)) + 2) / 5 + tm->tm_mday - 1;
    unsigned doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
    long days = (era * 146097 + (int)doe - 719468);
    int secs = tm->tm_hour * 3600 + tm->tm_min * 60 + tm->tm_sec;

    return days * 24 * 60 * 60 + secs;
}

static int clock_cmd_scan(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
    char *pt;
    struct tm tm;
    time_t now = time(0);
    time_t now = time(NULL);

    struct clock_options options = { 0, NULL };
    if (!Jim_CompareStringImmediate(interp, argv[1], "-format")) {

    if (argc % 2 == 0) {
        return -1;
    }

    if (parse_clock_options(interp, argc - 1, argv + 1, &options) == JIM_ERR) {
        return JIM_ERR;
    }
    if (options.format == NULL) {
        return -1;
    }

    
    localtime_r(&now, &tm);

    pt = strptime(Jim_String(argv[0]), Jim_String(argv[2]), &tm);
    pt = strptime(Jim_String(argv[0]), options.format, &tm);
    if (pt == 0 || *pt != 0) {
        Jim_SetResultString(interp, "Failed to parse time according to format", -1);
        return JIM_ERR;
    }

    
    Jim_SetResultInt(interp, mktime(&tm));

    Jim_SetResultInt(interp, options.gmt ? jim_timegm(&tm) : mktime(&tm));

    return JIM_OK;
}
#endif

static int clock_cmd_seconds(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
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
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







-
-
-
-
-
-
-





-
+
+
+
+
+
+
+
+






-
+






-
+
-
-
-
-
-
-
-



-
+


-
-
+
+


+
+
+
+
+
+
+








    Jim_SetResultInt(interp, (jim_wide) tv.tv_sec * 1000 + tv.tv_usec / 1000);

    return JIM_OK;
}

static const jim_subcmd_type clock_command_table[] = {
    {   "seconds",
        NULL,
        clock_cmd_seconds,
        0,
        0,
        
    },
    {   "clicks",
        NULL,
        clock_cmd_micros,
        0,
        0,
        

    },
    {   "format",
        "seconds ?-format string? ?-gmt boolean?",
        clock_cmd_format,
        1,
        5,

    },
    {   "microseconds",
        NULL,
        clock_cmd_micros,
        0,
        0,
        

    },
    {   "milliseconds",
        NULL,
        clock_cmd_millis,
        0,
        0,
        

    },
    {   "format",
        "seconds ?-format format?",
        clock_cmd_format,
        1,
        3,
        
    },
#ifdef HAVE_STRPTIME
    {   "scan",
        "str -format format",
        "str -format format ?-gmt boolean?",
        clock_cmd_scan,
        3,
        3,
        
        5,

    },
#endif
    {   "seconds",
        NULL,
        clock_cmd_seconds,
        0,
        0,

    },
    { NULL }
};

int Jim_clockInit(Jim_Interp *interp)
{
    if (Jim_PackageProvide(interp, "clock", "1.0", JIM_ERRMSG))
        return JIM_ERR;
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
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







-
-
+
+
+














-
+


-
+





-
-
+










-
+











-
+







-
+




+
+
-
+


-
+


















-
+




+
+
-
+







#include <string.h>
#include <stdio.h>
#include <errno.h>


static int array_cmd_exists(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
    
    Jim_SetResultInt(interp, Jim_GetVariable(interp, argv[0], 0) != 0);

    Jim_Obj *dictObj = Jim_GetVariable(interp, argv[0], JIM_UNSHARED);
    Jim_SetResultInt(interp, dictObj && Jim_DictSize(interp, dictObj) != -1);
    return JIM_OK;
}

static int array_cmd_get(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
    Jim_Obj *objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
    Jim_Obj *patternObj;

    if (!objPtr) {
        return JIM_OK;
    }

    patternObj = (argc == 1) ? NULL : argv[1];

    

    if (patternObj == NULL || Jim_CompareStringImmediate(interp, patternObj, "*")) {
        if (Jim_IsList(objPtr) && Jim_ListLength(interp, objPtr) % 2 == 0) {
            

            Jim_SetResult(interp, objPtr);
            return JIM_OK;
        }
    }

    
    return Jim_DictValues(interp, objPtr, patternObj);
    return Jim_DictMatchTypes(interp, objPtr, patternObj, JIM_DICTMATCH_KEYS, JIM_DICTMATCH_KEYS | JIM_DICTMATCH_VALUES);
}

static int array_cmd_names(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
    Jim_Obj *objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);

    if (!objPtr) {
        return JIM_OK;
    }

    return Jim_DictKeys(interp, objPtr, argc == 1 ? NULL : argv[1]);
    return Jim_DictMatchTypes(interp, objPtr, argc == 1 ? NULL : argv[1], JIM_DICTMATCH_KEYS, JIM_DICTMATCH_KEYS);
}

static int array_cmd_unset(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
    int i;
    int len;
    Jim_Obj *resultObj;
    Jim_Obj *objPtr;
    Jim_Obj **dictValuesObj;

    if (argc == 1 || Jim_CompareStringImmediate(interp, argv[1], "*")) {
        

        Jim_UnsetVariable(interp, argv[0], JIM_NONE);
        return JIM_OK;
    }

    objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);

    if (objPtr == NULL) {
        

        return JIM_OK;
    }

    if (Jim_DictPairs(interp, objPtr, &dictValuesObj, &len) != JIM_OK) {

        Jim_SetResultString(interp, "", -1);
        return JIM_ERR;
        return JIM_OK;
    }

    

    resultObj = Jim_NewDictObj(interp, NULL, 0);

    for (i = 0; i < len; i += 2) {
        if (!Jim_StringMatchObj(interp, argv[1], dictValuesObj[i], 0)) {
            Jim_DictAddElement(interp, resultObj, dictValuesObj[i], dictValuesObj[i + 1]);
        }
    }
    Jim_Free(dictValuesObj);

    Jim_SetVariable(interp, argv[0], resultObj);
    return JIM_OK;
}

static int array_cmd_size(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
    Jim_Obj *objPtr;
    int len = 0;

    

    objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
    if (objPtr) {
        len = Jim_DictSize(interp, objPtr);
        if (len < 0) {

            Jim_SetResultInt(interp, 0);
            return JIM_ERR;
            return JIM_OK;
        }
    }

    Jim_SetResultInt(interp, len);

    return JIM_OK;
}
5874
5875
5876
5877
5878
5879
5880
5881

5882
5883
5884
5885
5886
5887
5888
5950
5951
5952
5953
5954
5955
5956

5957
5958
5959
5960
5961
5962
5963
5964







-
+







    if (len % 2) {
        Jim_SetResultString(interp, "list must have an even number of elements", -1);
        return JIM_ERR;
    }

    dictObj = Jim_GetVariable(interp, argv[0], JIM_UNSHARED);
    if (!dictObj) {
        

        return Jim_SetVariable(interp, argv[0], listObj);
    }
    else if (Jim_DictSize(interp, dictObj) < 0) {
        return JIM_ERR;
    }

    if (Jim_IsShared(dictObj)) {
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
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







-
+






-
+






-
+






-
+






-
+






-
+






-
+








static const jim_subcmd_type array_command_table[] = {
        {       "exists",
                "arrayName",
                array_cmd_exists,
                1,
                1,
                

        },
        {       "get",
                "arrayName ?pattern?",
                array_cmd_get,
                1,
                2,
                

        },
        {       "names",
                "arrayName ?pattern?",
                array_cmd_names,
                1,
                2,
                

        },
        {       "set",
                "arrayName list",
                array_cmd_set,
                2,
                2,
                

        },
        {       "size",
                "arrayName",
                array_cmd_size,
                1,
                1,
                

        },
        {       "stat",
                "arrayName",
                array_cmd_stat,
                1,
                1,
                

        },
        {       "unset",
                "arrayName ?pattern?",
                array_cmd_unset,
                1,
                2,
                

        },
        {       NULL
        }
};

int Jim_arrayInit(Jim_Interp *interp)
{
5985
5986
5987
5988
5989
5990
5991
5992




5993
5994
5995
5996
5997
5998
5999
6061
6062
6063
6064
6065
6066
6067

6068
6069
6070
6071
6072
6073
6074
6075
6076
6077
6078







-
+
+
+
+







Jim_execInit(interp);
Jim_clockInit(interp);
Jim_arrayInit(interp);
Jim_stdlibInit(interp);
Jim_tclcompatInit(interp);
return JIM_OK;
}
#define JIM_OPTIMIZATION        
#define JIM_OPTIMIZATION
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

#include <stdio.h>
#include <stdlib.h>

#include <string.h>
#include <stdarg.h>
#include <ctype.h>
6054
6055
6056
6057
6058
6059
6060






6061
6062
6063
6064
6065
6066
6067
6133
6134
6135
6136
6137
6138
6139
6140
6141
6142
6143
6144
6145
6146
6147
6148
6149
6150
6151
6152







+
+
+
+
+
+







#ifdef JIM_DEBUG_PANIC
static void JimPanicDump(int fail_condition, const char *fmt, ...);
#define JimPanic(X) JimPanicDump X
#else
#define JimPanic(X)
#endif

#ifdef JIM_OPTIMIZATION
#define JIM_IF_OPTIM(X) X
#else
#define JIM_IF_OPTIM(X)
#endif


static char JimEmptyStringRep[] = "";

static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int action);
static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int listindex, Jim_Obj *newObjPtr,
    int flags);
static int JimDeleteLocalProcs(Jim_Interp *interp, Jim_Stack *localCommands);
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
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







-
+






-
+





-
+





-
-
+
+


-
+








    if (flags & JIM_CHARSET_SCAN) {
        if (*pattern == '^') {
            not++;
            pattern++;
        }

        

        if (*pattern == ']') {
            goto first;
        }
    }

    while (*pattern && *pattern != ']') {
        

        if (pattern[0] == '\\') {
first:
            pattern += utf8_tounicode_case(pattern, &pchar, nocase);
        }
        else {
            

            int start;
            int end;

            pattern += utf8_tounicode_case(pattern, &start, nocase);
            if (pattern[0] == '-' && pattern[1]) {
                
                pattern += utf8_tounicode(pattern, &pchar);

                pattern++;
                pattern += utf8_tounicode_case(pattern, &end, nocase);

                

                if ((c >= start && c <= end) || (c >= end && c <= start)) {
                    match = 1;
                }
                continue;
            }
            pchar = start;
        }
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
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







-
+


-
+

-
+


-
+












-
+








-
+







        switch (pattern[0]) {
            case '*':
                while (pattern[1] == '*') {
                    pattern++;
                }
                pattern++;
                if (!pattern[0]) {
                    return 1;   
                    return 1;
                }
                while (*string) {
                    

                    if (JimGlobMatch(pattern, string, nocase))
                        return 1;       
                        return 1;
                    string += utf8_tounicode(string, &c);
                }
                return 0;       
                return 0;

            case '?':
                string += utf8_tounicode(string, &c);
                break;

            case '[': {
                    string += utf8_tounicode(string, &c);
                    pattern = JimCharsetMatch(pattern + 1, c, nocase ? JIM_NOCASE : 0);
                    if (!pattern) {
                        return 0;
                    }
                    if (!*pattern) {
                        

                        continue;
                    }
                    break;
                }
            case '\\':
                if (pattern[1]) {
                    pattern++;
                }
                

            default:
                string += utf8_tounicode_case(string, &c, nocase);
                utf8_tounicode_case(pattern, &pchar, nocase);
                if (pchar != c) {
                    return 0;
                }
                break;
6247
6248
6249
6250
6251
6252
6253
6254

6255
6256
6257
6258
6259
6260
6261
6332
6333
6334
6335
6336
6337
6338

6339
6340
6341
6342
6343
6344
6345
6346







-
+







            return JimSign(c1 - c2);
        }
        maxchars--;
    }
    if (!maxchars) {
        return 0;
    }
    

    if (*s1) {
        return 1;
    }
    if (*s2) {
        return -1;
    }
    return 0;
6288
6289
6290
6291
6292
6293
6294
6295

6296
6297
6298
6299
6300
6301
6302
6373
6374
6375
6376
6377
6378
6379

6380
6381
6382
6383
6384
6385
6386
6387







-
+







static int JimStringLast(const char *s1, int l1, const char *s2, int l2)
{
    const char *p;

    if (!l1 || !l2 || l1 > l2)
        return -1;

    

    for (p = s2 + l2 - 1; p != s2 - 1; p--) {
        if (*p == *s1 && memcmp(s1, p, l1) == 0) {
            return p - s2;
        }
    }
    return -1;
}
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
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







-
+



-
+







-
+

-
+


-
+

















-
+


















-
+







        if (str[i] == '+') {
            i++;
        }
        *sign = 1;
    }

    if (str[i] != '0') {
        

        return 0;
    }

    

    switch (str[i + 1]) {
        case 'x': case 'X': *base = 16; break;
        case 'o': case 'O': *base = 8; break;
        case 'b': case 'B': *base = 2; break;
        default: return 0;
    }
    i += 2;
    

    if (str[i] != '-' && str[i] != '+' && !isspace(UCHAR(str[i]))) {
        

        return i;
    }
    

    *base = 10;
    return 0;
}

static long jim_strtol(const char *str, char **endptr)
{
    int sign;
    int base;
    int i = JimNumberBase(str, &base, &sign);

    if (base != 10) {
        long value = strtol(str + i, endptr, base);
        if (endptr == NULL || *endptr != str + i) {
            return value * sign;
        }
    }

    

    return strtol(str, endptr, 10);
}


static jim_wide jim_strtoull(const char *str, char **endptr)
{
#ifdef HAVE_LONG_LONG
    int sign;
    int base;
    int i = JimNumberBase(str, &base, &sign);

    if (base != 10) {
        jim_wide value = strtoull(str + i, endptr, base);
        if (endptr == NULL || *endptr != str + i) {
            return value * sign;
        }
    }

    

    return strtoull(str, endptr, 10);
#else
    return (unsigned long)jim_strtol(str, endptr);
#endif
}

int Jim_StringToWide(const char *str, jim_wide * widePtr, int base)
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
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







-
+









-
+

+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+







    return JimCheckConversion(str, endptr);
}

int Jim_StringToDouble(const char *str, double *doublePtr)
{
    char *endptr;

    

    errno = 0;

    *doublePtr = strtod(str, &endptr);

    return JimCheckConversion(str, endptr);
}

static jim_wide JimPowWide(jim_wide b, jim_wide e)
{
    jim_wide i, res = 1;
    jim_wide res = 1;


    if (b == 1) {

        return 1;
    }
    if ((b == 0 && e != 0) || (e < 0))
        return 0;
    for (i = 0; i < e; i++) {
        res *= b;
    if (e < 0) {
        if (b != -1) {
            return 0;
        }
        e = -e;
    }
    while (e)
    {
        if (e & 1) {
            res *= b;
        }
        e >>= 1;
        b *= b;
    }
    return res;
}

#ifdef JIM_DEBUG_PANIC
static void JimPanicDump(int condition, const char *fmt, ...)
{
6507
6508
6509
6510
6511
6512
6513
6514

6515
6516
6517
6518
6519
6520
6521
6606
6607
6608
6609
6610
6611
6612

6613
6614
6615
6616
6617
6618
6619
6620







-
+







}

char *Jim_StrDupLen(const char *s, int l)
{
    char *copy = Jim_Alloc(l + 1);

    memcpy(copy, s, l + 1);
    copy[l] = 0;                
    copy[l] = 0;
    return copy;
}



static jim_wide JimClock(void)
{
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
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







-
+









-
+


-
+









-
+





-
+




-
+






-
+












-
+







        minimal = JIM_HT_INITIAL_SIZE;
    Jim_ExpandHashTable(ht, minimal);
}


void Jim_ExpandHashTable(Jim_HashTable *ht, unsigned int size)
{
    Jim_HashTable n;            
    Jim_HashTable n;
    unsigned int realsize = JimHashTableNextPower(size), i;

     if (size <= ht->used)
        return;

    Jim_InitHashTable(&n, ht->type, ht->privdata);
    n.size = realsize;
    n.sizemask = realsize - 1;
    n.table = Jim_Alloc(realsize * sizeof(Jim_HashEntry *));
    

    n.uniq = ht->uniq;

    

    memset(n.table, 0, realsize * sizeof(Jim_HashEntry *));

    n.used = ht->used;
    for (i = 0; ht->used > 0; i++) {
        Jim_HashEntry *he, *nextHe;

        if (ht->table[i] == NULL)
            continue;

        

        he = ht->table[i];
        while (he) {
            unsigned int h;

            nextHe = he->next;
            

            h = Jim_HashKey(ht, he->key) & n.sizemask;
            he->next = n.table[h];
            n.table[h] = he;
            ht->used--;
            

            he = nextHe;
        }
    }
    assert(ht->used == 0);
    Jim_Free(ht->table);

    

    *ht = n;
}


int Jim_AddHashEntry(Jim_HashTable *ht, const void *key, void *val)
{
    Jim_HashEntry *entry;

    entry = JimInsertHashEntry(ht, key, 0);
    if (entry == NULL)
        return JIM_ERR;

    

    Jim_SetHashKey(ht, entry, key);
    Jim_SetHashVal(ht, entry, val);
    return JIM_OK;
}


int Jim_ReplaceHashEntry(Jim_HashTable *ht, const void *key, void *val)
6676
6677
6678
6679
6680
6681
6682
6683

6684
6685
6686
6687
6688
6689
6690
6775
6776
6777
6778
6779
6780
6781

6782
6783
6784
6785
6786
6787
6788
6789







-
+







        else {
            Jim_FreeEntryVal(ht, entry);
            Jim_SetHashVal(ht, entry, val);
        }
        existed = 1;
    }
    else {
        

        Jim_SetHashKey(ht, entry, key);
        Jim_SetHashVal(ht, entry, val);
        existed = 0;
    }

    return existed;
}
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
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







-
+













-
+







-
+














-
+

-
+

-
+







        return JIM_ERR;
    h = Jim_HashKey(ht, key) & ht->sizemask;
    he = ht->table[h];

    prevHe = NULL;
    while (he) {
        if (Jim_CompareHashKeys(ht, key, he->key)) {
            

            if (prevHe)
                prevHe->next = he->next;
            else
                ht->table[h] = he->next;
            Jim_FreeEntryKey(ht, he);
            Jim_FreeEntryVal(ht, he);
            Jim_Free(he);
            ht->used--;
            return JIM_OK;
        }
        prevHe = he;
        he = he->next;
    }
    return JIM_ERR;             
    return JIM_ERR;
}


int Jim_FreeHashTable(Jim_HashTable *ht)
{
    unsigned int i;

    

    for (i = 0; ht->used > 0; i++) {
        Jim_HashEntry *he, *nextHe;

        if ((he = ht->table[i]) == NULL)
            continue;
        while (he) {
            nextHe = he->next;
            Jim_FreeEntryKey(ht, he);
            Jim_FreeEntryVal(ht, he);
            Jim_Free(he);
            ht->used--;
            he = nextHe;
        }
    }
    

    Jim_Free(ht->table);
    

    JimResetHashTable(ht);
    return JIM_OK;              
    return JIM_OK;
}

Jim_HashEntry *Jim_FindHashEntry(Jim_HashTable *ht, const void *key)
{
    Jim_HashEntry *he;
    unsigned int h;

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
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







-
+


-
+

-
+







-
+







}

static Jim_HashEntry *JimInsertHashEntry(Jim_HashTable *ht, const void *key, int replace)
{
    unsigned int h;
    Jim_HashEntry *he;

    

    JimExpandHashTableIfNeeded(ht);

    

    h = Jim_HashKey(ht, key) & ht->sizemask;
    

    he = ht->table[h];
    while (he) {
        if (Jim_CompareHashKeys(ht, key, he->key))
            return replace ? he : NULL;
        he = he->next;
    }

    

    he = Jim_Alloc(sizeof(*he));
    he->next = ht->table[h];
    ht->table[h] = he;
    ht->used++;
    he->key = NULL;

    return he;
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
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







-
-
-
-
-
-
+
+
+
+
+
+


















-
-
-
-
-
-
+
+
+
+
+
+








static void JimStringCopyHTKeyDestructor(void *privdata, void *key)
{
    Jim_Free(key);
}

static const Jim_HashTableType JimPackageHashTableType = {
    JimStringCopyHTHashFunction,     
    JimStringCopyHTDup,              
    NULL,                            
    JimStringCopyHTKeyCompare,       
    JimStringCopyHTKeyDestructor,    
    NULL                             
    JimStringCopyHTHashFunction,
    JimStringCopyHTDup,
    NULL,
    JimStringCopyHTKeyCompare,
    JimStringCopyHTKeyDestructor,
    NULL
};

typedef struct AssocDataValue
{
    Jim_InterpDeleteProc *delProc;
    void *data;
} AssocDataValue;

static void JimAssocDataHashTableValueDestructor(void *privdata, void *data)
{
    AssocDataValue *assocPtr = (AssocDataValue *) data;

    if (assocPtr->delProc != NULL)
        assocPtr->delProc((Jim_Interp *)privdata, assocPtr->data);
    Jim_Free(data);
}

static const Jim_HashTableType JimAssocDataHashTableType = {
    JimStringCopyHTHashFunction,    
    JimStringCopyHTDup,             
    NULL,                           
    JimStringCopyHTKeyCompare,      
    JimStringCopyHTKeyDestructor,   
    JimAssocDataHashTableValueDestructor        
    JimStringCopyHTHashFunction,
    JimStringCopyHTDup,
    NULL,
    JimStringCopyHTKeyCompare,
    JimStringCopyHTKeyDestructor,
    JimAssocDataHashTableValueDestructor
};

void Jim_InitStack(Jim_Stack *stack)
{
    stack->len = 0;
    stack->maxlen = 0;
    stack->vector = NULL;
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
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







-
-
-
-
-
-
+
+
+
+
+
+

-
-
-
+
+
+

-
-
+
+







+

-
+






+
+
+
+

-
-
+
+




-
-
-
+
+
+

-
-
-
-
-
-
-
+
+
+
+
+
+
+








    for (i = 0; i < stack->len; i++)
        freeFunc(stack->vector[i]);
}



#define JIM_TT_NONE    0          
#define JIM_TT_STR     1          
#define JIM_TT_ESC     2          
#define JIM_TT_VAR     3          
#define JIM_TT_DICTSUGAR   4      
#define JIM_TT_CMD     5          
#define JIM_TT_NONE    0
#define JIM_TT_STR     1
#define JIM_TT_ESC     2
#define JIM_TT_VAR     3
#define JIM_TT_DICTSUGAR   4
#define JIM_TT_CMD     5

#define JIM_TT_SEP     6          
#define JIM_TT_EOL     7          
#define JIM_TT_EOF     8          
#define JIM_TT_SEP     6
#define JIM_TT_EOL     7
#define JIM_TT_EOF     8

#define JIM_TT_LINE    9          
#define JIM_TT_WORD   10          
#define JIM_TT_LINE    9
#define JIM_TT_WORD   10


#define JIM_TT_SUBEXPR_START  11
#define JIM_TT_SUBEXPR_END    12
#define JIM_TT_SUBEXPR_COMMA  13
#define JIM_TT_EXPR_INT       14
#define JIM_TT_EXPR_DOUBLE    15
#define JIM_TT_EXPR_BOOLEAN   16

#define JIM_TT_EXPRSUGAR      16  
#define JIM_TT_EXPRSUGAR      17


#define JIM_TT_EXPR_OP        20

#define TOKEN_IS_SEP(type) (type >= JIM_TT_SEP && type <= JIM_TT_EOF)

#define TOKEN_IS_EXPR_START(type) (type == JIM_TT_NONE || type == JIM_TT_SUBEXPR_START || type == JIM_TT_SUBEXPR_COMMA)

#define TOKEN_IS_EXPR_OP(type) (type >= JIM_TT_EXPR_OP)

struct JimParseMissing {
    int ch;             
    int line;           
    int ch;
    int line;
};

struct JimParserCtx
{
    const char *p;              
    int len;                    
    int linenr;                 
    const char *p;
    int len;
    int linenr;
    const char *tstart;
    const char *tend;           
    int tline;                  
    int tt;                     
    int eof;                    
    int inquote;                
    int comment;                
    struct JimParseMissing missing;   
    const char *tend;
    int tline;
    int tt;
    int eof;
    int inquote;
    int comment;
    struct JimParseMissing missing;
};

static int JimParseScript(struct JimParserCtx *pc);
static int JimParseSep(struct JimParserCtx *pc);
static int JimParseEol(struct JimParserCtx *pc);
static int JimParseCmd(struct JimParserCtx *pc);
static int JimParseQuote(struct JimParserCtx *pc);
7028
7029
7030
7031
7032
7033
7034
7035

7036
7037
7038
7039
7040
7041
7042
7132
7133
7134
7135
7136
7137
7138

7139
7140
7141
7142
7143
7144
7145
7146







-
+







    pc->comment = 1;
    pc->missing.ch = ' ';
    pc->missing.line = linenr;
}

static int JimParseScript(struct JimParserCtx *pc)
{
    while (1) {                 
    while (1) {
        if (!pc->len) {
            pc->tstart = pc->p;
            pc->tend = pc->p - 1;
            pc->tline = pc->linenr;
            pc->tt = JIM_TT_EOL;
            pc->eof = 1;
            return JIM_OK;
7064
7065
7066
7067
7068
7069
7070
7071

7072
7073
7074
7075
7076
7077
7078
7168
7169
7170
7171
7172
7173
7174

7175
7176
7177
7178
7179
7180
7181
7182







-
+







                return JimParseStr(pc);
            case '[':
                pc->comment = 0;
                return JimParseCmd(pc);
            case '$':
                pc->comment = 0;
                if (JimParseVar(pc) == JIM_ERR) {
                    

                    pc->tstart = pc->tend = pc->p++;
                    pc->len--;
                    pc->tt = JIM_TT_ESC;
                }
                return JIM_OK;
            case '#':
                if (pc->comment) {
7125
7126
7127
7128
7129
7130
7131
7132

7133
7134
7135
7136
7137
7138
7139
7229
7230
7231
7232
7233
7234
7235

7236
7237
7238
7239
7240
7241
7242
7243







-
+







}


static void JimParseSubBrace(struct JimParserCtx *pc)
{
    int level = 1;

    

    pc->p++;
    pc->len--;
    while (pc->len) {
        switch (*pc->p) {
            case '\\':
                if (pc->len > 1) {
                    if (*++pc->p == '\n') {
7169
7170
7171
7172
7173
7174
7175
7176

7177
7178
7179
7180
7181
7182
7183
7273
7274
7275
7276
7277
7278
7279

7280
7281
7282
7283
7284
7285
7286
7287







-
+







}

static int JimParseSubQuote(struct JimParserCtx *pc)
{
    int tt = JIM_TT_STR;
    int line = pc->tline;

    

    pc->p++;
    pc->len--;
    while (pc->len) {
        switch (*pc->p) {
            case '\\':
                if (pc->len > 1) {
                    if (*++pc->p == '\n') {
7218
7219
7220
7221
7222
7223
7224
7225

7226
7227
7228
7229
7230
7231
7232
7322
7323
7324
7325
7326
7327
7328

7329
7330
7331
7332
7333
7334
7335
7336







-
+








static void JimParseSubCmd(struct JimParserCtx *pc)
{
    int level = 1;
    int startofword = 1;
    int line = pc->tline;

    

    pc->p++;
    pc->len--;
    while (pc->len) {
        switch (*pc->p) {
            case '\\':
                if (pc->len > 1) {
                    if (*++pc->p == '\n') {
7298
7299
7300
7301
7302
7303
7304
7305

7306
7307
7308
7309
7310
7311

7312
7313
7314
7315
7316
7317
7318
7402
7403
7404
7405
7406
7407
7408

7409
7410
7411
7412
7413
7414

7415
7416
7417
7418
7419
7420
7421
7422







-
+





-
+







    pc->tline = pc->linenr;
    pc->tt = JimParseSubQuote(pc);
    return JIM_OK;
}

static int JimParseVar(struct JimParserCtx *pc)
{
    

    pc->p++;
    pc->len--;

#ifdef EXPRSUGAR_BRACKET
    if (*pc->p == '[') {
        

        JimParseCmd(pc);
        pc->tt = JIM_TT_EXPRSUGAR;
        return JIM_OK;
    }
#endif

    pc->tstart = pc->p;
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
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







-
+














-
+







        if (pc->len) {
            pc->p++;
            pc->len--;
        }
    }
    else {
        while (1) {
            

            if (pc->p[0] == ':' && pc->p[1] == ':') {
                while (*pc->p == ':') {
                    pc->p++;
                    pc->len--;
                }
                continue;
            }
            if (isalnum(UCHAR(*pc->p)) || *pc->p == '_' || UCHAR(*pc->p) >= 0x80) {
                pc->p++;
                pc->len--;
                continue;
            }
            break;
        }
        

        if (*pc->p == '(') {
            int count = 1;
            const char *paren = NULL;

            pc->tt = JIM_TT_DICTSUGAR;

            while (count && pc->len) {
7376
7377
7378
7379
7380
7381
7382
7383

7384
7385
7386
7387
7388
7389
7390
7480
7481
7482
7483
7484
7485
7486

7487
7488
7489
7490
7491
7492
7493
7494







-
+







                }
            }
            if (count == 0) {
                pc->p++;
                pc->len--;
            }
            else if (paren) {
                

                paren++;
                pc->len += (pc->p - paren);
                pc->p = paren;
            }
#ifndef EXPRSUGAR_BRACKET
            if (*pc->tstart == '(') {
                pc->tt = JIM_TT_EXPRSUGAR;
7401
7402
7403
7404
7405
7406
7407
7408

7409
7410
7411
7412
7413
7414
7415
7416

7417
7418
7419
7420
7421
7422
7423
7505
7506
7507
7508
7509
7510
7511

7512
7513
7514
7515
7516
7517
7518
7519

7520
7521
7522
7523
7524
7525
7526
7527







-
+







-
+







    return JIM_OK;
}

static int JimParseStr(struct JimParserCtx *pc)
{
    if (pc->tt == JIM_TT_SEP || pc->tt == JIM_TT_EOL ||
        pc->tt == JIM_TT_NONE || pc->tt == JIM_TT_STR) {
        

        if (*pc->p == '{') {
            return JimParseBrace(pc);
        }
        if (*pc->p == '"') {
            pc->inquote = 1;
            pc->p++;
            pc->len--;
            

            pc->missing.line = pc->tline;
        }
    }
    pc->tstart = pc->p;
    pc->tline = pc->linenr;
    while (1) {
        if (pc->len == 0) {
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
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







-
+




-
+



-
+

-
+


-
+







                    if (*(pc->p + 1) == '\n') {
                        pc->linenr++;
                    }
                    pc->p++;
                    pc->len--;
                }
                else if (pc->len == 1) {
                    

                    pc->missing.ch = '\\';
                }
                break;
            case '(':
                

                if (pc->len > 1 && pc->p[1] != '$') {
                    break;
                }
                

            case ')':
                

                if (*pc->p == '(' || pc->tt == JIM_TT_VAR) {
                    if (pc->p == pc->tstart) {
                        

                        pc->p++;
                        pc->len--;
                    }
                    pc->tend = pc->p - 1;
                    pc->tt = JIM_TT_ESC;
                    return JIM_OK;
                }
7497
7498
7499
7500
7501
7502
7503
7504

7505
7506
7507
7508
7509
7510
7511
7601
7602
7603
7604
7605
7606
7607

7608
7609
7610
7611
7612
7613
7614
7615







-
+







                    return JIM_OK;
                }
                break;
        }
        pc->p++;
        pc->len--;
    }
    return JIM_OK;              
    return JIM_OK;
}

static int JimParseComment(struct JimParserCtx *pc)
{
    while (*pc->p) {
        if (*pc->p == '\\') {
            pc->p++;
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
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







-
+


-
+




-
+




-
+









-
+












-
+













-
+







                            for (k = 0; k < maxchars; k++) {
                                int c = xdigitval(s[i + k + 1]);
                                if (c == -1) {
                                    break;
                                }
                                val = (val << 4) | c;
                            }
                            

                            if (s[i] == '{') {
                                if (k == 0 || val > 0x1fffff || s[i + k + 1] != '}') {
                                    

                                    i--;
                                    k = 0;
                                }
                                else {
                                    

                                    k++;
                                }
                            }
                            if (k) {
                                

                                if (s[i] == 'x') {
                                    *p++ = val;
                                }
                                else {
                                    p += utf8_fromunicode(p, val);
                                }
                                i += k;
                                break;
                            }
                            

                            *p++ = s[i];
                        }
                        break;
                    case 'v':
                        *p++ = 0xb;
                        i++;
                        break;
                    case '\0':
                        *p++ = '\\';
                        i++;
                        break;
                    case '\n':
                        

                        *p++ = ' ';
                        do {
                            i++;
                        } while (s[i + 1] == ' ' || s[i + 1] == '\t');
                        break;
                    case '0':
                    case '1':
                    case '2':
                    case '3':
                    case '4':
                    case '5':
                    case '6':
                    case '7':
                        

                        {
                            int val = 0;
                            int c = odigitval(s[i + 1]);

                            val = c;
                            c = odigitval(s[i + 2]);
                            if (c == -1) {
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
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







+
-
+

-
-

-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-







{
    const char *start, *end;
    char *token;
    int len;

    start = pc->tstart;
    end = pc->tend;
    len = (end - start) + 1;
    if (start > end) {
    if (len < 0) {
        len = 0;
        token = Jim_Alloc(1);
        token[0] = '\0';
    }
    else {
        len = (end - start) + 1;
        token = Jim_Alloc(len + 1);
        if (pc->tt != JIM_TT_ESC) {
            
            memcpy(token, start, len);
            token[len] = '\0';
        }
        else {
            
            len = JimEscape(token, start, len);
    token = Jim_Alloc(len + 1);
    if (pc->tt != JIM_TT_ESC) {

        memcpy(token, start, len);
        token[len] = '\0';
    }
    else {

        len = JimEscape(token, start, len);
        }
    }

    return Jim_NewStringObjNoAlloc(interp, token, len);
}

static int JimParseListSep(struct JimParserCtx *pc);
static int JimParseListStr(struct JimParserCtx *pc);
7788
7789
7790
7791
7792
7793
7794
7795

7796
7797
7798
7799
7800
7801
7802
7888
7889
7890
7891
7892
7893
7894

7895
7896
7897
7898
7899
7900
7901
7902







-
+







    pc->tt = JIM_TT_STR;

    while (pc->len) {
        switch (*pc->p) {
            case '\\':
                pc->tt = JIM_TT_ESC;
                if (--pc->len == 0) {
                    

                    pc->tend = pc->p;
                    return JIM_OK;
                }
                pc->p++;
                break;
            case '\n':
                pc->linenr++;
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
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







-
+



















-
+

-
+




-
+





-
+











-
+



-
+

-
+




-
+









-
+







    while (pc->len) {
        if (isspace(UCHAR(*pc->p))) {
            pc->tend = pc->p - 1;
            return JIM_OK;
        }
        if (*pc->p == '\\') {
            if (--pc->len == 0) {
                

                pc->tend = pc->p;
                return JIM_OK;
            }
            pc->tt = JIM_TT_ESC;
            pc->p++;
        }
        pc->p++;
        pc->len--;
    }
    pc->tend = pc->p - 1;
    return JIM_OK;
}



Jim_Obj *Jim_NewObj(Jim_Interp *interp)
{
    Jim_Obj *objPtr;

    

    if (interp->freeList != NULL) {
        

        objPtr = interp->freeList;
        interp->freeList = objPtr->nextObjPtr;
    }
    else {
        

        objPtr = Jim_Alloc(sizeof(*objPtr));
    }

    objPtr->refCount = 0;

    

    objPtr->prevObjPtr = NULL;
    objPtr->nextObjPtr = interp->liveList;
    if (interp->liveList)
        interp->liveList->prevObjPtr = objPtr;
    interp->liveList = objPtr;

    return objPtr;
}

void Jim_FreeObj(Jim_Interp *interp, Jim_Obj *objPtr)
{
    

    JimPanic((objPtr->refCount != 0, "!!!Object %p freed with bad refcount %d, type=%s", objPtr,
        objPtr->refCount, objPtr->typePtr ? objPtr->typePtr->name : "<none>"));

    

    Jim_FreeIntRep(interp, objPtr);
    

    if (objPtr->bytes != NULL) {
        if (objPtr->bytes != JimEmptyStringRep)
            Jim_Free(objPtr->bytes);
    }
    

    if (objPtr->prevObjPtr)
        objPtr->prevObjPtr->nextObjPtr = objPtr->nextObjPtr;
    if (objPtr->nextObjPtr)
        objPtr->nextObjPtr->prevObjPtr = objPtr->prevObjPtr;
    if (interp->liveList == objPtr)
        interp->liveList = objPtr->nextObjPtr;
#ifdef JIM_DISABLE_OBJECT_POOL
    Jim_Free(objPtr);
#else
    

    objPtr->prevObjPtr = NULL;
    objPtr->nextObjPtr = interp->freeList;
    if (interp->freeList)
        interp->freeList->prevObjPtr = objPtr;
    interp->freeList = objPtr;
    objPtr->refCount = -1;
#endif
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
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







-
+



-








-
+



-
+






-
+









-
+












-
+
-
-
+








-
+
-
-
-
+




















+
+
+
+
+
+
+
+
+
+
+






-
-
-
-
+
+
+
+
+
-
-
+
-
+








Jim_Obj *Jim_DuplicateObj(Jim_Interp *interp, Jim_Obj *objPtr)
{
    Jim_Obj *dupPtr;

    dupPtr = Jim_NewObj(interp);
    if (objPtr->bytes == NULL) {
        

        dupPtr->bytes = NULL;
    }
    else if (objPtr->length == 0) {
        
        dupPtr->bytes = JimEmptyStringRep;
        dupPtr->length = 0;
        dupPtr->typePtr = NULL;
        return dupPtr;
    }
    else {
        dupPtr->bytes = Jim_Alloc(objPtr->length + 1);
        dupPtr->length = objPtr->length;
        

        memcpy(dupPtr->bytes, objPtr->bytes, objPtr->length + 1);
    }

    

    dupPtr->typePtr = objPtr->typePtr;
    if (objPtr->typePtr != NULL) {
        if (objPtr->typePtr->dupIntRepProc == NULL) {
            dupPtr->internalRep = objPtr->internalRep;
        }
        else {
            

            objPtr->typePtr->dupIntRepProc(interp, objPtr, dupPtr);
        }
    }
    return dupPtr;
}

const char *Jim_GetString(Jim_Obj *objPtr, int *lenPtr)
{
    if (objPtr->bytes == NULL) {
        

        JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name));
        objPtr->typePtr->updateStringProc(objPtr);
    }
    if (lenPtr)
        *lenPtr = objPtr->length;
    return objPtr->bytes;
}


int Jim_Length(Jim_Obj *objPtr)
{
    if (objPtr->bytes == NULL) {
        

        JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name));
        objPtr->typePtr->updateStringProc(objPtr);
        Jim_GetString(objPtr, NULL);
    }
    return objPtr->length;
}


const char *Jim_String(Jim_Obj *objPtr)
{
    if (objPtr->bytes == NULL) {
        

        JimPanic((objPtr->typePtr == NULL, "UpdateStringProc called against typeless value."));
        JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name));
        objPtr->typePtr->updateStringProc(objPtr);
        Jim_GetString(objPtr, NULL);
    }
    return objPtr->bytes;
}

static void JimSetStringBytes(Jim_Obj *objPtr, const char *str)
{
    objPtr->bytes = Jim_StrDup(str);
    objPtr->length = strlen(str);
}

static void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
static void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);

static const Jim_ObjType dictSubstObjType = {
    "dict-substitution",
    FreeDictSubstInternalRep,
    DupDictSubstInternalRep,
    NULL,
    JIM_TYPE_NONE,
};

static void FreeInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
static void DupInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);

static const Jim_ObjType interpolatedObjType = {
    "interpolated",
    FreeInterpolatedInternalRep,
    DupInterpolatedInternalRep,
    NULL,
    JIM_TYPE_NONE,
};

static void FreeInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
{
    Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr);
}

static const Jim_ObjType interpolatedObjType = {
    "interpolated",
    FreeInterpolatedInternalRep,
    NULL,
static void DupInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
{

    dupPtr->internalRep = srcPtr->internalRep;

    NULL,
    JIM_TYPE_NONE,
    Jim_IncrRefCount(dupPtr->internalRep.dictSubstValue.indexObjPtr);
};
}

static void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
static int SetStringFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);

static const Jim_ObjType stringObjType = {
    "string",
    NULL,
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
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







-
+

-
+



-
+

-
+


-
+







    dupPtr->internalRep.strValue.maxLength = srcPtr->length;
    dupPtr->internalRep.strValue.charLength = srcPtr->internalRep.strValue.charLength;
}

static int SetStringFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
{
    if (objPtr->typePtr != &stringObjType) {
        

        if (objPtr->bytes == NULL) {
            

            JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name));
            objPtr->typePtr->updateStringProc(objPtr);
        }
        

        Jim_FreeIntRep(interp, objPtr);
        

        objPtr->typePtr = &stringObjType;
        objPtr->internalRep.strValue.maxLength = objPtr->length;
        

        objPtr->internalRep.strValue.charLength = -1;
    }
    return JIM_OK;
}

int Jim_Utf8Length(Jim_Interp *interp, Jim_Obj *objPtr)
{
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
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







-
+


-
+




-
+
-
-



-
+








-
+




-
+







}


Jim_Obj *Jim_NewStringObj(Jim_Interp *interp, const char *s, int len)
{
    Jim_Obj *objPtr = Jim_NewObj(interp);

    

    if (len == -1)
        len = strlen(s);
    

    if (len == 0) {
        objPtr->bytes = JimEmptyStringRep;
    }
    else {
        objPtr->bytes = Jim_Alloc(len + 1);
        objPtr->bytes = Jim_StrDupLen(s, len);
        memcpy(objPtr->bytes, s, len);
        objPtr->bytes[len] = '\0';
    }
    objPtr->length = len;

    

    objPtr->typePtr = NULL;
    return objPtr;
}


Jim_Obj *Jim_NewStringObjUtf8(Jim_Interp *interp, const char *s, int charlen)
{
#ifdef JIM_UTF8
    

    int bytelen = utf8_index(s, charlen);

    Jim_Obj *objPtr = Jim_NewStringObj(interp, s, bytelen);

    

    objPtr->typePtr = &stringObjType;
    objPtr->internalRep.strValue.maxLength = bytelen;
    objPtr->internalRep.strValue.charLength = charlen;

    return objPtr;
#else
    return Jim_NewStringObj(interp, s, charlen);
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
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







-
+















-
+








    if (len == -1)
        len = strlen(str);
    needlen = objPtr->length + len;
    if (objPtr->internalRep.strValue.maxLength < needlen ||
        objPtr->internalRep.strValue.maxLength == 0) {
        needlen *= 2;
        

        if (needlen < 7) {
            needlen = 7;
        }
        if (objPtr->bytes == JimEmptyStringRep) {
            objPtr->bytes = Jim_Alloc(needlen + 1);
        }
        else {
            objPtr->bytes = Jim_Realloc(objPtr->bytes, needlen + 1);
        }
        objPtr->internalRep.strValue.maxLength = needlen;
    }
    memcpy(objPtr->bytes + objPtr->length, str, len);
    objPtr->bytes[objPtr->length + len] = '\0';

    if (objPtr->internalRep.strValue.charLength >= 0) {
        

        objPtr->internalRep.strValue.charLength += utf8_strlen(objPtr->bytes + objPtr->length, len);
    }
    objPtr->length += len;
}

void Jim_AppendString(Jim_Interp *interp, Jim_Obj *objPtr, const char *str, int len)
{
8208
8209
8210
8211
8212
8213
8214
8215

8216
8217
8218
8219
8220
8221
8222
8313
8314
8315
8316
8317
8318
8319

8320
8321
8322
8323
8324
8325
8326
8327







-
+







int Jim_StringCompareObj(Jim_Interp *interp, Jim_Obj *firstObjPtr, Jim_Obj *secondObjPtr, int nocase)
{
    int l1, l2;
    const char *s1 = Jim_GetString(firstObjPtr, &l1);
    const char *s2 = Jim_GetString(secondObjPtr, &l2);

    if (nocase) {
        

        return JimStringCompareLen(s1, s2, -1, nocase);
    }
    return JimStringCompare(s1, l1, s2, l2);
}

int Jim_StringCompareLenObj(Jim_Interp *interp, Jim_Obj *firstObjPtr, Jim_Obj *secondObjPtr, int nocase)
{
8310
8311
8312
8313
8314
8315
8316
8317

8318
8319
8320
8321
8322
8323
8324
8415
8416
8417
8418
8419
8420
8421

8422
8423
8424
8425
8426
8427
8428
8429







-
+







        return NULL;
    }

    if (first == 0 && rangeLen == len) {
        return strObjPtr;
    }
    if (len == bytelen) {
        

        return Jim_NewStringObj(interp, str + first, rangeLen);
    }
    return Jim_NewStringObjUtf8(interp, str + utf8_index(str, first), rangeLen);
#else
    return Jim_StringByteRangeObj(interp, strObjPtr, firstObjPtr, lastObjPtr);
#endif
}
8339
8340
8341
8342
8343
8344
8345
8346

8347
8348
8349

8350
8351
8352
8353
8354

8355
8356
8357
8358
8359
8360
8361
8444
8445
8446
8447
8448
8449
8450

8451
8452
8453

8454
8455
8456
8457
8458

8459
8460
8461
8462
8463
8464
8465
8466







-
+


-
+




-
+








    if (last < first) {
        return strObjPtr;
    }

    str = Jim_String(strObjPtr);

    

    objPtr = Jim_NewStringObjUtf8(interp, str, first);

    

    if (newStrObj) {
        Jim_AppendObj(interp, objPtr, newStrObj);
    }

    

    Jim_AppendString(interp, objPtr, str + utf8_index(str, last + 1), len - last - 1);

    return objPtr;
}

static void JimStrCopyUpperLower(char *dest, const char *str, int uc)
{
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
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







-
-
















-
-
-
-


















-
-
-
+








static Jim_Obj *JimStringToLower(Jim_Interp *interp, Jim_Obj *strObjPtr)
{
    char *buf;
    int len;
    const char *str;

    SetStringFromAny(interp, strObjPtr);

    str = Jim_GetString(strObjPtr, &len);

#ifdef JIM_UTF8
    len *= 2;
#endif
    buf = Jim_Alloc(len + 1);
    JimStrCopyUpperLower(buf, str, 0);
    return Jim_NewStringObjNoAlloc(interp, buf, -1);
}

static Jim_Obj *JimStringToUpper(Jim_Interp *interp, Jim_Obj *strObjPtr)
{
    char *buf;
    const char *str;
    int len;

    if (strObjPtr->typePtr != &stringObjType) {
        SetStringFromAny(interp, strObjPtr);
    }

    str = Jim_GetString(strObjPtr, &len);

#ifdef JIM_UTF8
    len *= 2;
#endif
    buf = Jim_Alloc(len + 1);
    JimStrCopyUpperLower(buf, str, 1);
    return Jim_NewStringObjNoAlloc(interp, buf, -1);
}

static Jim_Obj *JimStringToTitle(Jim_Interp *interp, Jim_Obj *strObjPtr)
{
    char *buf, *p;
    int len;
    int c;
    const char *str;

    str = Jim_GetString(strObjPtr, &len);
    if (len == 0) {
        return strObjPtr;
    }

#ifdef JIM_UTF8
    len *= 2;
#endif
    buf = p = Jim_Alloc(len + 1);

    str += utf8_tounicode(str, &c);
    p += utf8_getchars(p, utf8_title(c));
8450
8451
8452
8453
8454
8455
8456
8457

8458
8459
8460
8461
8462
8463
8464
8547
8548
8549
8550
8551
8552
8553

8554
8555
8556
8557
8558
8559
8560
8561







-
+







static const char *JimFindTrimLeft(const char *str, int len, const char *trimchars, int trimlen)
{
    while (len) {
        int c;
        int n = utf8_tounicode(str, &c);

        if (utf8_memchr(trimchars, trimlen, c) == NULL) {
            

            break;
        }
        str += n;
        len -= n;
    }
    return str;
}
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
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







-
+



-
+







-
+









-
+


-
+


-
+

-
+








    SetStringFromAny(interp, strObjPtr);

    len = Jim_Length(strObjPtr);
    nontrim = JimFindTrimRight(strObjPtr->bytes, len, trimchars, trimcharslen);

    if (nontrim == NULL) {
        

        return Jim_NewEmptyStringObj(interp);
    }
    if (nontrim == strObjPtr->bytes + len) {
        

        return strObjPtr;
    }

    if (Jim_IsShared(strObjPtr)) {
        strObjPtr = Jim_NewStringObj(interp, strObjPtr->bytes, (nontrim - strObjPtr->bytes));
    }
    else {
        

        strObjPtr->bytes[nontrim - strObjPtr->bytes] = 0;
        strObjPtr->length = (nontrim - strObjPtr->bytes);
    }

    return strObjPtr;
}

static Jim_Obj *JimStringTrim(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *trimcharsObjPtr)
{
    

    Jim_Obj *objPtr = JimStringTrimLeft(interp, strObjPtr, trimcharsObjPtr);

    

    strObjPtr = JimStringTrimRight(interp, objPtr, trimcharsObjPtr);

    

    if (objPtr != strObjPtr && objPtr->refCount == 0) {
        

        Jim_FreeNewObj(interp, objPtr);
    }

    return strObjPtr;
}


8573
8574
8575
8576
8577
8578
8579
8580

8581
8582
8583
8584
8585
8586

8587
8588
8589
8590
8591
8592
8593
8670
8671
8672
8673
8674
8675
8676

8677
8678
8679
8680
8681
8682

8683
8684
8685
8686
8687
8688
8689
8690







-
+





-
+







#endif

static int JimStringIs(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *strClass, int strict)
{
    static const char * const strclassnames[] = {
        "integer", "alpha", "alnum", "ascii", "digit",
        "double", "lower", "upper", "space", "xdigit",
        "control", "print", "graph", "punct",
        "control", "print", "graph", "punct", "boolean",
        NULL
    };
    enum {
        STR_IS_INTEGER, STR_IS_ALPHA, STR_IS_ALNUM, STR_IS_ASCII, STR_IS_DIGIT,
        STR_IS_DOUBLE, STR_IS_LOWER, STR_IS_UPPER, STR_IS_SPACE, STR_IS_XDIGIT,
        STR_IS_CONTROL, STR_IS_PRINT, STR_IS_GRAPH, STR_IS_PUNCT
        STR_IS_CONTROL, STR_IS_PRINT, STR_IS_GRAPH, STR_IS_PUNCT, STR_IS_BOOLEAN,
    };
    int strclass;
    int len;
    int i;
    const char *str;
    int (*isclassfunc)(int c) = NULL;

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
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







+
+
+
+
+
+
+


















-
+








        case STR_IS_DOUBLE:
            {
                double d;
                Jim_SetResultBool(interp, Jim_GetDouble(interp, strObjPtr, &d) == JIM_OK && errno != ERANGE);
                return JIM_OK;
            }

        case STR_IS_BOOLEAN:
            {
                int b;
                Jim_SetResultBool(interp, Jim_GetBoolean(interp, strObjPtr, &b) == JIM_OK);
                return JIM_OK;
            }

        case STR_IS_ALPHA: isclassfunc = isalpha; break;
        case STR_IS_ALNUM: isclassfunc = isalnum; break;
        case STR_IS_ASCII: isclassfunc = jim_isascii; break;
        case STR_IS_DIGIT: isclassfunc = isdigit; break;
        case STR_IS_LOWER: isclassfunc = islower; break;
        case STR_IS_UPPER: isclassfunc = isupper; break;
        case STR_IS_SPACE: isclassfunc = isspace; break;
        case STR_IS_XDIGIT: isclassfunc = isxdigit; break;
        case STR_IS_CONTROL: isclassfunc = iscntrl; break;
        case STR_IS_PRINT: isclassfunc = isprint; break;
        case STR_IS_GRAPH: isclassfunc = isgraph; break;
        case STR_IS_PUNCT: isclassfunc = ispunct; break;
        default:
            return JIM_ERR;
    }

    for (i = 0; i < len; i++) {
        if (!isclassfunc(str[i])) {
        if (!isclassfunc(UCHAR(str[i]))) {
            Jim_SetResultBool(interp, 0);
            return JIM_OK;
        }
    }
    Jim_SetResultBool(interp, 1);
    return JIM_OK;
}
8654
8655
8656
8657
8658
8659
8660
8661
8662
8663

8664
8665
8666
8667
8668
8669
8670

8671
8672
8673
8674
8675
8676
8677
8758
8759
8760
8761
8762
8763
8764



8765
8766
8767
8768
8769
8770
8771

8772
8773
8774
8775
8776
8777
8778
8779







-
-
-
+






-
+








int Jim_CompareStringImmediate(Jim_Interp *interp, Jim_Obj *objPtr, const char *str)
{
    if (objPtr->typePtr == &comparedStringObjType && objPtr->internalRep.ptr == str) {
        return 1;
    }
    else {
        const char *objStr = Jim_String(objPtr);

        if (strcmp(str, objStr) != 0)
        if (strcmp(str, Jim_String(objPtr)) != 0)
            return 0;

        if (objPtr->typePtr != &comparedStringObjType) {
            Jim_FreeIntRep(interp, objPtr);
            objPtr->typePtr = &comparedStringObjType;
        }
        objPtr->internalRep.ptr = (char *)str;  
        objPtr->internalRep.ptr = (char *)str;
        return 1;
    }
}

static int qsortCompareStringPointers(const void *a, const void *b)
{
    char *const *sa = (char *const *)a;
8756
8757
8758
8759
8760
8761
8762
8763
8764
8765
8766




8767
8768
8769
8770
8771
8772



8773
8774
8775
8776
8777
8778
8779
8858
8859
8860
8861
8862
8863
8864




8865
8866
8867
8868
8869
8870
8871



8872
8873
8874
8875
8876
8877
8878
8879
8880
8881







-
-
-
-
+
+
+
+



-
-
-
+
+
+







{
    Jim_Obj *objPtr;
    int type;
} ScriptToken;

typedef struct ScriptObj
{
    ScriptToken *token;         
    Jim_Obj *fileNameObj;       
    int len;                    
    int substFlags;             
    ScriptToken *token;
    Jim_Obj *fileNameObj;
    int len;
    int substFlags;
    int inUse;                  /* Used to share a ScriptObj. Currently
                                   only used by Jim_EvalObj() as protection against
                                   shimmering of the currently evaluated object. */
    int firstline;              
    int linenr;                 
    int missing;                
    int firstline;
    int linenr;
    int missing;
} ScriptObj;

static void JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
static int JimParseCheckMissing(Jim_Interp *interp, int ch);
static ScriptObj *JimGetScript(Jim_Interp *interp, Jim_Obj *objPtr);

void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
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
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







-
-
-
-
+
+
+
+




-
-
-
-
-
+
+
+
+
+







    JIM_NOTUSED(srcPtr);

    dupPtr->typePtr = NULL;
}

typedef struct
{
    const char *token;          
    int len;                    
    int type;                   
    int line;                   
    const char *token;
    int len;
    int type;
    int line;
} ParseToken;

typedef struct
{
    
    ParseToken *list;           
    int size;                   
    int count;                  
    ParseToken static_list[20]; 

    ParseToken *list;
    int size;
    int count;
    ParseToken static_list[20];
} ParseTokenList;

static void ScriptTokenListInit(ParseTokenList *tokenlist)
{
    tokenlist->list = tokenlist->static_list;
    tokenlist->size = sizeof(tokenlist->static_list) / sizeof(ParseToken);
    tokenlist->count = 0;
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
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







-
+






-
+












-
+




-
+


-
+



+
+
-
-
-
+
+
+
+
+
+
+
+













-
+
















-
+

-
+












-
+










-
+



-
+


-
+




-
+


-
+





-
+







-
+





-
+







-
+




-
+








static void ScriptAddToken(ParseTokenList *tokenlist, const char *token, int len, int type,
    int line)
{
    ParseToken *t;

    if (tokenlist->count == tokenlist->size) {
        

        tokenlist->size *= 2;
        if (tokenlist->list != tokenlist->static_list) {
            tokenlist->list =
                Jim_Realloc(tokenlist->list, tokenlist->size * sizeof(*tokenlist->list));
        }
        else {
            

            tokenlist->list = Jim_Alloc(tokenlist->size * sizeof(*tokenlist->list));
            memcpy(tokenlist->list, tokenlist->static_list,
                tokenlist->count * sizeof(*tokenlist->list));
        }
    }
    t = &tokenlist->list[tokenlist->count++];
    t->token = token;
    t->len = len;
    t->type = type;
    t->line = line;
}

static int JimCountWordTokens(ParseToken *t)
static int JimCountWordTokens(struct ScriptObj *script, ParseToken *t)
{
    int expand = 1;
    int count = 0;

    

    if (t->type == JIM_TT_STR && !TOKEN_IS_SEP(t[1].type)) {
        if ((t->len == 1 && *t->token == '*') || (t->len == 6 && strncmp(t->token, "expand", 6) == 0)) {
            

            expand = -1;
            t++;
        }
        else {
            if (script->missing == ' ') {
    }

    

                script->missing = '}';
                script->linenr = t[1].line;
            }
        }
    }


    while (!TOKEN_IS_SEP(t->type)) {
        t++;
        count++;
    }

    return count * expand;
}

static Jim_Obj *JimMakeScriptObj(Jim_Interp *interp, const ParseToken *t)
{
    Jim_Obj *objPtr;

    if (t->type == JIM_TT_ESC && memchr(t->token, '\\', t->len) != NULL) {
        

        int len = t->len;
        char *str = Jim_Alloc(len + 1);
        len = JimEscape(str, t->token, len);
        objPtr = Jim_NewStringObjNoAlloc(interp, str, len);
    }
    else {
        objPtr = Jim_NewStringObj(interp, t->token, t->len);
    }
    return objPtr;
}

static void ScriptObjAddTokens(Jim_Interp *interp, struct ScriptObj *script,
    ParseTokenList *tokenlist)
{
    int i;
    struct ScriptToken *token;
    

    int lineargs = 0;
    

    ScriptToken *linefirst;
    int count;
    int linenr;

#ifdef DEBUG_SHOW_SCRIPT_TOKENS
    printf("==== Tokens ====\n");
    for (i = 0; i < tokenlist->count; i++) {
        printf("[%2d]@%d %s '%.*s'\n", i, tokenlist->list[i].line, jim_tt_name(tokenlist->list[i].type),
            tokenlist->list[i].len, tokenlist->list[i].token);
    }
#endif

    

    count = tokenlist->count;
    for (i = 0; i < tokenlist->count; i++) {
        if (tokenlist->list[i].type == JIM_TT_EOL) {
            count++;
        }
    }
    linenr = script->firstline = tokenlist->list[0].line;

    token = script->token = Jim_Alloc(sizeof(ScriptToken) * count);

    

    linefirst = token++;

    for (i = 0; i < tokenlist->count; ) {
        

        int wordtokens;

        

        while (tokenlist->list[i].type == JIM_TT_SEP) {
            i++;
        }

        wordtokens = JimCountWordTokens(tokenlist->list + i);
        wordtokens = JimCountWordTokens(script, tokenlist->list + i);

        if (wordtokens == 0) {
            

            if (lineargs) {
                linefirst->type = JIM_TT_LINE;
                linefirst->objPtr = JimNewScriptLineObj(interp, lineargs, linenr);
                Jim_IncrRefCount(linefirst->objPtr);

                

                lineargs = 0;
                linefirst = token++;
            }
            i++;
            continue;
        }
        else if (wordtokens != 1) {
            

            token->type = JIM_TT_WORD;
            token->objPtr = Jim_NewIntObj(interp, wordtokens);
            Jim_IncrRefCount(token->objPtr);
            token++;
            if (wordtokens < 0) {
                

                i++;
                wordtokens = -wordtokens - 1;
                lineargs--;
            }
        }

        if (lineargs == 0) {
            

            linenr = tokenlist->list[i].line;
        }
        lineargs++;

        

        while (wordtokens--) {
            const ParseToken *t = &tokenlist->list[i++];

            token->type = t->type;
            token->objPtr = JimMakeScriptObj(interp, t);
            Jim_IncrRefCount(token->objPtr);

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
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







-
+

















+
+
+








int Jim_ScriptIsComplete(Jim_Interp *interp, Jim_Obj *scriptObj, char *stateCharPtr)
{
    ScriptObj *script = JimGetScript(interp, scriptObj);
    if (stateCharPtr) {
        *stateCharPtr = script->missing;
    }
    return (script->missing == ' ');
    return script->missing == ' ' || script->missing == '}';
}

static int JimParseCheckMissing(Jim_Interp *interp, int ch)
{
    const char *msg;

    switch (ch) {
        case '\\':
        case ' ':
            return JIM_OK;

        case '[':
            msg = "unmatched \"[\"";
            break;
        case '{':
            msg = "missing close-brace";
            break;
        case '}':
            msg = "extra characters after close-brace";
            break;
        case '"':
        default:
            msg = "missing quote";
            break;
    }

    Jim_SetResultString(interp, msg, -1);
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
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







-
+


















-
+




-
+









-
+


-
+















-
+


-
+










-
+







    struct ScriptToken *token;

    token = script->token = Jim_Alloc(sizeof(ScriptToken) * tokenlist->count);

    for (i = 0; i < tokenlist->count; i++) {
        const ParseToken *t = &tokenlist->list[i];

        

        token->type = t->type;
        token->objPtr = JimMakeScriptObj(interp, t);
        Jim_IncrRefCount(token->objPtr);
        token++;
    }

    script->len = i;
}

static void JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
{
    int scriptTextLen;
    const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
    struct JimParserCtx parser;
    struct ScriptObj *script;
    ParseTokenList tokenlist;
    int line = 1;

    

    if (objPtr->typePtr == &sourceObjType) {
        line = objPtr->internalRep.sourceValue.lineNumber;
    }

    

    ScriptTokenListInit(&tokenlist);

    JimParserInit(&parser, scriptText, scriptTextLen, line);
    while (!parser.eof) {
        JimParseScript(&parser);
        ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt,
            parser.tline);
    }

    

    ScriptAddToken(&tokenlist, scriptText + scriptTextLen, 0, JIM_TT_EOF, 0);

    

    script = Jim_Alloc(sizeof(*script));
    memset(script, 0, sizeof(*script));
    script->inUse = 1;
    if (objPtr->typePtr == &sourceObjType) {
        script->fileNameObj = objPtr->internalRep.sourceValue.fileNameObj;
    }
    else {
        script->fileNameObj = interp->emptyObj;
    }
    Jim_IncrRefCount(script->fileNameObj);
    script->missing = parser.missing.ch;
    script->linenr = parser.missing.line;

    ScriptObjAddTokens(interp, script, &tokenlist);

    

    ScriptTokenListFree(&tokenlist);

    

    Jim_FreeIntRep(interp, objPtr);
    Jim_SetIntRepPtr(objPtr, script);
    objPtr->typePtr = &scriptObjType;
}

static void JimAddErrorToStack(Jim_Interp *interp, ScriptObj *script);

static ScriptObj *JimGetScript(Jim_Interp *interp, Jim_Obj *objPtr)
{
    if (objPtr == interp->emptyObj) {
        

        objPtr = interp->nullScriptObj;
    }

    if (objPtr->typePtr != &scriptObjType || ((struct ScriptObj *)Jim_GetIntRepPtr(objPtr))->substFlags) {
        JimSetScriptFromAny(interp, objPtr);
    }

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
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







-
+





-
+





-








-
-
-
-
-
-
+
+
+
+
+
+








-
-
-
-
-
-
+
+
+
+
+
+









-
+





-
+







            Jim_DecrRefCount(interp, cmdPtr->u.proc.nsObj);
            if (cmdPtr->u.proc.staticVars) {
                Jim_FreeHashTable(cmdPtr->u.proc.staticVars);
                Jim_Free(cmdPtr->u.proc.staticVars);
            }
        }
        else {
            

            if (cmdPtr->u.native.delProc) {
                cmdPtr->u.native.delProc(interp, cmdPtr->u.native.privData);
            }
        }
        if (cmdPtr->prevCmd) {
            

            JimDecrCmdRefCount(interp, cmdPtr->prevCmd);
        }
        Jim_Free(cmdPtr);
    }
}


static void JimVariablesHTValDestructor(void *interp, void *val)
{
    Jim_DecrRefCount(interp, ((Jim_Var *)val)->objPtr);
    Jim_Free(val);
}

static const Jim_HashTableType JimVariablesHashTableType = {
    JimStringCopyHTHashFunction,        
    JimStringCopyHTDup,                 
    NULL,                               
    JimStringCopyHTKeyCompare,  
    JimStringCopyHTKeyDestructor,       
    JimVariablesHTValDestructor 
    JimStringCopyHTHashFunction,
    JimStringCopyHTDup,
    NULL,
    JimStringCopyHTKeyCompare,
    JimStringCopyHTKeyDestructor,
    JimVariablesHTValDestructor
};

static void JimCommandsHT_ValDestructor(void *interp, void *val)
{
    JimDecrCmdRefCount(interp, val);
}

static const Jim_HashTableType JimCommandsHashTableType = {
    JimStringCopyHTHashFunction,    
    JimStringCopyHTDup,             
    NULL,                           
    JimStringCopyHTKeyCompare,      
    JimStringCopyHTKeyDestructor,   
    JimCommandsHT_ValDestructor     
    JimStringCopyHTHashFunction,
    JimStringCopyHTDup,
    NULL,
    JimStringCopyHTKeyCompare,
    JimStringCopyHTKeyDestructor,
    JimCommandsHT_ValDestructor
};



#ifdef jim_ext_namespace
static Jim_Obj *JimQualifyNameObj(Jim_Interp *interp, Jim_Obj *nsObj)
{
    const char *name = Jim_String(nsObj);
    if (name[0] == ':' && name[1] == ':') {
        

        while (*++name == ':') {
        }
        nsObj = Jim_NewStringObj(interp, name, -1);
    }
    else if (Jim_Length(interp->framePtr->nsObj)) {
        

        nsObj = Jim_DuplicateObj(interp, interp->framePtr->nsObj);
        Jim_AppendStrings(interp, nsObj, "::", name, NULL);
    }
    return nsObj;
}

Jim_Obj *Jim_MakeGlobalNamespaceName(Jim_Interp *interp, Jim_Obj *nameObjPtr)
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
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







-
+




-
+












-
+


















-
+





-
+














-
+







}

static const char *JimQualifyName(Jim_Interp *interp, const char *name, Jim_Obj **objPtrPtr)
{
    Jim_Obj *objPtr = interp->emptyObj;

    if (name[0] == ':' && name[1] == ':') {
        

        while (*++name == ':') {
        }
    }
    else if (Jim_Length(interp->framePtr->nsObj)) {
        

        objPtr = Jim_DuplicateObj(interp, interp->framePtr->nsObj);
        Jim_AppendStrings(interp, objPtr, "::", name, NULL);
        name = Jim_String(objPtr);
    }
    Jim_IncrRefCount(objPtr);
    *objPtrPtr = objPtr;
    return name;
}

    #define JimFreeQualifiedName(INTERP, OBJ) Jim_DecrRefCount((INTERP), (OBJ))

#else
    

    #define JimQualifyName(INTERP, NAME, DUMMY) (((NAME)[0] == ':' && (NAME)[1] == ':') ? (NAME) + 2 : (NAME))
    #define JimFreeQualifiedName(INTERP, DUMMY) (void)(DUMMY)

Jim_Obj *Jim_MakeGlobalNamespaceName(Jim_Interp *interp, Jim_Obj *nameObjPtr)
{
    return nameObjPtr;
}
#endif

static int JimCreateCommand(Jim_Interp *interp, const char *name, Jim_Cmd *cmd)
{
    Jim_HashEntry *he = Jim_FindHashEntry(&interp->commands, name);
    if (he) {

        Jim_InterpIncrProcEpoch(interp);
    }

    if (he && interp->local) {
        

        cmd->prevCmd = Jim_GetHashEntryVal(he);
        Jim_SetHashVal(&interp->commands, he, cmd);
    }
    else {
        if (he) {
            

            Jim_DeleteHashEntry(&interp->commands, name);
        }

        Jim_AddHashEntry(&interp->commands, name, cmd);
    }
    return JIM_OK;
}


int Jim_CreateCommand(Jim_Interp *interp, const char *cmdNameStr,
    Jim_CmdProc *cmdProc, void *privData, Jim_DelCmdProc *delProc)
{
    Jim_Cmd *cmdPtr = Jim_Alloc(sizeof(*cmdPtr));

    

    memset(cmdPtr, 0, sizeof(*cmdPtr));
    cmdPtr->inUse = 1;
    cmdPtr->u.native.delProc = delProc;
    cmdPtr->u.native.cmdProc = cmdProc;
    cmdPtr->u.native.privData = privData;

    JimCreateCommand(interp, cmdNameStr, cmdPtr);
9324
9325
9326
9327
9328
9329
9330
9331

9332
9333
9334
9335
9336
9337
9338
9435
9436
9437
9438
9439
9440
9441

9442
9443
9444
9445
9446
9447
9448
9449







-
+







    Jim_InitHashTable(cmdPtr->u.proc.staticVars, &JimVariablesHashTableType, interp);
    for (i = 0; i < len; i++) {
        Jim_Obj *objPtr, *initObjPtr, *nameObjPtr;
        Jim_Var *varPtr;
        int subLen;

        objPtr = Jim_ListGetIndex(interp, staticsListObjPtr, i);
        

        subLen = Jim_ListLength(interp, objPtr);
        if (subLen == 1 || subLen == 2) {
            nameObjPtr = Jim_ListGetIndex(interp, objPtr, 0);
            if (subLen == 1) {
                initObjPtr = Jim_GetVariable(interp, nameObjPtr, JIM_NONE);
                if (initObjPtr == NULL) {
                    Jim_SetResultFormatted(interp,
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
9447
9448
9449
9450

9451
9452
9453
9454
9455
9456
9457
9481
9482
9483
9484
9485
9486
9487

9488
9489
9490
9491
9492
9493
9494
9495

9496
9497
9498
9499
9500
9501
9502
9503
9504
9505
9506
9507
9508
9509
9510
9511
9512

9513
9514
9515
9516
9517
9518
9519
9520
9521
9522
9523
9524
9525
9526
9527

9528
9529
9530
9531
9532


9533
9534
9535
9536
9537
9538
9539
9540

9541
9542
9543
9544
9545
9546
9547
9548
9549
9550
9551
9552
9553
9554
9555

9556
9557
9558
9559
9560

9561
9562
9563
9564
9565
9566
9567
9568







-
+







-
+
















-
+














-
+




-
-
+
+






-
+














-
+




-
+







    return JIM_OK;
}

static void JimUpdateProcNamespace(Jim_Interp *interp, Jim_Cmd *cmdPtr, const char *cmdname)
{
#ifdef jim_ext_namespace
    if (cmdPtr->isproc) {
        

        const char *pt = strrchr(cmdname, ':');
        if (pt && pt != cmdname && pt[-1] == ':') {
            Jim_DecrRefCount(interp, cmdPtr->u.proc.nsObj);
            cmdPtr->u.proc.nsObj = Jim_NewStringObj(interp, cmdname, pt - cmdname - 1);
            Jim_IncrRefCount(cmdPtr->u.proc.nsObj);

            if (Jim_FindHashEntry(&interp->commands, pt + 1)) {
                

                Jim_InterpIncrProcEpoch(interp);
            }
        }
    }
#endif
}

static Jim_Cmd *JimCreateProcedureCmd(Jim_Interp *interp, Jim_Obj *argListObjPtr,
    Jim_Obj *staticsListObjPtr, Jim_Obj *bodyObjPtr, Jim_Obj *nsObj)
{
    Jim_Cmd *cmdPtr;
    int argListLen;
    int i;

    argListLen = Jim_ListLength(interp, argListObjPtr);

    

    cmdPtr = Jim_Alloc(sizeof(*cmdPtr) + sizeof(struct Jim_ProcArg) * argListLen);
    memset(cmdPtr, 0, sizeof(*cmdPtr));
    cmdPtr->inUse = 1;
    cmdPtr->isproc = 1;
    cmdPtr->u.proc.argListObjPtr = argListObjPtr;
    cmdPtr->u.proc.argListLen = argListLen;
    cmdPtr->u.proc.bodyObjPtr = bodyObjPtr;
    cmdPtr->u.proc.argsPos = -1;
    cmdPtr->u.proc.arglist = (struct Jim_ProcArg *)(cmdPtr + 1);
    cmdPtr->u.proc.nsObj = nsObj ? nsObj : interp->emptyObj;
    Jim_IncrRefCount(argListObjPtr);
    Jim_IncrRefCount(bodyObjPtr);
    Jim_IncrRefCount(cmdPtr->u.proc.nsObj);

    

    if (staticsListObjPtr && JimCreateProcedureStatics(interp, cmdPtr, staticsListObjPtr) != JIM_OK) {
        goto err;
    }

    
    


    for (i = 0; i < argListLen; i++) {
        Jim_Obj *argPtr;
        Jim_Obj *nameObjPtr;
        Jim_Obj *defaultObjPtr;
        int len;

        

        argPtr = Jim_ListGetIndex(interp, argListObjPtr, i);
        len = Jim_ListLength(interp, argPtr);
        if (len == 0) {
            Jim_SetResultString(interp, "argument with no name", -1);
err:
            JimDecrCmdRefCount(interp, cmdPtr);
            return NULL;
        }
        if (len > 2) {
            Jim_SetResultFormatted(interp, "too many fields in argument specifier \"%#s\"", argPtr);
            goto err;
        }

        if (len == 2) {
            

            nameObjPtr = Jim_ListGetIndex(interp, argPtr, 0);
            defaultObjPtr = Jim_ListGetIndex(interp, argPtr, 1);
        }
        else {
            

            nameObjPtr = argPtr;
            defaultObjPtr = NULL;
        }


        if (Jim_CompareStringImmediate(interp, nameObjPtr, "args")) {
            if (cmdPtr->u.proc.argsPos >= 0) {
9508
9509
9510
9511
9512
9513
9514
9515

9516
9517
9518
9519
9520
9521
9522
9523
9524

9525
9526
9527
9528
9529
9530

9531
9532
9533

9534
9535
9536
9537
9538
9539
9540
9619
9620
9621
9622
9623
9624
9625

9626
9627
9628
9629
9630
9631
9632
9633
9634

9635
9636
9637
9638
9639
9640

9641
9642
9643

9644
9645
9646
9647
9648
9649
9650
9651







-
+








-
+





-
+


-
+







    if (newName[0] == 0) {
        return Jim_DeleteCommand(interp, oldName);
    }

    fqold = JimQualifyName(interp, oldName, &qualifiedOldNameObj);
    fqnew = JimQualifyName(interp, newName, &qualifiedNewNameObj);

    

    he = Jim_FindHashEntry(&interp->commands, fqold);
    if (he == NULL) {
        Jim_SetResultFormatted(interp, "can't rename \"%s\": command doesn't exist", oldName);
    }
    else if (Jim_FindHashEntry(&interp->commands, fqnew)) {
        Jim_SetResultFormatted(interp, "can't rename to \"%s\": command already exists", newName);
    }
    else {
        

        cmdPtr = Jim_GetHashEntryVal(he);
        JimIncrCmdRefCount(cmdPtr);
        JimUpdateProcNamespace(interp, cmdPtr, fqnew);
        Jim_AddHashEntry(&interp->commands, fqnew, cmdPtr);

        

        Jim_DeleteHashEntry(&interp->commands, fqold);

        

        Jim_InterpIncrProcEpoch(interp);

        ret = JIM_OK;
    }

    JimFreeQualifiedName(interp, qualifiedOldNameObj);
    JimFreeQualifiedName(interp, qualifiedNewNameObj);
9569
9570
9571
9572
9573
9574
9575
9576

9577
9578

9579
9580
9581
9582
9583
9584
9585
9586
9587
9588

9589
9590
9591
9592
9593
9594
9595
9596
9597
9598
9599

9600
9601
9602
9603
9604
9605
9606
9607
9608
9609
9610
9611
9612

9613
9614
9615
9616
9617
9618
9619
9620
9621
9622
9623
9624
9625
9626
9627
9628
9629
9630
9631

9632
9633
9634
9635
9636
9637
9638
9639
9640
9641
9642
9643
9644
9645

9646
9647
9648
9649
9650
9651
9652
9653
9654
9655
9656
9657
9658
9659
9660
9661
9662
9663
9664
9665

9666
9667
9668
9669

9670
9671
9672

9673
9674
9675
9676
9677
9678
9679
9680
9681
9682
9683
9684

9685
9686
9687
9688
9689
9690
9691
9692
9693
9694
9695
9696
9697
9698
9699
9700

9701
9702
9703
9704

9705
9706
9707
9708
9709
9710
9711
9712

9713
9714
9715
9716
9717
9718
9719
9720
9721
9722
9723
9724
9725
9726
9727
9728
9729
9730
9731

9732
9733
9734
9735
9736
9737
9738
9739
9740
9741
9742
9743
9744
9745
9746
9747
9748
9749
9750

9751
9752
9753

9754
9755
9756
9757
9758
9759
9760
9680
9681
9682
9683
9684
9685
9686

9687
9688

9689
9690
9691
9692
9693
9694
9695
9696
9697
9698

9699
9700
9701
9702
9703
9704
9705
9706
9707
9708
9709

9710
9711
9712
9713
9714
9715
9716
9717
9718
9719
9720
9721
9722

9723
9724
9725
9726
9727
9728
9729
9730
9731
9732
9733
9734
9735
9736
9737
9738
9739
9740
9741

9742
9743
9744
9745
9746
9747
9748
9749
9750
9751
9752
9753
9754
9755

9756
9757
9758
9759
9760
9761
9762
9763
9764
9765
9766
9767
9768
9769
9770
9771
9772
9773
9774
9775

9776
9777
9778
9779

9780
9781
9782

9783
9784
9785
9786
9787
9788
9789
9790
9791
9792
9793
9794

9795
9796
9797
9798
9799
9800
9801
9802
9803
9804
9805
9806
9807
9808
9809
9810

9811
9812
9813
9814

9815
9816
9817
9818
9819
9820
9821
9822

9823
9824
9825
9826
9827
9828
9829
9830
9831
9832
9833
9834
9835
9836
9837
9838
9839
9840
9841

9842
9843
9844
9845
9846
9847
9848
9849
9850
9851
9852
9853
9854
9855
9856
9857
9858
9859
9860

9861
9862
9863

9864
9865
9866
9867
9868
9869
9870
9871







-
+

-
+









-
+










-
+












-
+


















-
+













-
+



















-
+



-
+


-
+











-
+















-
+



-
+







-
+


















-
+


















-
+


-
+








    if (objPtr->typePtr != &commandObjType ||
            objPtr->internalRep.cmdValue.procEpoch != interp->procEpoch
#ifdef jim_ext_namespace
            || !Jim_StringEqObj(objPtr->internalRep.cmdValue.nsObj, interp->framePtr->nsObj)
#endif
        ) {
        


        

        const char *name = Jim_String(objPtr);
        Jim_HashEntry *he;

        if (name[0] == ':' && name[1] == ':') {
            while (*++name == ':') {
            }
        }
#ifdef jim_ext_namespace
        else if (Jim_Length(interp->framePtr->nsObj)) {
            

            Jim_Obj *nameObj = Jim_DuplicateObj(interp, interp->framePtr->nsObj);
            Jim_AppendStrings(interp, nameObj, "::", name, NULL);
            he = Jim_FindHashEntry(&interp->commands, Jim_String(nameObj));
            Jim_FreeNewObj(interp, nameObj);
            if (he) {
                goto found;
            }
        }
#endif

        

        he = Jim_FindHashEntry(&interp->commands, name);
        if (he == NULL) {
            if (flags & JIM_ERRMSG) {
                Jim_SetResultFormatted(interp, "invalid command name \"%#s\"", objPtr);
            }
            return NULL;
        }
#ifdef jim_ext_namespace
found:
#endif
        cmd = Jim_GetHashEntryVal(he);

        

        Jim_FreeIntRep(interp, objPtr);
        objPtr->typePtr = &commandObjType;
        objPtr->internalRep.cmdValue.procEpoch = interp->procEpoch;
        objPtr->internalRep.cmdValue.cmdPtr = cmd;
        objPtr->internalRep.cmdValue.nsObj = interp->framePtr->nsObj;
        Jim_IncrRefCount(interp->framePtr->nsObj);
    }
    else {
        cmd = objPtr->internalRep.cmdValue.cmdPtr;
    }
    while (cmd->u.proc.upcall) {
        cmd = cmd->prevCmd;
    }
    return cmd;
}



#define JIM_DICT_SUGAR 100      
#define JIM_DICT_SUGAR 100

static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);

static const Jim_ObjType variableObjType = {
    "variable",
    NULL,
    NULL,
    NULL,
    JIM_TYPE_REFERENCES,
};

static int JimValidName(Jim_Interp *interp, const char *type, Jim_Obj *nameObjPtr)
{
    

    if (nameObjPtr->typePtr != &variableObjType) {
        int len;
        const char *str = Jim_GetString(nameObjPtr, &len);
        if (memchr(str, '\0', len)) {
            Jim_SetResultFormatted(interp, "%s name contains embedded null", type);
            return JIM_ERR;
        }
    }
    return JIM_OK;
}

static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
{
    const char *varName;
    Jim_CallFrame *framePtr;
    Jim_HashEntry *he;
    int global;
    int len;

    

    if (objPtr->typePtr == &variableObjType) {
        framePtr = objPtr->internalRep.varValue.global ? interp->topFramePtr : interp->framePtr;
        if (objPtr->internalRep.varValue.callFrameId == framePtr->id) {
            

            return JIM_OK;
        }
        

    }
    else if (objPtr->typePtr == &dictSubstObjType) {
        return JIM_DICT_SUGAR;
    }
    else if (JimValidName(interp, "variable", objPtr) != JIM_OK) {
        return JIM_ERR;
    }


    varName = Jim_GetString(objPtr, &len);

    

    if (len && varName[len - 1] == ')' && strchr(varName, '(') != NULL) {
        return JIM_DICT_SUGAR;
    }

    if (varName[0] == ':' && varName[1] == ':') {
        while (*++varName == ':') {
        }
        global = 1;
        framePtr = interp->topFramePtr;
    }
    else {
        global = 0;
        framePtr = interp->framePtr;
    }

    

    he = Jim_FindHashEntry(&framePtr->vars, varName);
    if (he == NULL) {
        if (!global && framePtr->staticVars) {
            

            he = Jim_FindHashEntry(framePtr->staticVars, varName);
        }
        if (he == NULL) {
            return JIM_ERR;
        }
    }

    

    Jim_FreeIntRep(interp, objPtr);
    objPtr->typePtr = &variableObjType;
    objPtr->internalRep.varValue.callFrameId = framePtr->id;
    objPtr->internalRep.varValue.varPtr = Jim_GetHashEntryVal(he);
    objPtr->internalRep.varValue.global = global;
    return JIM_OK;
}


static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *ObjPtr, Jim_Obj *valObjPtr);
static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *ObjPtr, int flags);

static Jim_Var *JimCreateVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr)
{
    const char *name;
    Jim_CallFrame *framePtr;
    int global;

    

    Jim_Var *var = Jim_Alloc(sizeof(*var));

    var->objPtr = valObjPtr;
    Jim_IncrRefCount(valObjPtr);
    var->linkFramePtr = NULL;

    name = Jim_String(nameObjPtr);
    if (name[0] == ':' && name[1] == ':') {
        while (*++name == ':') {
        }
        framePtr = interp->topFramePtr;
        global = 1;
    }
    else {
        framePtr = interp->framePtr;
        global = 0;
    }

    

    Jim_AddHashEntry(&framePtr->vars, name, var);

    

    Jim_FreeIntRep(interp, nameObjPtr);
    nameObjPtr->typePtr = &variableObjType;
    nameObjPtr->internalRep.varValue.callFrameId = framePtr->id;
    nameObjPtr->internalRep.varValue.varPtr = var;
    nameObjPtr->internalRep.varValue.global = global;

    return var;
9780
9781
9782
9783
9784
9785
9786
9787

9788
9789
9790
9791
9792
9793
9794
9891
9892
9893
9894
9895
9896
9897

9898
9899
9900
9901
9902
9903
9904
9905







-
+







        case JIM_OK:
            var = nameObjPtr->internalRep.varValue.varPtr;
            if (var->linkFramePtr == NULL) {
                Jim_IncrRefCount(valObjPtr);
                Jim_DecrRefCount(interp, var->objPtr);
                var->objPtr = valObjPtr;
            }
            else {                  
            else {
                Jim_CallFrame *savedCallFrame;

                savedCallFrame = interp->framePtr;
                interp->framePtr = var->linkFramePtr;
                err = Jim_SetVariable(interp, var->objPtr, valObjPtr);
                interp->framePtr = savedCallFrame;
                if (err != JIM_OK)
9820
9821
9822
9823
9824
9825
9826
9827

9828
9829
9830
9831
9832
9833
9834

9835
9836
9837
9838
9839
9840
9841
9842
9843
9844
9845
9846
9847
9848

9849
9850
9851

9852
9853
9854
9855
9856
9857
9858
9859
9860
9861
9862
9863

9864
9865
9866
9867
9868
9869


9870
9871
9872
9873
9874
9875

9876
9877
9878
9879
9880
9881
9882
9931
9932
9933
9934
9935
9936
9937

9938
9939
9940

9941

9942

9943

9944
9945
9946
9947
9948
9949
9950
9951
9952
9953
9954
9955

9956
9957
9958

9959
9960
9961
9962
9963
9964
9965
9966
9967
9968
9969
9970

9971
9972
9973
9974
9975


9976
9977
9978
9979
9980
9981
9982

9983
9984
9985
9986
9987
9988
9989
9990







-
+


-

-

-
+
-












-
+


-
+











-
+




-
-
+
+





-
+







    result = Jim_SetVariableStr(interp, name, objPtr);
    interp->framePtr = savedFramePtr;
    return result;
}

int Jim_SetVariableStrWithStr(Jim_Interp *interp, const char *name, const char *val)
{
    Jim_Obj *nameObjPtr, *valObjPtr;
    Jim_Obj *valObjPtr;
    int result;

    nameObjPtr = Jim_NewStringObj(interp, name, -1);
    valObjPtr = Jim_NewStringObj(interp, val, -1);
    Jim_IncrRefCount(nameObjPtr);
    Jim_IncrRefCount(valObjPtr);
    result = Jim_SetVariable(interp, nameObjPtr, valObjPtr);
    result = Jim_SetVariableStr(interp, name, valObjPtr);
    Jim_DecrRefCount(interp, nameObjPtr);
    Jim_DecrRefCount(interp, valObjPtr);
    return result;
}

int Jim_SetVariableLink(Jim_Interp *interp, Jim_Obj *nameObjPtr,
    Jim_Obj *targetNameObjPtr, Jim_CallFrame *targetCallFrame)
{
    const char *varName;
    const char *targetName;
    Jim_CallFrame *framePtr;
    Jim_Var *varPtr;

    

    switch (SetVariableFromAny(interp, nameObjPtr)) {
        case JIM_DICT_SUGAR:
            

            Jim_SetResultFormatted(interp, "bad variable name \"%#s\": upvar won't create a scalar variable that looks like an array element", nameObjPtr);
            return JIM_ERR;

        case JIM_OK:
            varPtr = nameObjPtr->internalRep.varValue.varPtr;

            if (varPtr->linkFramePtr == NULL) {
                Jim_SetResultFormatted(interp, "variable \"%#s\" already exists", nameObjPtr);
                return JIM_ERR;
            }

            

            varPtr->linkFramePtr = NULL;
            break;
    }

    
    


    varName = Jim_String(nameObjPtr);

    if (varName[0] == ':' && varName[1] == ':') {
        while (*++varName == ':') {
        }
        

        framePtr = interp->topFramePtr;
    }
    else {
        framePtr = interp->framePtr;
    }

    targetName = Jim_String(targetNameObjPtr);
9892
9893
9894
9895
9896
9897
9898
9899

9900
9901
9902
9903

9904
9905
9906
9907
9908
9909
9910
9911
9912
9913
9914
9915
9916
9917
9918
9919

9920
9921

9922
9923
9924
9925
9926
9927
9928
9929
9930
9931
9932
9933
9934
9935
9936
9937
9938
9939

9940
9941
9942
9943
9944
9945
9946
9947
9948

9949
9950
9951
9952
9953
9954

9955
9956
9957
9958
9959
9960
9961
10000
10001
10002
10003
10004
10005
10006

10007
10008
10009
10010

10011
10012
10013
10014
10015
10016
10017
10018
10019
10020
10021
10022
10023
10024
10025
10026

10027
10028

10029
10030
10031
10032
10033
10034
10035
10036
10037
10038
10039
10040
10041
10042
10043
10044
10045
10046

10047
10048
10049
10050
10051
10052
10053
10054
10055

10056
10057
10058
10059
10060
10061

10062
10063
10064
10065
10066
10067
10068
10069







-
+



-
+















-
+

-
+

















-
+








-
+





-
+







        Jim_SetResultFormatted(interp,
            "bad variable name \"%#s\": upvar won't create namespace variable that refers to procedure variable",
            nameObjPtr);
        Jim_DecrRefCount(interp, targetNameObjPtr);
        return JIM_ERR;
    }

    

    if (framePtr == targetCallFrame) {
        Jim_Obj *objPtr = targetNameObjPtr;

        

        while (1) {
            if (strcmp(Jim_String(objPtr), varName) == 0) {
                Jim_SetResultString(interp, "can't upvar from variable to itself", -1);
                Jim_DecrRefCount(interp, targetNameObjPtr);
                return JIM_ERR;
            }
            if (SetVariableFromAny(interp, objPtr) != JIM_OK)
                break;
            varPtr = objPtr->internalRep.varValue.varPtr;
            if (varPtr->linkFramePtr != targetCallFrame)
                break;
            objPtr = varPtr->objPtr;
        }
    }

    

    Jim_SetVariable(interp, nameObjPtr, targetNameObjPtr);
    

    nameObjPtr->internalRep.varValue.varPtr->linkFramePtr = targetCallFrame;
    Jim_DecrRefCount(interp, targetNameObjPtr);
    return JIM_OK;
}

Jim_Obj *Jim_GetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
{
    switch (SetVariableFromAny(interp, nameObjPtr)) {
        case JIM_OK:{
                Jim_Var *varPtr = nameObjPtr->internalRep.varValue.varPtr;

                if (varPtr->linkFramePtr == NULL) {
                    return varPtr->objPtr;
                }
                else {
                    Jim_Obj *objPtr;

                    

                    Jim_CallFrame *savedCallFrame = interp->framePtr;

                    interp->framePtr = varPtr->linkFramePtr;
                    objPtr = Jim_GetVariable(interp, varPtr->objPtr, flags);
                    interp->framePtr = savedCallFrame;
                    if (objPtr) {
                        return objPtr;
                    }
                    

                }
            }
            break;

        case JIM_DICT_SUGAR:
            

            return JimDictSugarGet(interp, nameObjPtr, flags);
    }
    if (flags & JIM_ERRMSG) {
        Jim_SetResultFormatted(interp, "can't read \"%#s\": no such variable", nameObjPtr);
    }
    return NULL;
}
10001
10002
10003
10004
10005
10006
10007
10008

10009
10010
10011
10012
10013
10014

10015
10016
10017
10018
10019
10020
10021
10022
10023
10024
10025
10026
10027
10028
10029
10030
10031
10032
10033

10034
10035
10036
10037
10038
10039
10040
10109
10110
10111
10112
10113
10114
10115

10116
10117
10118
10119
10120
10121

10122
10123
10124
10125
10126
10127
10128
10129
10130
10131
10132
10133
10134
10135
10136
10137
10138
10139
10140

10141
10142
10143
10144
10145
10146
10147
10148







-
+





-
+


















-
+







{
    Jim_Var *varPtr;
    int retval;
    Jim_CallFrame *framePtr;

    retval = SetVariableFromAny(interp, nameObjPtr);
    if (retval == JIM_DICT_SUGAR) {
        

        return JimDictSugarSet(interp, nameObjPtr, NULL);
    }
    else if (retval == JIM_OK) {
        varPtr = nameObjPtr->internalRep.varValue.varPtr;

        

        if (varPtr->linkFramePtr) {
            framePtr = interp->framePtr;
            interp->framePtr = varPtr->linkFramePtr;
            retval = Jim_UnsetVariable(interp, varPtr->objPtr, JIM_NONE);
            interp->framePtr = framePtr;
        }
        else {
            const char *name = Jim_String(nameObjPtr);
            if (nameObjPtr->internalRep.varValue.global) {
                name += 2;
                framePtr = interp->topFramePtr;
            }
            else {
                framePtr = interp->framePtr;
            }

            retval = Jim_DeleteHashEntry(&framePtr->vars, name);
            if (retval == JIM_OK) {
                

                framePtr->id = interp->callFrameEpoch++;
            }
        }
    }
    if (retval != JIM_OK && (flags & JIM_ERRMSG)) {
        Jim_SetResultFormatted(interp, "can't unset \"%#s\": no such variable", nameObjPtr);
    }
10059
10060
10061
10062
10063
10064
10065
10066

10067
10068
10069
10070
10071
10072
10073
10074
10075
10076
10077
10078
10079
10080
10081
10082
10083
10084
10085

10086
10087
10088
10089
10090

10091
10092
10093
10094
10095
10096
10097

10098
10099
10100
10101
10102
10103
10104
10167
10168
10169
10170
10171
10172
10173

10174
10175
10176
10177
10178
10179
10180
10181
10182
10183
10184
10185
10186
10187
10188
10189
10190
10191
10192

10193
10194
10195
10196
10197

10198
10199
10200
10201
10202
10203
10204

10205
10206
10207
10208
10209
10210
10211
10212







-
+


















-
+




-
+






-
+








    p++;
    keyLen = (str + len) - p;
    if (str[len - 1] == ')') {
        keyLen--;
    }

    

    keyObjPtr = Jim_NewStringObj(interp, p, keyLen);

    Jim_IncrRefCount(varObjPtr);
    Jim_IncrRefCount(keyObjPtr);
    *varPtrPtr = varObjPtr;
    *keyPtrPtr = keyObjPtr;
}

static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *valObjPtr)
{
    int err;

    SetDictSubstFromAny(interp, objPtr);

    err = Jim_SetDictKeysVector(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr,
        &objPtr->internalRep.dictSubstValue.indexObjPtr, 1, valObjPtr, JIM_MUSTEXIST);

    if (err == JIM_OK) {
        

        Jim_SetEmptyResult(interp);
    }
    else {
        if (!valObjPtr) {
            

            if (Jim_GetVariable(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr, JIM_NONE)) {
                Jim_SetResultFormatted(interp, "can't unset \"%#s\": no such element in array",
                    objPtr);
                return err;
            }
        }
        

        Jim_SetResultFormatted(interp, "can't %s \"%#s\": variable isn't array",
            (valObjPtr ? "set" : "unset"), objPtr);
    }
    return err;
}

static Jim_Obj *JimDictExpandArrayVariable(Jim_Interp *interp, Jim_Obj *varObjPtr,
10116
10117
10118
10119
10120
10121
10122
10123

10124
10125
10126
10127
10128
10129
10130
10224
10225
10226
10227
10228
10229
10230

10231
10232
10233
10234
10235
10236
10237
10238







-
+







    ret = Jim_DictKey(interp, dictObjPtr, keyObjPtr, &resObjPtr, JIM_NONE);
    if (ret != JIM_OK) {
        Jim_SetResultFormatted(interp,
            "can't read \"%#s(%#s)\": %s array", varObjPtr, keyObjPtr,
            ret < 0 ? "variable isn't" : "no such element in");
    }
    else if ((flags & JIM_UNSHARED) && Jim_IsShared(dictObjPtr)) {
        

        Jim_SetVariable(interp, varObjPtr, Jim_DuplicateObj(interp, dictObjPtr));
    }

    return resObjPtr;
}


10141
10142
10143
10144
10145
10146
10147
10148

10149
10150
10151
10152
10153
10154




10155
10156
10157
10158
10159
10160
10161
10162
10163
10164
10165

10166
10167
10168
10169
10170
10171
10172
10249
10250
10251
10252
10253
10254
10255

10256
10257

10258



10259
10260
10261
10262

10263
10264
10265
10266
10267
10268
10269
10270
10271

10272
10273
10274
10275
10276
10277
10278
10279







-
+

-

-
-
-
+
+
+
+
-









-
+








void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
{
    Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr);
    Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr);
}

void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
static void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
{
    JIM_NOTUSED(interp);

    dupPtr->internalRep.dictSubstValue.varNameObjPtr =
        srcPtr->internalRep.dictSubstValue.varNameObjPtr;
    dupPtr->internalRep.dictSubstValue.indexObjPtr = srcPtr->internalRep.dictSubstValue.indexObjPtr;
    dupPtr->internalRep = srcPtr->internalRep;

    Jim_IncrRefCount(dupPtr->internalRep.dictSubstValue.varNameObjPtr);
    Jim_IncrRefCount(dupPtr->internalRep.dictSubstValue.indexObjPtr);
    dupPtr->typePtr = &dictSubstObjType;
}


static void SetDictSubstFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
{
    if (objPtr->typePtr != &dictSubstObjType) {
        Jim_Obj *varObjPtr, *keyObjPtr;

        if (objPtr->typePtr == &interpolatedObjType) {
            


            varObjPtr = objPtr->internalRep.dictSubstValue.varNameObjPtr;
            keyObjPtr = objPtr->internalRep.dictSubstValue.indexObjPtr;

            Jim_IncrRefCount(varObjPtr);
            Jim_IncrRefCount(keyObjPtr);
        }
10200
10201
10202
10203
10204
10205
10206
10207
10208
10209
10210


10211
10212
10213
10214
10215
10216
10217
10218
10219
10307
10308
10309
10310
10311
10312
10313




10314
10315


10316
10317
10318
10319
10320
10321
10322







-
-
-
-
+
+
-
-







    Jim_DecrRefCount(interp, substKeyObjPtr);

    return resObjPtr;
}

static Jim_Obj *JimExpandExprSugar(Jim_Interp *interp, Jim_Obj *objPtr)
{
    Jim_Obj *resultObjPtr;

    if (Jim_EvalExpression(interp, objPtr, &resultObjPtr) == JIM_OK) {
        
    if (Jim_EvalExpression(interp, objPtr) == JIM_OK) {
        return Jim_GetResult(interp);
        resultObjPtr->refCount--;
        return resultObjPtr;
    }
    return NULL;
}


static Jim_CallFrame *JimCreateCallFrame(Jim_Interp *interp, Jim_CallFrame *parent, Jim_Obj *nsObj)
{
10247
10248
10249
10250
10251
10252
10253
10254

10255
10256
10257
10258
10259
10260
10261
10262
10263
10264
10265
10266
10267
10268
10269
10270
10271
10272
10273

10274
10275
10276

10277
10278
10279
10280
10281
10282

10283
10284
10285
10286
10287
10288
10289
10290
10291
10292

10293
10294
10295
















































10296
10297
10298
10299
10300
10301
10302
10350
10351
10352
10353
10354
10355
10356

10357
10358
10359
10360
10361
10362
10363
10364
10365
10366
10367
10368
10369
10370
10371
10372
10373
10374
10375

10376
10377
10378

10379
10380
10381
10382
10383

10384
10385
10386
10387
10388
10389
10390
10391
10392
10393
10394
10395
10396



10397
10398
10399
10400
10401
10402
10403
10404
10405
10406
10407
10408
10409
10410
10411
10412
10413
10414
10415
10416
10417
10418
10419
10420
10421
10422
10423
10424
10425
10426
10427
10428
10429
10430
10431
10432
10433
10434
10435
10436
10437
10438
10439
10440
10441
10442
10443
10444
10445
10446
10447
10448
10449
10450
10451







-
+


















-
+


-
+




-

+










+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







    Jim_IncrRefCount(nsObj);

    return cf;
}

static int JimDeleteLocalProcs(Jim_Interp *interp, Jim_Stack *localCommands)
{
    

    if (localCommands) {
        Jim_Obj *cmdNameObj;

        while ((cmdNameObj = Jim_StackPop(localCommands)) != NULL) {
            Jim_HashEntry *he;
            Jim_Obj *fqObjName;
            Jim_HashTable *ht = &interp->commands;

            const char *fqname = JimQualifyName(interp, Jim_String(cmdNameObj), &fqObjName);

            he = Jim_FindHashEntry(ht, fqname);

            if (he) {
                Jim_Cmd *cmd = Jim_GetHashEntryVal(he);
                if (cmd->prevCmd) {
                    Jim_Cmd *prevCmd = cmd->prevCmd;
                    cmd->prevCmd = NULL;

                    

                    JimDecrCmdRefCount(interp, cmd);

                    

                    Jim_SetHashVal(ht, he, prevCmd);
                }
                else {
                    Jim_DeleteHashEntry(ht, fqname);
                    Jim_InterpIncrProcEpoch(interp);
                }
                Jim_InterpIncrProcEpoch(interp);
            }
            Jim_DecrRefCount(interp, cmdNameObj);
            JimFreeQualifiedName(interp, fqObjName);
        }
        Jim_FreeStack(localCommands);
        Jim_Free(localCommands);
    }
    return JIM_OK;
}

static int JimInvokeDefer(Jim_Interp *interp, int retcode)

#define JIM_FCF_FULL 0          
#define JIM_FCF_REUSE 1         
{
    Jim_Obj *objPtr;


    if (Jim_FindHashEntry(&interp->framePtr->vars, "jim::defer") == NULL) {
        return retcode;
    }

    objPtr = Jim_GetVariableStr(interp, "jim::defer", JIM_NONE);

    if (objPtr) {
        int ret = JIM_OK;
        int i;
        int listLen = Jim_ListLength(interp, objPtr);
        Jim_Obj *resultObjPtr;

        Jim_IncrRefCount(objPtr);

        resultObjPtr = Jim_GetResult(interp);
        Jim_IncrRefCount(resultObjPtr);
        Jim_SetEmptyResult(interp);


        for (i = listLen; i > 0; i--) {

            Jim_Obj *scriptObjPtr = Jim_ListGetIndex(interp, objPtr, i - 1);
            ret = Jim_EvalObj(interp, scriptObjPtr);
            if (ret != JIM_OK) {
                break;
            }
        }

        if (ret == JIM_OK || retcode == JIM_ERR) {

            Jim_SetResult(interp, resultObjPtr);
        }
        else {
            retcode = ret;
        }

        Jim_DecrRefCount(interp, resultObjPtr);
        Jim_DecrRefCount(interp, objPtr);
    }
    return retcode;
}

#define JIM_FCF_FULL 0
#define JIM_FCF_REUSE 1
static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int action)
 {
    JimDeleteLocalProcs(interp, cf->localCommands);

    if (cf->procArgsObjPtr)
        Jim_DecrRefCount(interp, cf->procArgsObjPtr);
    if (cf->procBodyObjPtr)
10325
10326
10327
10328
10329
10330
10331
10332
10333
10334
10335
10336
10337
10338
10339
10340
10341
10342
10343
10344
10345
10346
10347
10348
10349
10350
10351
10352
10353
10354
10355
10356
10357
10358
10359
10360
10361
10362
10363
10364
10365
10366
10367
10368
10369
10370
10371
10372
10373
10374
10375
10376
10377
10378
10379
10380
10381
10382
10383
10384
10385
10386
10387
10388
10389
10390
10391
10392
10393
10394
10395
10396
10397
10398
10399
10400
10401
10402
10403
10404
10405
10406
10407
10408
10409
10410
10411
10412
10413
10414
10415
10416
10417
10418
10419
10420
10421
10422
10423
10424
10425
10426
10427
10428
10429
10430
10431
10432
10433
10434
10435
10436
10437
10438
10439
10440
10441
10442
10443
10444
10445
10446
10447
10448
10449
10450
10451
10452
10453
10454
10455
10456
10457
10458
10459
10460
10461
10462
10463
10464
10465
10466
10467
10468
10469
10470
10471
10472
10473
10474
10475
10476
10477
10478
10479
10480
10481
10482
10483
10484
10485
10486
10487
10488
10489
10490
10491
10492
10493
10494
10495
10496
10497
10498
10499
10500
10501
10502
10503
10504
10505
10506
10507
10508
10509
10510
10511
10512
10513
10514
10515
10516
10517
10518
10519
10520
10521
10522
10523
10524
10525
10526
10527
10528
10529
10530
10531
10532
10533
10534
10535
10536
10537
10538
10539
10540
10541
10542
10543
10544
10545
10546
10547
10548
10549
10550
10551
10552
10553
10554
10555
10556
10557
10558
10559
10560
10561
10562
10563
10564
10565
10566
10567
10568
10569
10570
10571
10572
10573
10574
10575
10576
10577
10578
10579
10580
10581
10582
10583
10584
10585
10586
10587
10588
10589
10590
10591
10474
10475
10476
10477
10478
10479
10480





























































































































































































































































10481
10482
10483
10484
10485
10486
10487







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-







        cf->vars.used = 0;
    }
    cf->next = interp->freeFramesList;
    interp->freeFramesList = cf;
}


#ifdef JIM_REFERENCES

static void JimReferencesHTValDestructor(void *interp, void *val)
{
    Jim_Reference *refPtr = (void *)val;

    Jim_DecrRefCount(interp, refPtr->objPtr);
    if (refPtr->finalizerCmdNamePtr != NULL) {
        Jim_DecrRefCount(interp, refPtr->finalizerCmdNamePtr);
    }
    Jim_Free(val);
}

static unsigned int JimReferencesHTHashFunction(const void *key)
{
    
    const unsigned long *widePtr = key;
    unsigned int intValue = (unsigned int)*widePtr;

    return Jim_IntHashFunction(intValue);
}

static void *JimReferencesHTKeyDup(void *privdata, const void *key)
{
    void *copy = Jim_Alloc(sizeof(unsigned long));

    JIM_NOTUSED(privdata);

    memcpy(copy, key, sizeof(unsigned long));
    return copy;
}

static int JimReferencesHTKeyCompare(void *privdata, const void *key1, const void *key2)
{
    JIM_NOTUSED(privdata);

    return memcmp(key1, key2, sizeof(unsigned long)) == 0;
}

static void JimReferencesHTKeyDestructor(void *privdata, void *key)
{
    JIM_NOTUSED(privdata);

    Jim_Free(key);
}

static const Jim_HashTableType JimReferencesHashTableType = {
    JimReferencesHTHashFunction,        
    JimReferencesHTKeyDup,      
    NULL,                       
    JimReferencesHTKeyCompare,  
    JimReferencesHTKeyDestructor,       
    JimReferencesHTValDestructor        
};



#define JIM_REFERENCE_SPACE (35+JIM_REFERENCE_TAGLEN)

static int JimFormatReference(char *buf, Jim_Reference *refPtr, unsigned long id)
{
    const char *fmt = "<reference.<%s>.%020lu>";

    sprintf(buf, fmt, refPtr->tag, id);
    return JIM_REFERENCE_SPACE;
}

static void UpdateStringOfReference(struct Jim_Obj *objPtr);

static const Jim_ObjType referenceObjType = {
    "reference",
    NULL,
    NULL,
    UpdateStringOfReference,
    JIM_TYPE_REFERENCES,
};

static void UpdateStringOfReference(struct Jim_Obj *objPtr)
{
    char buf[JIM_REFERENCE_SPACE + 1];

    JimFormatReference(buf, objPtr->internalRep.refValue.refPtr, objPtr->internalRep.refValue.id);
    JimSetStringBytes(objPtr, buf);
}

static int isrefchar(int c)
{
    return (c == '_' || isalnum(c));
}

static int SetReferenceFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
{
    unsigned long value;
    int i, len;
    const char *str, *start, *end;
    char refId[21];
    Jim_Reference *refPtr;
    Jim_HashEntry *he;
    char *endptr;

    
    str = Jim_GetString(objPtr, &len);
    
    if (len < JIM_REFERENCE_SPACE)
        goto badformat;
    
    start = str;
    end = str + len - 1;
    while (*start == ' ')
        start++;
    while (*end == ' ' && end > start)
        end--;
    if (end - start + 1 != JIM_REFERENCE_SPACE)
        goto badformat;
    
    if (memcmp(start, "<reference.<", 12) != 0)
        goto badformat;
    if (start[12 + JIM_REFERENCE_TAGLEN] != '>' || end[0] != '>')
        goto badformat;
    
    for (i = 0; i < JIM_REFERENCE_TAGLEN; i++) {
        if (!isrefchar(start[12 + i]))
            goto badformat;
    }
    
    memcpy(refId, start + 14 + JIM_REFERENCE_TAGLEN, 20);
    refId[20] = '\0';
    
    value = strtoul(refId, &endptr, 10);
    if (JimCheckConversion(refId, endptr) != JIM_OK)
        goto badformat;
    
    he = Jim_FindHashEntry(&interp->references, &value);
    if (he == NULL) {
        Jim_SetResultFormatted(interp, "invalid reference id \"%#s\"", objPtr);
        return JIM_ERR;
    }
    refPtr = Jim_GetHashEntryVal(he);
    
    Jim_FreeIntRep(interp, objPtr);
    objPtr->typePtr = &referenceObjType;
    objPtr->internalRep.refValue.id = value;
    objPtr->internalRep.refValue.refPtr = refPtr;
    return JIM_OK;

  badformat:
    Jim_SetResultFormatted(interp, "expected reference but got \"%#s\"", objPtr);
    return JIM_ERR;
}

Jim_Obj *Jim_NewReference(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *tagPtr, Jim_Obj *cmdNamePtr)
{
    struct Jim_Reference *refPtr;
    unsigned long id;
    Jim_Obj *refObjPtr;
    const char *tag;
    int tagLen, i;

    
    Jim_CollectIfNeeded(interp);

    refPtr = Jim_Alloc(sizeof(*refPtr));
    refPtr->objPtr = objPtr;
    Jim_IncrRefCount(objPtr);
    refPtr->finalizerCmdNamePtr = cmdNamePtr;
    if (cmdNamePtr)
        Jim_IncrRefCount(cmdNamePtr);
    id = interp->referenceNextId++;
    Jim_AddHashEntry(&interp->references, &id, refPtr);
    refObjPtr = Jim_NewObj(interp);
    refObjPtr->typePtr = &referenceObjType;
    refObjPtr->bytes = NULL;
    refObjPtr->internalRep.refValue.id = id;
    refObjPtr->internalRep.refValue.refPtr = refPtr;
    interp->referenceNextId++;
    tag = Jim_GetString(tagPtr, &tagLen);
    if (tagLen > JIM_REFERENCE_TAGLEN)
        tagLen = JIM_REFERENCE_TAGLEN;
    for (i = 0; i < JIM_REFERENCE_TAGLEN; i++) {
        if (i < tagLen && isrefchar(tag[i]))
            refPtr->tag[i] = tag[i];
        else
            refPtr->tag[i] = '_';
    }
    refPtr->tag[JIM_REFERENCE_TAGLEN] = '\0';
    return refObjPtr;
}

Jim_Reference *Jim_GetReference(Jim_Interp *interp, Jim_Obj *objPtr)
{
    if (objPtr->typePtr != &referenceObjType && SetReferenceFromAny(interp, objPtr) == JIM_ERR)
        return NULL;
    return objPtr->internalRep.refValue.refPtr;
}

int Jim_SetFinalizer(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *cmdNamePtr)
{
    Jim_Reference *refPtr;

    if ((refPtr = Jim_GetReference(interp, objPtr)) == NULL)
        return JIM_ERR;
    Jim_IncrRefCount(cmdNamePtr);
    if (refPtr->finalizerCmdNamePtr)
        Jim_DecrRefCount(interp, refPtr->finalizerCmdNamePtr);
    refPtr->finalizerCmdNamePtr = cmdNamePtr;
    return JIM_OK;
}

int Jim_GetFinalizer(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj **cmdNamePtrPtr)
{
    Jim_Reference *refPtr;

    if ((refPtr = Jim_GetReference(interp, objPtr)) == NULL)
        return JIM_ERR;
    *cmdNamePtrPtr = refPtr->finalizerCmdNamePtr;
    return JIM_OK;
}



static const Jim_HashTableType JimRefMarkHashTableType = {
    JimReferencesHTHashFunction,        
    JimReferencesHTKeyDup,      
    NULL,                       
    JimReferencesHTKeyCompare,  
    JimReferencesHTKeyDestructor,       
    NULL                        
};


int Jim_Collect(Jim_Interp *interp)
{
    int collected = 0;
    return collected;
}

#define JIM_COLLECT_ID_PERIOD 5000
#define JIM_COLLECT_TIME_PERIOD 300

void Jim_CollectIfNeeded(Jim_Interp *interp)
{
    unsigned long elapsedId;
    int elapsedTime;

    elapsedId = interp->referenceNextId - interp->lastCollectId;
    elapsedTime = time(NULL) - interp->lastCollectTime;


    if (elapsedId > JIM_COLLECT_ID_PERIOD || elapsedTime > JIM_COLLECT_TIME_PERIOD) {
        Jim_Collect(interp);
    }
}
#endif

int Jim_IsBigEndian(void)
{
    union {
        unsigned short s;
        unsigned char c[2];
    } uval = {0x0102};
10628
10629
10630
10631
10632
10633
10634
10635

10636
10637
10638
10639
10640
10641
10642
10524
10525
10526
10527
10528
10529
10530

10531
10532
10533
10534
10535
10536
10537
10538







-
+







    Jim_IncrRefCount(i->unknown);
    Jim_IncrRefCount(i->currentScriptObj);
    Jim_IncrRefCount(i->nullScriptObj);
    Jim_IncrRefCount(i->errorProc);
    Jim_IncrRefCount(i->trueObj);
    Jim_IncrRefCount(i->falseObj);

    

    Jim_SetVariableStrWithStr(i, JIM_LIBPATH, TCL_LIBRARY);
    Jim_SetVariableStrWithStr(i, JIM_INTERACTIVE, "0");

    Jim_SetVariableStrWithStr(i, "tcl_platform(engine)", "Jim");
    Jim_SetVariableStrWithStr(i, "tcl_platform(os)", TCL_PLATFORM_OS);
    Jim_SetVariableStrWithStr(i, "tcl_platform(platform)", TCL_PLATFORM_PLATFORM);
    Jim_SetVariableStrWithStr(i, "tcl_platform(pathSeparator)", TCL_PLATFORM_PATH_SEPARATOR);
10650
10651
10652
10653
10654
10655
10656
10657

10658


10659
10660
10661
10662
10663
10664
10665
10546
10547
10548
10549
10550
10551
10552

10553
10554
10555
10556
10557
10558
10559
10560
10561
10562
10563







-
+

+
+








void Jim_FreeInterp(Jim_Interp *i)
{
    Jim_CallFrame *cf, *cfx;

    Jim_Obj *objPtr, *nextObjPtr;

    

    for (cf = i->framePtr; cf; cf = cfx) {

        JimInvokeDefer(i, JIM_OK);
        cfx = cf->parent;
        JimFreeCallFrame(i, cf, JIM_FCF_FULL);
    }

    Jim_DecrRefCount(i, i->emptyObj);
    Jim_DecrRefCount(i, i->trueObj);
    Jim_DecrRefCount(i, i->falseObj);
10682
10683
10684
10685
10686
10687
10688

10689
10690
10691
10692
10693
10694
10695
10580
10581
10582
10583
10584
10585
10586
10587
10588
10589
10590
10591
10592
10593
10594







+







    if (i->liveList != NULL) {
        objPtr = i->liveList;

        printf("\n-------------------------------------\n");
        printf("Objects still in the free list:\n");
        while (objPtr) {
            const char *type = objPtr->typePtr ? objPtr->typePtr->name : "string";
            Jim_String(objPtr);

            if (objPtr->bytes && strlen(objPtr->bytes) > 20) {
                printf("%p (%d) %-10s: '%.20s...'\n",
                    (void *)objPtr, objPtr->refCount, type, objPtr->bytes);
            }
            else {
                printf("%p (%d) %-10s: '%s'\n",
10703
10704
10705
10706
10707
10708
10709
10710

10711
10712
10713
10714
10715
10716
10717
10718

10719
10720
10721
10722
10723
10724
10725
10726

10727
10728
10729
10730
10731
10732
10733
10602
10603
10604
10605
10606
10607
10608

10609
10610
10611
10612
10613
10614
10615
10616

10617
10618
10619
10620
10621
10622
10623
10624

10625
10626
10627
10628
10629
10630
10631
10632







-
+







-
+







-
+







            objPtr = objPtr->nextObjPtr;
        }
        printf("-------------------------------------\n\n");
        JimPanic((1, "Live list non empty freeing the interpreter! Leak?"));
    }
#endif

    

    objPtr = i->freeList;
    while (objPtr) {
        nextObjPtr = objPtr->nextObjPtr;
        Jim_Free(objPtr);
        objPtr = nextObjPtr;
    }

    

    for (cf = i->freeFramesList; cf; cf = cfx) {
        cfx = cf->next;
        if (cf->vars.table)
            Jim_FreeHashTable(&cf->vars);
        Jim_Free(cf);
    }

    

    Jim_Free(i);
}

Jim_CallFrame *Jim_GetCallFrameByLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr)
{
    long level;
    const char *str;
10744
10745
10746
10747
10748
10749
10750
10751

10752
10753
10754
10755
10756
10757

10758
10759
10760
10761
10762
10763
10764
10765

10766
10767
10768
10769
10770
10771
10772
10773
10774
10775
10776
10777
10778
10779
10780
10781
10782
10783
10784

10785
10786
10787
10788
10789
10790
10791
10792

10793
10794
10795
10796
10797
10798
10799
10643
10644
10645
10646
10647
10648
10649

10650
10651
10652
10653
10654
10655

10656
10657
10658
10659
10660
10661
10662
10663

10664
10665
10666
10667
10668
10669
10670
10671
10672
10673
10674
10675
10676
10677
10678
10679
10680
10681
10682

10683
10684
10685
10686
10687
10688
10689
10690

10691
10692
10693
10694
10695
10696
10697
10698







-
+





-
+







-
+


















-
+







-
+







            }
        }
        else {
            if (Jim_GetLong(interp, levelObjPtr, &level) != JIM_OK || level < 0) {
                level = -1;
            }
            else {
                

                level = interp->framePtr->level - level;
            }
        }
    }
    else {
        str = "1";              
        str = "1";
        level = interp->framePtr->level - 1;
    }

    if (level == 0) {
        return interp->topFramePtr;
    }
    if (level > 0) {
        

        for (framePtr = interp->framePtr; framePtr; framePtr = framePtr->parent) {
            if (framePtr->level == level) {
                return framePtr;
            }
        }
    }

    Jim_SetResultFormatted(interp, "bad level \"%s\"", str);
    return NULL;
}

static Jim_CallFrame *JimGetCallFrameByInteger(Jim_Interp *interp, Jim_Obj *levelObjPtr)
{
    long level;
    Jim_CallFrame *framePtr;

    if (Jim_GetLong(interp, levelObjPtr, &level) == JIM_OK) {
        if (level <= 0) {
            

            level = interp->framePtr->level + level;
        }

        if (level == 0) {
            return interp->topFramePtr;
        }

        

        for (framePtr = interp->framePtr; framePtr; framePtr = framePtr->parent) {
            if (framePtr->level == level) {
                return framePtr;
            }
        }
    }

10808
10809
10810
10811
10812
10813
10814
10815

10816
10817
10818
10819
10820
10821
10822
10823
10824
10825
10826
10827
10828
10829
10830
10831
10832
10833
10834
10835
10836

10837
10838
10839
10840
10841
10842
10843
10844
10845
10846

10847
10848

10849
10850
10851
10852
10853
10854

10855
10856
10857

10858
10859
10860
10861
10862
10863
10864
10707
10708
10709
10710
10711
10712
10713

10714
10715
10716
10717
10718
10719
10720
10721
10722
10723
10724
10725
10726
10727
10728
10729
10730
10731
10732
10733
10734

10735
10736
10737
10738
10739
10740
10741
10742
10743
10744

10745
10746

10747
10748
10749
10750
10751
10752

10753
10754
10755

10756
10757
10758
10759
10760
10761
10762
10763







-
+




















-
+









-
+

-
+





-
+


-
+







    Jim_IncrRefCount(interp->stackTrace);
}

static void JimSetStackTrace(Jim_Interp *interp, Jim_Obj *stackTraceObj)
{
    int len;

    

    Jim_IncrRefCount(stackTraceObj);
    Jim_DecrRefCount(interp, interp->stackTrace);
    interp->stackTrace = stackTraceObj;
    interp->errorFlag = 1;

    len = Jim_ListLength(interp, interp->stackTrace);
    if (len >= 3) {
        if (Jim_Length(Jim_ListGetIndex(interp, interp->stackTrace, len - 2)) == 0) {
            interp->addStackTrace = 1;
        }
    }
}

static void JimAppendStackTrace(Jim_Interp *interp, const char *procname,
    Jim_Obj *fileNameObj, int linenr)
{
    if (strcmp(procname, "unknown") == 0) {
        procname = "";
    }
    if (!*procname && !Jim_Length(fileNameObj)) {
        

        return;
    }

    if (Jim_IsShared(interp->stackTrace)) {
        Jim_DecrRefCount(interp, interp->stackTrace);
        interp->stackTrace = Jim_DuplicateObj(interp, interp->stackTrace);
        Jim_IncrRefCount(interp->stackTrace);
    }

    

    if (!*procname && Jim_Length(fileNameObj)) {
        

        int len = Jim_ListLength(interp, interp->stackTrace);

        if (len >= 3) {
            Jim_Obj *objPtr = Jim_ListGetIndex(interp, interp->stackTrace, len - 3);
            if (Jim_Length(objPtr)) {
                

                objPtr = Jim_ListGetIndex(interp, interp->stackTrace, len - 2);
                if (Jim_Length(objPtr) == 0) {
                    

                    ListSetIndex(interp, interp->stackTrace, len - 2, fileNameObj, 0);
                    ListSetIndex(interp, interp->stackTrace, len - 1, Jim_NewIntObj(interp, linenr), 0);
                    return;
                }
            }
        }
    }
10956
10957
10958
10959
10960
10961
10962
10963

10964
10965
10966
10967
10968

10969
10970

10971
10972
10973
10974
10975
10976
10977
10978
10979
10980
10981

10982
10983
10984
10985
10986
10987
10988
10855
10856
10857
10858
10859
10860
10861

10862
10863
10864
10865
10866

10867
10868

10869
10870
10871
10872
10873
10874
10875
10876
10877
10878
10879

10880
10881
10882
10883
10884
10885
10886
10887







-
+




-
+

-
+










-
+








static int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
{
    jim_wide wideValue;
    const char *str;

    if (objPtr->typePtr == &coercedDoubleObjType) {
        

        objPtr->typePtr = &intObjType;
        return JIM_OK;
    }

    

    str = Jim_String(objPtr);
    

    if (Jim_StringToWide(str, &wideValue, 0) != JIM_OK) {
        if (flags & JIM_ERRMSG) {
            Jim_SetResultFormatted(interp, "expected integer but got \"%#s\"", objPtr);
        }
        return JIM_ERR;
    }
    if ((wideValue == JIM_WIDE_MIN || wideValue == JIM_WIDE_MAX) && errno == ERANGE) {
        Jim_SetResultString(interp, "Integer value too big to be represented", -1);
        return JIM_ERR;
    }
    

    Jim_FreeIntRep(interp, objPtr);
    objPtr->typePtr = &intObjType;
    objPtr->internalRep.wideValue = wideValue;
    return JIM_OK;
}

#ifdef JIM_OPTIMIZATION
11073
11074
11075
11076
11077
11078
11079
11080

11081
11082
11083
11084
11085
11086

11087
11088
11089
11090
11091
11092
11093
10972
10973
10974
10975
10976
10977
10978

10979
10980
10981
10982
10983
10984

10985
10986
10987
10988
10989
10990
10991
10992







-
+





-
+







        return;
    }
    {
        char buf[JIM_DOUBLE_SPACE + 1];
        int i;
        int len = sprintf(buf, "%.12g", value);

        

        for (i = 0; i < len; i++) {
            if (buf[i] == '.' || buf[i] == 'e') {
#if defined(JIM_SPRINTF_DOUBLE_NEEDS_FIX)
                char *e = strchr(buf, 'e');
                if (e && (e[1] == '-' || e[1] == '+') && e[2] == '0') {
                    

                    e += 2;
                    memmove(e, e + 1, len - (e - buf));
                }
#endif
                break;
            }
        }
11102
11103
11104
11105
11106
11107
11108
11109
11110
11111
11112

11113
11114
11115
11116
11117
11118
11119
11120

11121
11122
11123
11124
11125


11126
11127

11128
11129
11130
11131
11132
11133
11134

11135
11136
11137
11138
11139

11140
11141
11142
11143
11144
11145
11146
11001
11002
11003
11004
11005
11006
11007


11008

11009
11010
11011
11012
11013
11014
11015
11016

11017
11018
11019
11020

11021
11022
11023
11024

11025
11026
11027
11028
11029
11030
11031

11032
11033
11034
11035
11036

11037
11038
11039
11040
11041
11042
11043
11044







-
-

-
+







-
+



-

+
+

-
+






-
+




-
+








static int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
{
    double doubleValue;
    jim_wide wideValue;
    const char *str;

    str = Jim_String(objPtr);

#ifdef HAVE_LONG_LONG
    

#define MIN_INT_IN_DOUBLE -(1LL << 53)
#define MAX_INT_IN_DOUBLE -(MIN_INT_IN_DOUBLE + 1)

    if (objPtr->typePtr == &intObjType
        && JimWideValue(objPtr) >= MIN_INT_IN_DOUBLE
        && JimWideValue(objPtr) <= MAX_INT_IN_DOUBLE) {

        

        objPtr->typePtr = &coercedDoubleObjType;
        return JIM_OK;
    }
    else
#endif
    str = Jim_String(objPtr);

    if (Jim_StringToWide(str, &wideValue, 10) == JIM_OK) {
        

        Jim_FreeIntRep(interp, objPtr);
        objPtr->typePtr = &coercedDoubleObjType;
        objPtr->internalRep.wideValue = wideValue;
        return JIM_OK;
    }
    else {
        

        if (Jim_StringToDouble(str, &doubleValue) != JIM_OK) {
            Jim_SetResultFormatted(interp, "expected floating-point number but got \"%#s\"", objPtr);
            return JIM_ERR;
        }
        

        Jim_FreeIntRep(interp, objPtr);
    }
    objPtr->typePtr = &doubleObjType;
    objPtr->internalRep.doubleValue = doubleValue;
    return JIM_OK;
}

11168
11169
11170
11171
11172
11173
11174








































11175
11176
11177
11178
11179
11180
11181
11066
11067
11068
11069
11070
11071
11072
11073
11074
11075
11076
11077
11078
11079
11080
11081
11082
11083
11084
11085
11086
11087
11088
11089
11090
11091
11092
11093
11094
11095
11096
11097
11098
11099
11100
11101
11102
11103
11104
11105
11106
11107
11108
11109
11110
11111
11112
11113
11114
11115
11116
11117
11118
11119







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+








    objPtr = Jim_NewObj(interp);
    objPtr->typePtr = &doubleObjType;
    objPtr->bytes = NULL;
    objPtr->internalRep.doubleValue = doubleValue;
    return objPtr;
}

static int SetBooleanFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags);

int Jim_GetBoolean(Jim_Interp *interp, Jim_Obj *objPtr, int * booleanPtr)
{
    if (objPtr->typePtr != &intObjType && SetBooleanFromAny(interp, objPtr, JIM_ERRMSG) == JIM_ERR)
        return JIM_ERR;
    *booleanPtr = (int) JimWideValue(objPtr);
    return JIM_OK;
}

static int SetBooleanFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
{
    static const char * const falses[] = {
        "0", "false", "no", "off", NULL
    };
    static const char * const trues[] = {
        "1", "true", "yes", "on", NULL
    };

    int boolean;

    int index;
    if (Jim_GetEnum(interp, objPtr, falses, &index, NULL, 0) == JIM_OK) {
        boolean = 0;
    } else if (Jim_GetEnum(interp, objPtr, trues, &index, NULL, 0) == JIM_OK) {
        boolean = 1;
    } else {
        if (flags & JIM_ERRMSG) {
            Jim_SetResultFormatted(interp, "expected boolean but got \"%#s\"", objPtr);
        }
        return JIM_ERR;
    }


    Jim_FreeIntRep(interp, objPtr);
    objPtr->typePtr = &intObjType;
    objPtr->internalRep.wideValue = boolean;
    return JIM_OK;
}

static void ListInsertElements(Jim_Obj *listPtr, int idx, int elemc, Jim_Obj *const *elemVec);
static void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr);
static void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
static void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
static void UpdateStringOfList(struct Jim_Obj *objPtr);
static int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
11219
11220
11221
11222
11223
11224
11225
11226

11227
11228
11229
11230
11231
11232
11233
11157
11158
11159
11160
11161
11162
11163

11164
11165
11166
11167
11168
11169
11170
11171







-
+







#define JIM_ELESTR_SIMPLE 0
#define JIM_ELESTR_BRACE 1
#define JIM_ELESTR_QUOTE 2
static unsigned char ListElementQuotingType(const char *s, int len)
{
    int i, level, blevel, trySimple = 1;

    

    if (len == 0)
        return JIM_ELESTR_BRACE;
    if (s[0] == '"' || s[0] == '{') {
        trySimple = 0;
        goto testbrace;
    }
    for (i = 0; i < len; i++) {
11241
11242
11243
11244
11245
11246
11247
11248

11249
11250
11251
11252
11253
11254
11255
11256
11257

11258
11259
11260
11261
11262
11263
11264
11179
11180
11181
11182
11183
11184
11185

11186
11187
11188
11189
11190
11191
11192
11193
11194

11195
11196
11197
11198
11199
11200
11201
11202







-
+








-
+







            case '\\':
            case '\r':
            case '\n':
            case '\t':
            case '\f':
            case '\v':
                trySimple = 0;
                

            case '{':
            case '}':
                goto testbrace;
        }
    }
    return JIM_ELESTR_SIMPLE;

  testbrace:
    

    if (s[len - 1] == '\\')
        return JIM_ELESTR_QUOTE;
    level = 0;
    blevel = 0;
    for (i = 0; i < len; i++) {
        switch (s[i]) {
            case '{':
11370
11371
11372
11373
11374
11375
11376
11377

11378
11379
11380
11381
11382
11383
11384
11385
11386
11387
11388
11389
11390
11391
11392
11393
11394
11395
11396

11397
11398

11399
11400
11401
11402
11403
11404
11405
11406

11407
11408
11409
11410

11411
11412
11413
11414
11415
11416
11417
11308
11309
11310
11311
11312
11313
11314

11315
11316
11317
11318
11319
11320
11321
11322
11323
11324
11325
11326
11327
11328
11329
11330
11331
11332
11333

11334
11335

11336
11337
11338
11339
11340
11341
11342
11343

11344
11345
11346
11347

11348
11349
11350
11351
11352
11353
11354
11355







-
+


















-
+

-
+







-
+



-
+







{
    #define STATIC_QUOTING_LEN 32
    int i, bufLen, realLength;
    const char *strRep;
    char *p;
    unsigned char *quotingType, staticQuoting[STATIC_QUOTING_LEN];

    

    if (objc > STATIC_QUOTING_LEN) {
        quotingType = Jim_Alloc(objc);
    }
    else {
        quotingType = staticQuoting;
    }
    bufLen = 0;
    for (i = 0; i < objc; i++) {
        int len;

        strRep = Jim_GetString(objv[i], &len);
        quotingType[i] = ListElementQuotingType(strRep, len);
        switch (quotingType[i]) {
            case JIM_ELESTR_SIMPLE:
                if (i != 0 || strRep[0] != '#') {
                    bufLen += len;
                    break;
                }
                

                quotingType[i] = JIM_ELESTR_BRACE;
                

            case JIM_ELESTR_BRACE:
                bufLen += len + 2;
                break;
            case JIM_ELESTR_QUOTE:
                bufLen += len * 2;
                break;
        }
        bufLen++;               
        bufLen++;
    }
    bufLen++;

    

    p = objPtr->bytes = Jim_Alloc(bufLen + 1);
    realLength = 0;
    for (i = 0; i < objc; i++) {
        int len, qlen;

        strRep = Jim_GetString(objv[i], &len);

11434
11435
11436
11437
11438
11439
11440
11441

11442
11443
11444
11445
11446
11447

11448
11449
11450
11451
11452
11453
11454
11372
11373
11374
11375
11376
11377
11378

11379
11380
11381
11382
11383
11384

11385
11386
11387
11388
11389
11390
11391
11392







-
+





-
+







                    realLength++;
                }
                qlen = BackslashQuoteString(strRep, len, p);
                p += qlen;
                realLength += qlen;
                break;
        }
        

        if (i + 1 != objc) {
            *p++ = ' ';
            realLength++;
        }
    }
    *p = '\0';                  
    *p = '\0';
    objPtr->length = realLength;

    if (quotingType != staticQuoting) {
        Jim_Free(quotingType);
    }
}

11475
11476
11477
11478
11479
11480
11481
11482

11483
11484
11485
11486
11487
11488
11489
11490
11491
11492

11493
11494
11495
11496
11497
11498
11499
11500
11501
11502
11503

11504
11505
11506
11507
11508
11509
11510
11511
11512

11513
11514
11515
11516
11517
11518
11519
11413
11414
11415
11416
11417
11418
11419

11420
11421
11422
11423
11424
11425
11426
11427
11428
11429

11430
11431
11432
11433
11434
11435
11436
11437
11438
11439
11440

11441
11442
11443
11444
11445
11446
11447
11448
11449

11450
11451
11452
11453
11454
11455
11456
11457







-
+









-
+










-
+








-
+







        int i;

        listObjPtrPtr = JimDictPairs(objPtr, &len);
        for (i = 0; i < len; i++) {
            Jim_IncrRefCount(listObjPtrPtr[i]);
        }

        

        Jim_FreeIntRep(interp, objPtr);
        objPtr->typePtr = &listObjType;
        objPtr->internalRep.listValue.len = len;
        objPtr->internalRep.listValue.maxLen = len;
        objPtr->internalRep.listValue.ele = listObjPtrPtr;

        return JIM_OK;
    }

    

    if (objPtr->typePtr == &sourceObjType) {
        fileNameObj = objPtr->internalRep.sourceValue.fileNameObj;
        linenr = objPtr->internalRep.sourceValue.lineNumber;
    }
    else {
        fileNameObj = interp->emptyObj;
        linenr = 1;
    }
    Jim_IncrRefCount(fileNameObj);

    

    str = Jim_GetString(objPtr, &strLen);

    Jim_FreeIntRep(interp, objPtr);
    objPtr->typePtr = &listObjType;
    objPtr->internalRep.listValue.len = 0;
    objPtr->internalRep.listValue.maxLen = 0;
    objPtr->internalRep.listValue.ele = NULL;

    

    if (strLen) {
        JimParserInit(&parser, str, strLen, linenr);
        while (!parser.eof) {
            Jim_Obj *elementPtr;

            JimParseList(&parser);
            if (parser.tt != JIM_TT_STR && parser.tt != JIM_TT_ESC)
11639
11640
11641
11642
11643
11644
11645
11646

11647
11648
11649
11650
11651
11652
11653
11577
11578
11579
11580
11581
11582
11583

11584
11585
11586
11587
11588
11589
11590
11591







-
+







static int ListSortCommand(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
{
    Jim_Obj *compare_script;
    int rc;

    jim_wide ret = 0;

    

    compare_script = Jim_DuplicateObj(sort_info->interp, sort_info->command);
    Jim_ListAppendElement(sort_info->interp, compare_script, *lhsObj);
    Jim_ListAppendElement(sort_info->interp, compare_script, *rhsObj);

    rc = Jim_EvalObj(sort_info->interp, compare_script);

    if (rc != JIM_OK || Jim_GetWide(sort_info->interp, Jim_GetResult(sort_info->interp), &ret) != JIM_OK) {
11661
11662
11663
11664
11665
11666
11667
11668

11669
11670
11671
11672

11673
11674
11675
11676
11677
11678
11679
11680








11681
11682
11683
11684
11685
11686
11687
11688
11689
11690
11691
11692
11693
11694
11695
11696
11697
11698

11699
11700
11701
11702
11703
11704
11705
11599
11600
11601
11602
11603
11604
11605

11606
11607
11608
11609

11610
11611
11612
11613
11614




11615
11616
11617
11618
11619
11620
11621
11622
11623
11624
11625
11626
11627
11628
11629
11630
11631
11632
11633
11634
11635
11636
11637
11638
11639

11640
11641
11642
11643
11644
11645
11646
11647







-
+



-
+




-
-
-
-
+
+
+
+
+
+
+
+

















-
+







{
    int src;
    int dst = 0;
    Jim_Obj **ele = listObjPtr->internalRep.listValue.ele;

    for (src = 1; src < listObjPtr->internalRep.listValue.len; src++) {
        if (comp(&ele[dst], &ele[src]) == 0) {
            

            Jim_DecrRefCount(sort_info->interp, ele[dst]);
        }
        else {
            

            dst++;
        }
        ele[dst] = ele[src];
    }
    
    ele[++dst] = ele[src];

    


    dst++;
    if (dst < listObjPtr->internalRep.listValue.len) {
        ele[dst] = ele[src];
    }


    listObjPtr->internalRep.listValue.len = dst;
}


static int ListSortElements(Jim_Interp *interp, Jim_Obj *listObjPtr, struct lsort_info *info)
{
    struct lsort_info *prev_info;

    typedef int (qsort_comparator) (const void *, const void *);
    int (*fn) (Jim_Obj **, Jim_Obj **);
    Jim_Obj **vector;
    int len;
    int rc;

    JimPanic((Jim_IsShared(listObjPtr), "ListSortElements called with shared object"));
    SetListFromAny(interp, listObjPtr);

    

    prev_info = sort_info;
    sort_info = info;

    vector = listObjPtr->internalRep.listValue.ele;
    len = listObjPtr->internalRep.listValue.len;
    switch (info->type) {
        case JIM_LSORT_ASCII:
11714
11715
11716
11717
11718
11719
11720
11721

11722
11723

11724
11725
11726
11727

11728
11729
11730
11731
11732
11733
11734
11656
11657
11658
11659
11660
11661
11662

11663
11664

11665
11666
11667
11668

11669
11670
11671
11672
11673
11674
11675
11676







-
+

-
+



-
+







        case JIM_LSORT_REAL:
            fn = ListSortReal;
            break;
        case JIM_LSORT_COMMAND:
            fn = ListSortCommand;
            break;
        default:
            fn = NULL;          
            fn = NULL;
            JimPanic((1, "ListSort called with invalid sort type"));
            return -1; 
            return -1;
    }

    if (info->indexed) {
        

        info->subfn = fn;
        fn = ListSortIndexHelper;
    }

    if ((rc = setjmp(info->jmpbuf)) == 0) {
        qsort(vector, len, sizeof(Jim_Obj *), (qsort_comparator *) fn);

11748
11749
11750
11751
11752
11753
11754
11755

11756
11757
11758
11759
11760
11761
11762
11690
11691
11692
11693
11694
11695
11696

11697
11698
11699
11700
11701
11702
11703
11704







-
+







    int currentLen = listPtr->internalRep.listValue.len;
    int requiredLen = currentLen + elemc;
    int i;
    Jim_Obj **point;

    if (requiredLen > listPtr->internalRep.listValue.maxLen) {
        if (requiredLen < 2) {
            

            requiredLen = 4;
        }
        else {
            requiredLen *= 2;
        }

        listPtr->internalRep.listValue.ele = Jim_Realloc(listPtr->internalRep.listValue.ele,
11934
11935
11936
11937
11938
11939
11940
11941

11942
11943
11944
11945

11946
11947
11948
11949
11950
11951

11952
11953
11954
11955
11956

11957
11958
11959
11960
11961
11962

11963
11964

11965
11966
11967
11968
11969
11970
11971
11876
11877
11878
11879
11880
11881
11882

11883
11884
11885
11886

11887
11888
11889
11890
11891
11892

11893
11894
11895
11896
11897

11898
11899
11900
11901
11902
11903

11904
11905

11906
11907
11908
11909
11910
11911
11912
11913







-
+



-
+





-
+




-
+





-
+

-
+







        Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);

        for (i = 0; i < objc; i++)
            ListAppendList(objPtr, objv[i]);
        return objPtr;
    }
    else {
        

        int len = 0, objLen;
        char *bytes, *p;

        

        for (i = 0; i < objc; i++) {
            len += Jim_Length(objv[i]);
        }
        if (objc)
            len += objc - 1;
        

        p = bytes = Jim_Alloc(len + 1);
        for (i = 0; i < objc; i++) {
            const char *s = Jim_GetString(objv[i], &objLen);

            

            while (objLen && isspace(UCHAR(*s))) {
                s++;
                objLen--;
                len--;
            }
            

            while (objLen && isspace(UCHAR(s[objLen - 1]))) {
                

                if (objLen > 1 && s[objLen - 2] == '\\') {
                    break;
                }
                objLen--;
                len--;
            }
            memcpy(p, s, objLen);
11988
11989
11990
11991
11992
11993
11994
11995

11996
11997
11998
11999
12000
12001
12002
11930
11931
11932
11933
11934
11935
11936

11937
11938
11939
11940
11941
11942
11943
11944







-
+







{
    int first, last;
    int len, rangeLen;

    if (Jim_GetIndex(interp, firstObjPtr, &first) != JIM_OK ||
        Jim_GetIndex(interp, lastObjPtr, &last) != JIM_OK)
        return NULL;
    len = Jim_ListLength(interp, listObjPtr);   
    len = Jim_ListLength(interp, listObjPtr);
    first = JimRelToAbsIndex(len, first);
    last = JimRelToAbsIndex(len, last);
    JimRelToAbsRange(len, &first, &last, &rangeLen);
    if (first == 0 && last == len) {
        return listObjPtr;
    }
    return Jim_NewListObj(interp, listObjPtr->internalRep.listValue.ele + first, rangeLen);
12028
12029
12030
12031
12032
12033
12034
12035
12036
12037
12038
12039
12040






12041
12042
12043
12044
12045
12046
12047
11970
11971
11972
11973
11974
11975
11976






11977
11978
11979
11980
11981
11982
11983
11984
11985
11986
11987
11988
11989







-
-
-
-
-
-
+
+
+
+
+
+








static void JimObjectHTKeyValDestructor(void *interp, void *val)
{
    Jim_DecrRefCount(interp, (Jim_Obj *)val);
}

static const Jim_HashTableType JimDictHashTableType = {
    JimObjectHTHashFunction,    
    JimObjectHTKeyValDup,       
    JimObjectHTKeyValDup,       
    JimObjectHTKeyCompare,      
    JimObjectHTKeyValDestructor,    
    JimObjectHTKeyValDestructor 
    JimObjectHTHashFunction,
    JimObjectHTKeyValDup,
    JimObjectHTKeyValDup,
    JimObjectHTKeyCompare,
    JimObjectHTKeyValDestructor,
    JimObjectHTKeyValDestructor
};

static const Jim_ObjType dictObjType = {
    "dict",
    FreeDictInternalRep,
    DupDictInternalRep,
    UpdateStringOfDict,
12058
12059
12060
12061
12062
12063
12064
12065

12066
12067
12068
12069
12070
12071

12072
12073
12074
12075
12076
12077
12078
12079
12080
12081
12082
12083
12084
12085
12086
12087
12088
12089
12090
12091

12092
12093
12094
12095
12096
12097
12098
12099
12100
12101
12102
12103
12104
12105

12106
12107
12108
12109

12110
12111
12112
12113
12114
12115
12116
12117
12118
12119
12120
12121
12122
12123
12124
12125
12126
12127

12128
12129
12130
12131
12132
12133
12134

12135
12136
12137
12138
12139
12140
12141
12000
12001
12002
12003
12004
12005
12006

12007
12008
12009
12010
12011
12012

12013
12014
12015
12016
12017
12018
12019
12020
12021
12022
12023
12024
12025
12026
12027
12028
12029
12030
12031
12032

12033
12034
12035
12036
12037
12038
12039
12040
12041
12042
12043
12044
12045
12046

12047
12048
12049
12050

12051
12052
12053
12054
12055
12056
12057
12058
12059
12060
12061
12062
12063
12064
12065
12066
12067
12068

12069
12070
12071
12072
12073
12074
12075

12076
12077
12078
12079
12080
12081
12082
12083







-
+





-
+



















-
+













-
+



-
+

















-
+






-
+








void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
{
    Jim_HashTable *ht, *dupHt;
    Jim_HashTableIterator htiter;
    Jim_HashEntry *he;

    

    ht = srcPtr->internalRep.ptr;
    dupHt = Jim_Alloc(sizeof(*dupHt));
    Jim_InitHashTable(dupHt, &JimDictHashTableType, interp);
    if (ht->size != 0)
        Jim_ExpandHashTable(dupHt, ht->size);
    

    JimInitHashTableIterator(ht, &htiter);
    while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
        Jim_AddHashEntry(dupHt, he->key, he->u.val);
    }

    dupPtr->internalRep.ptr = dupHt;
    dupPtr->typePtr = &dictObjType;
}

static Jim_Obj **JimDictPairs(Jim_Obj *dictPtr, int *len)
{
    Jim_HashTable *ht;
    Jim_HashTableIterator htiter;
    Jim_HashEntry *he;
    Jim_Obj **objv;
    int i;

    ht = dictPtr->internalRep.ptr;

    

    objv = Jim_Alloc((ht->used * 2) * sizeof(Jim_Obj *));
    JimInitHashTableIterator(ht, &htiter);
    i = 0;
    while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
        objv[i++] = Jim_GetHashEntryKey(he);
        objv[i++] = Jim_GetHashEntryVal(he);
    }
    *len = i;
    return objv;
}

static void UpdateStringOfDict(struct Jim_Obj *objPtr)
{
    

    int len;
    Jim_Obj **objv = JimDictPairs(objPtr, &len);

    

    JimMakeListStringRep(objPtr, objv, len);

    Jim_Free(objv);
}

static int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
{
    int listlen;

    if (objPtr->typePtr == &dictObjType) {
        return JIM_OK;
    }

    if (Jim_IsList(objPtr) && Jim_IsShared(objPtr)) {
        Jim_String(objPtr);
    }

    

    listlen = Jim_ListLength(interp, objPtr);
    if (listlen % 2) {
        Jim_SetResultString(interp, "missing value to go with key", -1);
        return JIM_ERR;
    }
    else {
        

        Jim_HashTable *ht;
        int i;

        ht = Jim_Alloc(sizeof(*ht));
        Jim_InitHashTable(ht, &JimDictHashTableType, interp);

        for (i = 0; i < listlen; i += 2) {
12156
12157
12158
12159
12160
12161
12162
12163

12164
12165
12166
12167
12168
12169
12170
12098
12099
12100
12101
12102
12103
12104

12105
12106
12107
12108
12109
12110
12111
12112







-
+









static int DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
    Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
{
    Jim_HashTable *ht = objPtr->internalRep.ptr;

    if (valueObjPtr == NULL) {  
    if (valueObjPtr == NULL) {
        return Jim_DeleteHashEntry(ht, keyObjPtr);
    }
    Jim_ReplaceHashEntry(ht, keyObjPtr, valueObjPtr);
    return JIM_OK;
}

int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
12207
12208
12209
12210
12211
12212
12213

12214
12215



12216
12217
12218
12219
12220
12221
12222
12149
12150
12151
12152
12153
12154
12155
12156


12157
12158
12159
12160
12161
12162
12163
12164
12165
12166







+
-
-
+
+
+







    ht = dictPtr->internalRep.ptr;
    if ((he = Jim_FindHashEntry(ht, keyPtr)) == NULL) {
        if (flags & JIM_ERRMSG) {
            Jim_SetResultFormatted(interp, "key \"%#s\" not known in dictionary", keyPtr);
        }
        return JIM_ERR;
    }
    else {
    *objPtrPtr = he->u.val;
    return JIM_OK;
        *objPtrPtr = Jim_GetHashEntryVal(he);
        return JIM_OK;
    }
}


int Jim_DictPairs(Jim_Interp *interp, Jim_Obj *dictPtr, Jim_Obj ***objPtrPtr, int *len)
{
    if (SetDictFromAny(interp, dictPtr) != JIM_OK) {
        return JIM_ERR;
12256
12257
12258
12259
12260
12261
12262
12263

12264
12265
12266
12267
12268
12269
12270
12271
12272
12273
12274
12275
12276
12277

12278
12279
12280
12281
12282
12283

12284
12285
12286
12287
12288
12289
12290
12291
12292

12293
12294
12295
12296
12297
12298
12299
12300
12301
12302
12303
12304
12305
12306
12307
12308
12309

12310
12311
12312
12313
12314
12315
12316
12200
12201
12202
12203
12204
12205
12206

12207
12208
12209
12210
12211
12212
12213
12214
12215
12216
12217
12218
12219
12220

12221
12222
12223
12224
12225
12226

12227
12228
12229
12230
12231
12232
12233
12234
12235

12236
12237
12238
12239
12240
12241
12242
12243
12244
12245
12246
12247
12248
12249
12250
12251
12252

12253
12254
12255
12256
12257
12258
12259
12260







-
+













-
+





-
+








-
+
















-
+







{
    Jim_Obj *varObjPtr, *objPtr, *dictObjPtr;
    int shared, i;

    varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, flags);
    if (objPtr == NULL) {
        if (newObjPtr == NULL && (flags & JIM_MUSTEXIST)) {
            

            return JIM_ERR;
        }
        varObjPtr = objPtr = Jim_NewDictObj(interp, NULL, 0);
        if (Jim_SetVariable(interp, varNamePtr, objPtr) != JIM_OK) {
            Jim_FreeNewObj(interp, varObjPtr);
            return JIM_ERR;
        }
    }
    if ((shared = Jim_IsShared(objPtr)))
        varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
    for (i = 0; i < keyc; i++) {
        dictObjPtr = objPtr;

        

        if (SetDictFromAny(interp, dictObjPtr) != JIM_OK) {
            goto err;
        }

        if (i == keyc - 1) {
            

            if (Jim_DictAddElement(interp, objPtr, keyv[keyc - 1], newObjPtr) != JIM_OK) {
                if (newObjPtr || (flags & JIM_MUSTEXIST)) {
                    goto err;
                }
            }
            break;
        }

        

        Jim_InvalidateStringRep(dictObjPtr);
        if (Jim_DictKey(interp, dictObjPtr, keyv[i], &objPtr,
                newObjPtr ? JIM_NONE : JIM_ERRMSG) == JIM_OK) {
            if (Jim_IsShared(objPtr)) {
                objPtr = Jim_DuplicateObj(interp, objPtr);
                DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
            }
        }
        else {
            if (newObjPtr == NULL) {
                goto err;
            }
            objPtr = Jim_NewDictObj(interp, NULL, 0);
            DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
        }
    }
    

    Jim_InvalidateStringRep(objPtr);
    Jim_InvalidateStringRep(varObjPtr);
    if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK) {
        goto err;
    }
    Jim_SetResult(interp, varObjPtr);
    return JIM_OK;
12339
12340
12341
12342
12343
12344
12345
12346

12347
12348
12349
12350
12351
12352
12353
12354
12355
12356
12357
12358
12359

12360
12361
12362

12363
12364
12365
12366
12367
12368
12369
12370
12371
12372
12373
12374
12375
12376
12377

12378
12379
12380
12381
12382
12383
12384
12385
12386
12387

12388
12389
12390
12391
12392
12393
12394
12395
12396
12397
12398
12399

12400
12401
12402
12403
12404
12405
12406
12407

12408
12409
12410
12411
12412
12413
12414
12415
12416
12417
12418
12419
12420
12421

12422
12423
12424
12425
12426
12427
12428
12283
12284
12285
12286
12287
12288
12289

12290
12291
12292
12293
12294
12295
12296
12297
12298
12299
12300
12301
12302

12303
12304
12305

12306
12307
12308
12309
12310
12311
12312
12313
12314
12315
12316
12317
12318
12319
12320

12321
12322
12323
12324
12325
12326
12327
12328
12329
12330

12331
12332
12333
12334
12335
12336
12337
12338
12339
12340
12341
12342

12343
12344
12345
12346
12347
12348
12349
12350

12351
12352
12353
12354
12355
12356
12357
12358
12359
12360
12361
12362
12363
12364

12365
12366
12367
12368
12369
12370
12371
12372







-
+












-
+


-
+














-
+









-
+











-
+







-
+













-
+







    }
    else {
        char buf[JIM_INTEGER_SPACE + 1];
        if (objPtr->internalRep.intValue >= 0) {
            sprintf(buf, "%d", objPtr->internalRep.intValue);
        }
        else {
            

            sprintf(buf, "end%d", objPtr->internalRep.intValue + 1);
        }
        JimSetStringBytes(objPtr, buf);
    }
}

static int SetIndexFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
{
    int idx, end = 0;
    const char *str;
    char *endptr;

    

    str = Jim_String(objPtr);

    

    if (strncmp(str, "end", 3) == 0) {
        end = 1;
        str += 3;
        idx = 0;
    }
    else {
        idx = jim_strtol(str, &endptr);

        if (endptr == str) {
            goto badindex;
        }
        str = endptr;
    }

    

    if (*str == '+' || *str == '-') {
        int sign = (*str == '+' ? 1 : -1);

        idx += sign * jim_strtol(++str, &endptr);
        if (str == endptr || *endptr) {
            goto badindex;
        }
        str = endptr;
    }
    

    while (isspace(UCHAR(*str))) {
        str++;
    }
    if (*str) {
        goto badindex;
    }
    if (end) {
        if (idx > 0) {
            idx = INT_MAX;
        }
        else {
            

            idx--;
        }
    }
    else if (idx < 0) {
        idx = -INT_MAX;
    }

    

    Jim_FreeIntRep(interp, objPtr);
    objPtr->typePtr = &indexObjType;
    objPtr->internalRep.intValue = idx;
    return JIM_OK;

  badindex:
    Jim_SetResultFormatted(interp,
        "bad index \"%#s\": must be integer?[+-]integer? or end?[+-]integer?", objPtr);
    return JIM_ERR;
}

int Jim_GetIndex(Jim_Interp *interp, Jim_Obj *objPtr, int *indexPtr)
{
    

    if (objPtr->typePtr == &intObjType) {
        jim_wide val = JimWideValue(objPtr);

        if (val < 0)
            *indexPtr = -INT_MAX;
        else if (val > INT_MAX)
            *indexPtr = INT_MAX;
12446
12447
12448
12449
12450
12451
12452
12453

12454
12455
12456
12457
12458
12459
12460
12390
12391
12392
12393
12394
12395
12396

12397
12398
12399
12400
12401
12402
12403
12404







-
+







    "continue",
    "signal",
    "exit",
    "eval",
    NULL
};

#define jimReturnCodesSize (sizeof(jimReturnCodes)/sizeof(*jimReturnCodes))
#define jimReturnCodesSize (sizeof(jimReturnCodes)/sizeof(*jimReturnCodes) - 1)

static const Jim_ObjType returnCodeObjType = {
    "return-code",
    NULL,
    NULL,
    NULL,
    JIM_TYPE_NONE,
12471
12472
12473
12474
12475
12476
12477
12478

12479
12480
12481
12482
12483
12484
12485

12486
12487
12488
12489
12490
12491
12492
12493
12494
12495
12496
12497
12498
12499
12500
12501
12502
12503

12504
12505
12506
12507
12508
12509
12510
12511




12512
12513
12514
12515
12516
12517
12518
12519
12520
12521
12522
12523
12524
12525
12526

12527
12528
12529
12530
12531

12532
12533
12534
12535
12536

12537
12538
12539
12540
12541
12542

12543
12544
12545
12546
12547

12548
12549
12550
12551

12552
12553
12554

12555
12556
12557
12558
12559
12560

12561
12562
12563
12564
12565
12566


12567
12568
12569
12570
12571
12572
12573
12574
12575
12576


12577
12578
12579
12580
12581

12582
12583
12584
12585
12586
12587
12588
12589
12590
12591


12592
12593
12594
12595
12596





12597
12598
12599


12600
12601
12602
12603
12604
12605
12606

12607
12608
12609

12610
12611
12612
12613

12614
12615
12616
12617
12618
12619
12620



12621
12622
12623
12624

12625
12626
12627

12628
12629
12630

12631




12632
12633

12634
12635
12636
12637
12638
12639
12640
12415
12416
12417
12418
12419
12420
12421

12422
12423
12424
12425
12426
12427
12428

12429
12430
12431
12432
12433
12434
12435
12436
12437
12438
12439
12440
12441
12442
12443
12444
12445
12446

12447

12448
12449
12450
12451



12452
12453
12454
12455
12456
12457
12458
12459
12460
12461
12462
12463
12464
12465
12466
12467
12468
12469

12470
12471
12472



12473





12474






12475





12476




12477
12478
12479

12480
12481
12482
12483
12484
12485

12486
12487
12488
12489
12490


12491
12492

12493
12494
12495
12496
12497
12498
12499


12500
12501
12502
12503
12504
12505
12506
12507
12508
12509
12510
12511
12512
12513
12514
12515
12516
12517
12518
12519
12520
12521



12522
12523
12524
12525
12526



12527
12528
12529
12530
12531
12532
12533
12534

12535
12536
12537

12538
12539
12540
12541

12542







12543
12544
12545




12546
12547
12548

12549

12550
12551
12552
12553
12554
12555
12556
12557
12558

12559
12560
12561
12562
12563
12564
12565
12566







-
+






-
+

















-
+
-




-
-
-
+
+
+
+














-
+


-
-
-
+
-
-
-
-
-
+
-
-
-
-
-
-
+
-
-
-
-
-
+
-
-
-
-
+


-
+





-
+




-
-
+
+
-







-
-
+
+





+










+
+


-
-
-
+
+
+
+
+
-
-
-
+
+






-
+


-
+



-
+
-
-
-
-
-
-
-
+
+
+
-
-
-
-
+


-
+
-


+

+
+
+
+

-
+







}

static int SetReturnCodeFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
{
    int returnCode;
    jim_wide wideValue;

    

    if (JimGetWideNoErr(interp, objPtr, &wideValue) != JIM_ERR)
        returnCode = (int)wideValue;
    else if (Jim_GetEnum(interp, objPtr, jimReturnCodes, &returnCode, NULL, JIM_NONE) != JIM_OK) {
        Jim_SetResultFormatted(interp, "expected return code but got \"%#s\"", objPtr);
        return JIM_ERR;
    }
    

    Jim_FreeIntRep(interp, objPtr);
    objPtr->typePtr = &returnCodeObjType;
    objPtr->internalRep.intValue = returnCode;
    return JIM_OK;
}

int Jim_GetReturnCode(Jim_Interp *interp, Jim_Obj *objPtr, int *intPtr)
{
    if (objPtr->typePtr != &returnCodeObjType && SetReturnCodeFromAny(interp, objPtr) == JIM_ERR)
        return JIM_ERR;
    *intPtr = objPtr->internalRep.intValue;
    return JIM_OK;
}

static int JimParseExprOperator(struct JimParserCtx *pc);
static int JimParseExprNumber(struct JimParserCtx *pc);
static int JimParseExprIrrational(struct JimParserCtx *pc);

static int JimParseExprBoolean(struct JimParserCtx *pc);



enum
{
    
    
    JIM_EXPROP_MUL = JIM_TT_EXPR_OP,             



    JIM_EXPROP_MUL = JIM_TT_EXPR_OP,
    JIM_EXPROP_DIV,
    JIM_EXPROP_MOD,
    JIM_EXPROP_SUB,
    JIM_EXPROP_ADD,
    JIM_EXPROP_LSHIFT,
    JIM_EXPROP_RSHIFT,
    JIM_EXPROP_ROTL,
    JIM_EXPROP_ROTR,
    JIM_EXPROP_LT,
    JIM_EXPROP_GT,
    JIM_EXPROP_LTE,
    JIM_EXPROP_GTE,
    JIM_EXPROP_NUMEQ,
    JIM_EXPROP_NUMNE,
    JIM_EXPROP_BITAND,          
    JIM_EXPROP_BITAND,
    JIM_EXPROP_BITXOR,
    JIM_EXPROP_BITOR,

    
    JIM_EXPROP_LOGICAND,        
    JIM_EXPROP_LOGICAND,
    JIM_EXPROP_LOGICAND_LEFT,
    JIM_EXPROP_LOGICAND_RIGHT,

    
    JIM_EXPROP_LOGICOR,         
    JIM_EXPROP_LOGICOR,
    JIM_EXPROP_LOGICOR_LEFT,
    JIM_EXPROP_LOGICOR_RIGHT,

    
    
    JIM_EXPROP_TERNARY,         
    JIM_EXPROP_TERNARY,
    JIM_EXPROP_TERNARY_LEFT,
    JIM_EXPROP_TERNARY_RIGHT,

    
    JIM_EXPROP_COLON,           
    JIM_EXPROP_COLON,
    JIM_EXPROP_COLON_LEFT,
    JIM_EXPROP_COLON_RIGHT,

    JIM_EXPROP_POW,             
    JIM_EXPROP_POW,


    JIM_EXPROP_STREQ,           
    JIM_EXPROP_STREQ,
    JIM_EXPROP_STRNE,
    JIM_EXPROP_STRIN,
    JIM_EXPROP_STRNI,


    JIM_EXPROP_NOT,             
    JIM_EXPROP_NOT,
    JIM_EXPROP_BITNOT,
    JIM_EXPROP_UNARYMINUS,
    JIM_EXPROP_UNARYPLUS,

    
    JIM_EXPROP_FUNC_FIRST,      

    JIM_EXPROP_FUNC_INT,
    JIM_EXPROP_FUNC_INT = JIM_EXPROP_FUNC_FIRST,
    JIM_EXPROP_FUNC_WIDE,
    JIM_EXPROP_FUNC_ABS,
    JIM_EXPROP_FUNC_DOUBLE,
    JIM_EXPROP_FUNC_ROUND,
    JIM_EXPROP_FUNC_RAND,
    JIM_EXPROP_FUNC_SRAND,

    
    JIM_EXPROP_FUNC_SIN,        

    JIM_EXPROP_FUNC_SIN,
    JIM_EXPROP_FUNC_COS,
    JIM_EXPROP_FUNC_TAN,
    JIM_EXPROP_FUNC_ASIN,
    JIM_EXPROP_FUNC_ACOS,
    JIM_EXPROP_FUNC_ATAN,
    JIM_EXPROP_FUNC_ATAN2,
    JIM_EXPROP_FUNC_SINH,
    JIM_EXPROP_FUNC_COSH,
    JIM_EXPROP_FUNC_TANH,
    JIM_EXPROP_FUNC_CEIL,
    JIM_EXPROP_FUNC_FLOOR,
    JIM_EXPROP_FUNC_EXP,
    JIM_EXPROP_FUNC_LOG,
    JIM_EXPROP_FUNC_LOG10,
    JIM_EXPROP_FUNC_SQRT,
    JIM_EXPROP_FUNC_POW,
    JIM_EXPROP_FUNC_HYPOT,
    JIM_EXPROP_FUNC_FMOD,
};

struct JimExprState
{
    Jim_Obj **stack;
struct JimExprNode {
    int type;
    struct Jim_Obj *objPtr;

    struct JimExprNode *left;
    int stacklen;
    int opcode;
    int skip;
    struct JimExprNode *right;
    struct JimExprNode *ternary;
};


typedef struct Jim_ExprOperator
{
    const char *name;
    int (*funcop) (Jim_Interp *interp, struct JimExprState * e);
    int (*funcop) (Jim_Interp *interp, struct JimExprNode *opnode);
    unsigned char precedence;
    unsigned char arity;
    unsigned char lazy;
    unsigned char attr;
    unsigned char namelen;
} Jim_ExprOperator;

static void ExprPush(struct JimExprState *e, Jim_Obj *obj)
static int JimExprGetTerm(Jim_Interp *interp, struct JimExprNode *node, Jim_Obj **objPtrPtr);
{
    Jim_IncrRefCount(obj);
    e->stack[e->stacklen++] = obj;
}

static Jim_Obj *ExprPop(struct JimExprState *e)
{
static int JimExprGetTermBoolean(Jim_Interp *interp, struct JimExprNode *node);
static int JimExprEvalTermNode(Jim_Interp *interp, struct JimExprNode *node);

    return e->stack[--e->stacklen];
}

static int JimExprOpNumUnary(Jim_Interp *interp, struct JimExprState *e)
static int JimExprOpNumUnary(Jim_Interp *interp, struct JimExprNode *node)
{
    int intresult = 1;
    int rc = JIM_OK;
    int rc;
    Jim_Obj *A = ExprPop(e);
    double dA, dC = 0;
    jim_wide wA, wC = 0;
    Jim_Obj *A;

    if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
        return rc;
    }

    if ((A->typePtr != &doubleObjType || A->bytes) && JimGetWideNoErr(interp, A, &wA) == JIM_OK) {
        switch (e->opcode) {
        switch (node->type) {
            case JIM_EXPROP_FUNC_INT:
            case JIM_EXPROP_FUNC_WIDE:
            case JIM_EXPROP_FUNC_ROUND:
            case JIM_EXPROP_UNARYPLUS:
                wC = wA;
                break;
            case JIM_EXPROP_FUNC_DOUBLE:
12651
12652
12653
12654
12655
12656
12657
12658

12659
12660
12661
12662
12663
12664
12665
12666
12667
12668
12669
12670
12671



12672

12673
12674
12675
12676
12677
12678
12679
12680
12681
12682
12683
12684
12685
12686
12687
12688
12689

12690
12691
12692

12693
12694
12695
12696
12697
12698
12699
12700
12701
12702
12703
12704
12705
12706
12707
12708
12709

12710

12711
12712
12713
12714








12715
12716

12717
12718

12719
12720
12721
12722

12723
12724
12725
12726
12727
12728
12729
12730
12731
12732
12733
12734

12735
12736

12737
12738

12739
12740
12741
12742
12743
12744

12745
12746

12747
12748





12749
12750
12751
12752

12753
12754
12755
12756
12757
12758
12759
12577
12578
12579
12580
12581
12582
12583

12584
12585
12586
12587
12588
12589
12590
12591
12592
12593
12594
12595
12596
12597
12598
12599
12600
12601
12602
12603
12604
12605
12606
12607
12608
12609
12610
12611
12612
12613
12614
12615
12616
12617
12618

12619
12620
12621

12622
12623
12624
12625
12626
12627
12628
12629
12630
12631
12632
12633
12634
12635
12636
12637
12638

12639
12640
12641




12642
12643
12644
12645
12646
12647
12648
12649
12650

12651
12652

12653
12654
12655
12656

12657
12658
12659
12660
12661
12662
12663
12664
12665
12666
12667
12668

12669
12670

12671
12672

12673
12674
12675
12676
12677
12678

12679
12680
12681
12682


12683
12684
12685
12686
12687
12688
12689
12690

12691
12692
12693
12694
12695
12696
12697
12698







-
+













+
+
+

+
















-
+


-
+
















-
+

+
-
-
-
-
+
+
+
+
+
+
+
+

-
+

-
+



-
+











-
+

-
+

-
+





-
+


+
-
-
+
+
+
+
+



-
+







                wC = !wA;
                break;
            default:
                abort();
        }
    }
    else if ((rc = Jim_GetDouble(interp, A, &dA)) == JIM_OK) {
        switch (e->opcode) {
        switch (node->type) {
            case JIM_EXPROP_FUNC_INT:
            case JIM_EXPROP_FUNC_WIDE:
                wC = dA;
                break;
            case JIM_EXPROP_FUNC_ROUND:
                wC = dA < 0 ? (dA - 0.5) : (dA + 0.5);
                break;
            case JIM_EXPROP_FUNC_DOUBLE:
            case JIM_EXPROP_UNARYPLUS:
                dC = dA;
                intresult = 0;
                break;
            case JIM_EXPROP_FUNC_ABS:
#ifdef JIM_MATH_FUNCTIONS
                dC = fabs(dA);
#else
                dC = dA >= 0 ? dA : -dA;
#endif
                intresult = 0;
                break;
            case JIM_EXPROP_UNARYMINUS:
                dC = -dA;
                intresult = 0;
                break;
            case JIM_EXPROP_NOT:
                wC = !dA;
                break;
            default:
                abort();
        }
    }

    if (rc == JIM_OK) {
        if (intresult) {
            ExprPush(e, Jim_NewIntObj(interp, wC));
            Jim_SetResultInt(interp, wC);
        }
        else {
            ExprPush(e, Jim_NewDoubleObj(interp, dC));
            Jim_SetResult(interp, Jim_NewDoubleObj(interp, dC));
        }
    }

    Jim_DecrRefCount(interp, A);

    return rc;
}

static double JimRandDouble(Jim_Interp *interp)
{
    unsigned long x;
    JimRandomBytes(interp, &x, sizeof(x));

    return (double)x / (unsigned long)~0;
}

static int JimExprOpIntUnary(Jim_Interp *interp, struct JimExprState *e)
static int JimExprOpIntUnary(Jim_Interp *interp, struct JimExprNode *node)
{
    jim_wide wA;
    Jim_Obj *A = ExprPop(e);
    jim_wide wA;

    int rc = Jim_GetWide(interp, A, &wA);
    Jim_Obj *A;
    int rc;

    if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
        return rc;
    }

    rc = Jim_GetWide(interp, A, &wA);
    if (rc == JIM_OK) {
        switch (e->opcode) {
        switch (node->type) {
            case JIM_EXPROP_BITNOT:
                ExprPush(e, Jim_NewIntObj(interp, ~wA));
                Jim_SetResultInt(interp, ~wA);
                break;
            case JIM_EXPROP_FUNC_SRAND:
                JimPrngSeed(interp, (unsigned char *)&wA, sizeof(wA));
                ExprPush(e, Jim_NewDoubleObj(interp, JimRandDouble(interp)));
                Jim_SetResult(interp, Jim_NewDoubleObj(interp, JimRandDouble(interp)));
                break;
            default:
                abort();
        }
    }

    Jim_DecrRefCount(interp, A);

    return rc;
}

static int JimExprOpNone(Jim_Interp *interp, struct JimExprState *e)
static int JimExprOpNone(Jim_Interp *interp, struct JimExprNode *node)
{
    JimPanic((e->opcode != JIM_EXPROP_FUNC_RAND, "JimExprOpNone only support rand()"));
    JimPanic((node->type != JIM_EXPROP_FUNC_RAND, "JimExprOpNone only support rand()"));

    ExprPush(e, Jim_NewDoubleObj(interp, JimRandDouble(interp)));
    Jim_SetResult(interp, Jim_NewDoubleObj(interp, JimRandDouble(interp)));

    return JIM_OK;
}

#ifdef JIM_MATH_FUNCTIONS
static int JimExprOpDoubleUnary(Jim_Interp *interp, struct JimExprState *e)
static int JimExprOpDoubleUnary(Jim_Interp *interp, struct JimExprNode *node)
{
    int rc;
    double dA, dC;
    Jim_Obj *A = ExprPop(e);
    double dA, dC;
    Jim_Obj *A;

    if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
        return rc;
    }

    rc = Jim_GetDouble(interp, A, &dA);
    if (rc == JIM_OK) {
        switch (e->opcode) {
        switch (node->type) {
            case JIM_EXPROP_FUNC_SIN:
                dC = sin(dA);
                break;
            case JIM_EXPROP_FUNC_COS:
                dC = cos(dA);
                break;
            case JIM_EXPROP_FUNC_TAN:
12794
12795
12796
12797
12798
12799
12800
12801

12802
12803
12804
12805
12806
12807
12808
12809
12810
12811

12812


12813
12814
12815
12816











12817
12818
12819
12820
12821
12822
12823

12824
12825
12826
12827
12828
12829
12830
12733
12734
12735
12736
12737
12738
12739

12740
12741
12742
12743
12744
12745
12746
12747
12748
12749

12750
12751
12752
12753




12754
12755
12756
12757
12758
12759
12760
12761
12762
12763
12764
12765
12766
12767
12768
12769
12770

12771
12772
12773
12774
12775
12776
12777
12778







-
+









-
+

+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+






-
+







                break;
            case JIM_EXPROP_FUNC_SQRT:
                dC = sqrt(dA);
                break;
            default:
                abort();
        }
        ExprPush(e, Jim_NewDoubleObj(interp, dC));
        Jim_SetResult(interp, Jim_NewDoubleObj(interp, dC));
    }

    Jim_DecrRefCount(interp, A);

    return rc;
}
#endif


static int JimExprOpIntBin(Jim_Interp *interp, struct JimExprState *e)
static int JimExprOpIntBin(Jim_Interp *interp, struct JimExprNode *node)
{
    jim_wide wA, wB;
    int rc;
    Jim_Obj *B = ExprPop(e);
    Jim_Obj *A = ExprPop(e);
    jim_wide wA, wB;
    int rc = JIM_ERR;
    Jim_Obj *A, *B;

    if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
        return rc;
    }
    if ((rc = JimExprGetTerm(interp, node->right, &B)) != JIM_OK) {
        Jim_DecrRefCount(interp, A);
        return rc;
    }

    rc = JIM_ERR;

    if (Jim_GetWide(interp, A, &wA) == JIM_OK && Jim_GetWide(interp, B, &wB) == JIM_OK) {
        jim_wide wC;

        rc = JIM_OK;

        switch (e->opcode) {
        switch (node->type) {
            case JIM_EXPROP_LSHIFT:
                wC = wA << wB;
                break;
            case JIM_EXPROP_RSHIFT:
                wC = wA >> wB;
                break;
            case JIM_EXPROP_BITAND:
12857
12858
12859
12860
12861
12862
12863
12864

12865
12866
12867
12868
12869

12870
12871
12872

12873
12874
12875
12876
12877
12878
12879
12880
12881

12882
12883
12884
12885
12886
12887
12888
12889
12890
12891
12892
12893

12894
12895
12896
12897
12898

12899




12900

12901


12902
12903
12904
12905
12906
12907

12908
12909

12910
12911





12912
12913

12914
12915
12916

12917
12918
12919

12920
12921
12922

12923
12924
12925
12926

12927
12928
12929
12930
12931
12932
12933
12934
12935
12936

12937
12938
12939
12940
12941

12942
12943
12944

12945
12946
12947

12948
12949
12950

12951
12952
12953

12954
12955
12956
12957

12958
12959
12960
12961

12962
12963


12964
12965
12966

12967
12968


12969
12970















12971
12972
12973
12974
12975

12976
12977
12978

12979
12980
12981

12982
12983
12984
12985
12986
12987
12988
12989
12990
12991
12992
12993

12994
12995
12996

12997
12998
12999
13000

13001
13002
13003
13004

13005
13006
13007
13008

13009
13010
13011
13012

13013
13014
13015
13016

13017
13018
13019
13020
13021
13022
13023

13024
13025

13026
13027
13028

13029
13030
13031

13032
13033
13034

13035
13036
13037

13038
13039
13040

13041
13042
13043

13044
13045
13046
13047

13048
13049
13050
13051
13052
13053

13054
13055
13056
13057
13058
13059
13060
13061

13062
13063
13064
13065






13066
13067
13068
13069
13070
13071
13072
13073
13074
13075
13076
13077
13078
13079
13080
13081


13082

13083
13084
13085
13086
13087
13088
13089













13090
13091
13092
13093

13094
13095
13096
13097
13098
13099
13100
13101
13102
13103
13104
13105
13106

13107
13108
13109
13110
13111

13112
13113
13114
13115
13116
13117





13118
13119
13120

13121
13122
13123


13124
13125
13126
13127

13128
13129
13130
13131
13132
13133

13134
13135
13136

13137
13138
13139
13140
13141
13142
13143
13144
13145
13146
13147
13148
13149

13150
13151
13152

13153
13154
13155

13156
13157
13158
13159
13160
13161

13162
13163

13164
13165

13166
13167

13168
13169

13170
13171

13172
13173
13174


13175
13176
13177

13178
13179
13180

13181
13182
13183

13184
13185
13186
13187
13188
13189
13190
13191
13192
13193
13194

13195
13196
13197
13198

13199
13200
13201
13202
13203
13204
13205
13206







13207
13208
13209
13210
13211
13212


13213
13214
13215
13216
13217
13218
13219
13220
13221
13222
13223
13224
13225
13226
13227
13228
13229

13230
13231
13232
13233
13234
13235

13236
13237
13238


13239
13240

13241
13242
13243
13244
13245
13246
13247




13248
13249

13250
13251

13252
13253
13254
13255
13256
13257


13258
13259
13260
13261

13262
13263
13264
13265
13266
13267

13268
13269

13270
13271
13272
13273


13274
13275
13276
13277
13278
13279
13280
12805
12806
12807
12808
12809
12810
12811

12812
12813
12814
12815
12816

12817
12818
12819

12820
12821
12822
12823
12824
12825
12826
12827
12828

12829

12830
12831
12832
12833
12834
12835
12836
12837
12838
12839

12840
12841

12842
12843
12844
12845
12846
12847
12848
12849
12850

12851

12852
12853
12854
12855
12856
12857
12858

12859
12860

12861
12862
12863
12864
12865
12866
12867
12868
12869

12870
12871
12872

12873
12874
12875

12876
12877
12878

12879
12880
12881
12882
12883
12884
12885
12886
12887
12888
12889
12890
12891
12892
12893
12894
12895
12896

12897
12898

12899
12900
12901

12902
12903
12904

12905
12906
12907

12908
12909
12910

12911
12912
12913


12914

12915
12916

12917


12918
12919
12920
12921

12922


12923
12924
12925
12926
12927
12928
12929
12930
12931
12932
12933
12934
12935
12936
12937
12938
12939
12940
12941
12942

12943
12944

12945
12946
12947

12948
12949
12950

12951
12952
12953
12954
12955
12956
12957
12958
12959
12960
12961
12962

12963
12964
12965

12966

12967
12968

12969

12970
12971

12972

12973
12974

12975

12976
12977

12978

12979
12980

12981



12982
12983
12984

12985
12986

12987
12988
12989

12990
12991
12992

12993
12994
12995

12996
12997
12998

12999
13000
13001

13002
13003
13004

13005
13006
13007


13008


13009
13010
13011

13012








13013
13014
13015

13016
13017
13018
13019
13020
13021
13022
13023
13024
13025
13026
13027
13028
13029
13030
13031
13032
13033
13034
13035
13036
13037
13038
13039
13040

13041
13042






13043
13044
13045
13046
13047
13048
13049
13050
13051
13052
13053
13054
13055
13056
13057
13058

13059
13060
13061
13062
13063
13064
13065
13066
13067
13068
13069
13070
13071

13072
13073
13074
13075
13076

13077
13078
13079
13080
13081
13082
13083
13084
13085
13086
13087
13088
13089
13090

13091
13092


13093
13094
13095



13096






13097



13098



13099









13100



13101
13102
13103

13104
13105



13106

13107


13108


13109


13110


13111


13112



13113
13114

13115

13116



13117
13118
13119

13120
13121







13122


13123

13124


13125








13126
13127
13128
13129
13130
13131
13132






13133
13134














13135


13136






13137



13138
13139


13140







13141
13142
13143
13144


13145


13146






13147
13148
13149



13150
13151
13152
13153
13154


13155


13156
13157
13158


13159
13160
13161
13162
13163
13164
13165
13166
13167







-
+




-
+


-
+








-
+
-










-
+

-



+

+
+
+
+
-
+
-
+
+





-
+

-
+


+
+
+
+
+

-
+


-
+


-
+


-
+




+










+

-


-
+


-
+


-
+


-
+


-
+


-
-
+
-


-
+
-
-
+
+


-
+
-
-
+
+


+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-


-
+


-
+


-
+











-
+


-
+
-


-
+
-


-
+
-


-
+
-


-
+
-


-
+
-
-
-



-
+

-
+


-
+


-
+


-
+


-
+


-
+


-
+


-
-
+
-
-



-
+
-
-
-
-
-
-
-
-
+


-

+
+
+
+
+
+
















+
+
-
+

-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+



-
+












-
+




-
+






+
+
+
+
+


-
+

-
-
+
+

-
-
-
+
-
-
-
-
-
-
+
-
-
-
+
-
-
-

-
-
-
-
-
-
-
-
-
+
-
-
-
+


-
+

-
-
-

-
+
-
-
+
-
-
+
-
-
+
-
-
+
-
-
+
-
-
-
+
+
-

-
+
-
-
-
+


-
+

-
-
-
-
-
-
-

-
-
+
-

-
-
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-

-
-
+
-
-
-
-
-
-
+
-
-
-
+
+
-
-
+
-
-
-
-
-
-
-
+
+
+
+
-
-
+
-
-
+
-
-
-
-
-
-
+
+

-
-
-
+




-
-
+
-
-
+


-
-
+
+







                    if (negative) {
                        wC = -wC;
                    }
                }
                break;
            case JIM_EXPROP_ROTL:
            case JIM_EXPROP_ROTR:{
                    

                    unsigned long uA = (unsigned long)wA;
                    unsigned long uB = (unsigned long)wB;
                    const unsigned int S = sizeof(unsigned long) * 8;

                    

                    uB %= S;

                    if (e->opcode == JIM_EXPROP_ROTR) {
                    if (node->type == JIM_EXPROP_ROTR) {
                        uB = S - uB;
                    }
                    wC = (unsigned long)(uA << uB) | (uA >> (S - uB));
                    break;
                }
            default:
                abort();
        }
        ExprPush(e, Jim_NewIntObj(interp, wC));
        Jim_SetResultInt(interp, wC);

    }

    Jim_DecrRefCount(interp, A);
    Jim_DecrRefCount(interp, B);

    return rc;
}



static int JimExprOpBin(Jim_Interp *interp, struct JimExprState *e)
static int JimExprOpBin(Jim_Interp *interp, struct JimExprNode *node)
{
    int intresult = 1;
    int rc = JIM_OK;
    double dA, dB, dC = 0;
    jim_wide wA, wB, wC = 0;
    Jim_Obj *A, *B;

    if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
        return rc;
    }
    if ((rc = JimExprGetTerm(interp, node->right, &B)) != JIM_OK) {
    Jim_Obj *B = ExprPop(e);
        Jim_DecrRefCount(interp, A);
    Jim_Obj *A = ExprPop(e);
        return rc;
    }

    if ((A->typePtr != &doubleObjType || A->bytes) &&
        (B->typePtr != &doubleObjType || B->bytes) &&
        JimGetWideNoErr(interp, A, &wA) == JIM_OK && JimGetWideNoErr(interp, B, &wB) == JIM_OK) {

        


        switch (e->opcode) {
        switch (node->type) {
            case JIM_EXPROP_POW:
            case JIM_EXPROP_FUNC_POW:
                if (wA == 0 && wB < 0) {
                    Jim_SetResultString(interp, "exponentiation of zero by negative power", -1);
                    rc = JIM_ERR;
                    goto done;
                }
                wC = JimPowWide(wA, wB);
                break;
                goto intresult;
            case JIM_EXPROP_ADD:
                wC = wA + wB;
                break;
                goto intresult;
            case JIM_EXPROP_SUB:
                wC = wA - wB;
                break;
                goto intresult;
            case JIM_EXPROP_MUL:
                wC = wA * wB;
                break;
                goto intresult;
            case JIM_EXPROP_DIV:
                if (wB == 0) {
                    Jim_SetResultString(interp, "Division by zero", -1);
                    rc = JIM_ERR;
                    goto done;
                }
                else {
                    if (wB < 0) {
                        wB = -wB;
                        wA = -wA;
                    }
                    wC = wA / wB;
                    if (wA % wB < 0) {
                        wC--;
                    }
                    goto intresult;
                }
                break;
            case JIM_EXPROP_LT:
                wC = wA < wB;
                break;
                goto intresult;
            case JIM_EXPROP_GT:
                wC = wA > wB;
                break;
                goto intresult;
            case JIM_EXPROP_LTE:
                wC = wA <= wB;
                break;
                goto intresult;
            case JIM_EXPROP_GTE:
                wC = wA >= wB;
                break;
                goto intresult;
            case JIM_EXPROP_NUMEQ:
                wC = wA == wB;
                break;
                goto intresult;
            case JIM_EXPROP_NUMNE:
                wC = wA != wB;
                break;
            default:
                goto intresult;
                abort();
        }
    }
    else if (Jim_GetDouble(interp, A, &dA) == JIM_OK && Jim_GetDouble(interp, B, &dB) == JIM_OK) {
    if (Jim_GetDouble(interp, A, &dA) == JIM_OK && Jim_GetDouble(interp, B, &dB) == JIM_OK) {
        intresult = 0;
        switch (e->opcode) {
        switch (node->type) {
#ifndef JIM_MATH_FUNCTIONS
            case JIM_EXPROP_POW:
            case JIM_EXPROP_FUNC_POW:
#ifdef JIM_MATH_FUNCTIONS
            case JIM_EXPROP_FUNC_ATAN2:
                dC = pow(dA, dB);
#else
            case JIM_EXPROP_FUNC_HYPOT:
            case JIM_EXPROP_FUNC_FMOD:
                Jim_SetResultString(interp, "unsupported", -1);
                rc = JIM_ERR;
                goto done;
#else
            case JIM_EXPROP_POW:
            case JIM_EXPROP_FUNC_POW:
                dC = pow(dA, dB);
                goto doubleresult;
            case JIM_EXPROP_FUNC_ATAN2:
                dC = atan2(dA, dB);
                goto doubleresult;
            case JIM_EXPROP_FUNC_HYPOT:
                dC = hypot(dA, dB);
                goto doubleresult;
            case JIM_EXPROP_FUNC_FMOD:
                dC = fmod(dA, dB);
                goto doubleresult;
#endif
                break;
            case JIM_EXPROP_ADD:
                dC = dA + dB;
                break;
                goto doubleresult;
            case JIM_EXPROP_SUB:
                dC = dA - dB;
                break;
                goto doubleresult;
            case JIM_EXPROP_MUL:
                dC = dA * dB;
                break;
                goto doubleresult;
            case JIM_EXPROP_DIV:
                if (dB == 0) {
#ifdef INFINITY
                    dC = dA < 0 ? -INFINITY : INFINITY;
#else
                    dC = (dA < 0 ? -1.0 : 1.0) * strtod("Inf", NULL);
#endif
                }
                else {
                    dC = dA / dB;
                }
                break;
                goto doubleresult;
            case JIM_EXPROP_LT:
                wC = dA < dB;
                intresult = 1;
                goto intresult;
                break;
            case JIM_EXPROP_GT:
                wC = dA > dB;
                intresult = 1;
                goto intresult;
                break;
            case JIM_EXPROP_LTE:
                wC = dA <= dB;
                intresult = 1;
                goto intresult;
                break;
            case JIM_EXPROP_GTE:
                wC = dA >= dB;
                intresult = 1;
                goto intresult;
                break;
            case JIM_EXPROP_NUMEQ:
                wC = dA == dB;
                intresult = 1;
                goto intresult;
                break;
            case JIM_EXPROP_NUMNE:
                wC = dA != dB;
                intresult = 1;
                goto intresult;
                break;
            default:
                abort();
        }
    }
    else {
        


        

        int i = Jim_StringCompareObj(interp, A, B, 0);

        switch (e->opcode) {
        switch (node->type) {
            case JIM_EXPROP_LT:
                wC = i < 0;
                break;
                goto intresult;
            case JIM_EXPROP_GT:
                wC = i > 0;
                break;
                goto intresult;
            case JIM_EXPROP_LTE:
                wC = i <= 0;
                break;
                goto intresult;
            case JIM_EXPROP_GTE:
                wC = i >= 0;
                break;
                goto intresult;
            case JIM_EXPROP_NUMEQ:
                wC = i == 0;
                break;
                goto intresult;
            case JIM_EXPROP_NUMNE:
                wC = i != 0;
                break;
            default:
                goto intresult;
                rc = JIM_ERR;
                break;
        }
    }

    if (rc == JIM_OK) {
    rc = JIM_ERR;
        if (intresult) {
            ExprPush(e, Jim_NewIntObj(interp, wC));
        }
        else {
            ExprPush(e, Jim_NewDoubleObj(interp, dC));
        }
    }

done:
    Jim_DecrRefCount(interp, A);
    Jim_DecrRefCount(interp, B);

    return rc;
intresult:
    Jim_SetResultInt(interp, wC);
    goto done;
doubleresult:
    Jim_SetResult(interp, Jim_NewDoubleObj(interp, dC));
    goto done;
}

static int JimSearchList(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *valObj)
{
    int listlen;
    int i;

    listlen = Jim_ListLength(interp, listObjPtr);
    for (i = 0; i < listlen; i++) {
        if (Jim_StringEqObj(Jim_ListGetIndex(interp, listObjPtr, i), valObj)) {
            return 1;
        }
    }
    return 0;
}



static int JimExprOpStrBin(Jim_Interp *interp, struct JimExprState *e)
static int JimExprOpStrBin(Jim_Interp *interp, struct JimExprNode *node)
{
    Jim_Obj *B = ExprPop(e);
    Jim_Obj *A = ExprPop(e);

    jim_wide wC;

    switch (e->opcode) {
    Jim_Obj *A, *B;
    jim_wide wC;
    int rc;

    if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
        return rc;
    }
    if ((rc = JimExprGetTerm(interp, node->right, &B)) != JIM_OK) {
        Jim_DecrRefCount(interp, A);
        return rc;
    }

    switch (node->type) {
        case JIM_EXPROP_STREQ:
        case JIM_EXPROP_STRNE:
            wC = Jim_StringEqObj(A, B);
            if (e->opcode == JIM_EXPROP_STRNE) {
            if (node->type == JIM_EXPROP_STRNE) {
                wC = !wC;
            }
            break;
        case JIM_EXPROP_STRIN:
            wC = JimSearchList(interp, B, A);
            break;
        case JIM_EXPROP_STRNI:
            wC = !JimSearchList(interp, B, A);
            break;
        default:
            abort();
    }
    ExprPush(e, Jim_NewIntObj(interp, wC));
    Jim_SetResultInt(interp, wC);

    Jim_DecrRefCount(interp, A);
    Jim_DecrRefCount(interp, B);

    return JIM_OK;
    return rc;
}

static int ExprBool(Jim_Interp *interp, Jim_Obj *obj)
{
    long l;
    double d;
    int b;
    int ret = -1;


    Jim_IncrRefCount(obj);

    if (Jim_GetLong(interp, obj, &l) == JIM_OK) {
        return l != 0;
        ret = (l != 0);
    }
    if (Jim_GetDouble(interp, obj, &d) == JIM_OK) {
        return d != 0;
    else if (Jim_GetDouble(interp, obj, &d) == JIM_OK) {
        ret = (d != 0);
    }
    return -1;
}

    else if (Jim_GetBoolean(interp, obj, &b) == JIM_OK) {
static int JimExprOpAndLeft(Jim_Interp *interp, struct JimExprState *e)
{
    Jim_Obj *skip = ExprPop(e);
    Jim_Obj *A = ExprPop(e);
    int rc = JIM_OK;

        ret = (b != 0);
    switch (ExprBool(interp, A)) {
        case 0:
            
    }
            e->skip = JimWideValue(skip);
            ExprPush(e, Jim_NewIntObj(interp, 0));
            break;

        case 1:
            
            break;

        case -1:
            
            rc = JIM_ERR;
    }
    Jim_DecrRefCount(interp, A);
    Jim_DecrRefCount(interp, obj);
    Jim_DecrRefCount(interp, skip);

    return rc;
    return ret;
}

static int JimExprOpOrLeft(Jim_Interp *interp, struct JimExprState *e)
static int JimExprOpAnd(Jim_Interp *interp, struct JimExprNode *node)
{
    Jim_Obj *skip = ExprPop(e);
    Jim_Obj *A = ExprPop(e);
    int rc = JIM_OK;

    switch (ExprBool(interp, A)) {
    int result = JimExprGetTermBoolean(interp, node->left);
        case 0:
            

            break;

    if (result == 1) {
        case 1:
            

            e->skip = JimWideValue(skip);
            ExprPush(e, Jim_NewIntObj(interp, 1));
        result = JimExprGetTermBoolean(interp, node->right);
            break;

    }
        case -1:
            
            rc = JIM_ERR;
    if (result == -1) {
        return JIM_ERR;
            break;
    }
    Jim_DecrRefCount(interp, A);
    Jim_SetResultInt(interp, result);
    Jim_DecrRefCount(interp, skip);

    return rc;
    return JIM_OK;
}

static int JimExprOpAndOrRight(Jim_Interp *interp, struct JimExprState *e)
static int JimExprOpOr(Jim_Interp *interp, struct JimExprNode *node)
{
    Jim_Obj *A = ExprPop(e);
    int rc = JIM_OK;

    switch (ExprBool(interp, A)) {
        case 0:
            ExprPush(e, Jim_NewIntObj(interp, 0));
            break;

        case 1:
            ExprPush(e, Jim_NewIntObj(interp, 1));
    int result = JimExprGetTermBoolean(interp, node->left);
            break;

        case -1:
            
    if (result == 0) {
            rc = JIM_ERR;
            break;
    }
    Jim_DecrRefCount(interp, A);

    return rc;
}


        result = JimExprGetTermBoolean(interp, node->right);
    }
    if (result == -1) {
        return JIM_ERR;
    }
    Jim_SetResultInt(interp, result);
static int JimExprOpTernaryLeft(Jim_Interp *interp, struct JimExprState *e)
{
    Jim_Obj *skip = ExprPop(e);
    Jim_Obj *A = ExprPop(e);
    int rc = JIM_OK;

    return JIM_OK;
}
    
    ExprPush(e, A);

    switch (ExprBool(interp, A)) {
        case 0:
            
            e->skip = JimWideValue(skip);
            
            ExprPush(e, Jim_NewIntObj(interp, 0));
            break;

        case 1:
            
            break;

        case -1:
            
static int JimExprOpTernary(Jim_Interp *interp, struct JimExprNode *node)
            rc = JIM_ERR;
            break;
    }
    Jim_DecrRefCount(interp, A);
    Jim_DecrRefCount(interp, skip);

{
    return rc;
}


    int result = JimExprGetTermBoolean(interp, node->left);
static int JimExprOpColonLeft(Jim_Interp *interp, struct JimExprState *e)
{

    Jim_Obj *skip = ExprPop(e);
    Jim_Obj *B = ExprPop(e);
    Jim_Obj *A = ExprPop(e);

    
    if (ExprBool(interp, A)) {
        
    if (result == 1) {

        return JimExprEvalTermNode(interp, node->right);
    }
        e->skip = JimWideValue(skip);
        
    else if (result == 0) {
        ExprPush(e, B);
    }


    Jim_DecrRefCount(interp, skip);
    Jim_DecrRefCount(interp, A);
    Jim_DecrRefCount(interp, B);
    return JIM_OK;
}
        return JimExprEvalTermNode(interp, node->ternary);
    }

static int JimExprOpNull(Jim_Interp *interp, struct JimExprState *e)
{
    return JIM_OK;
    return JIM_ERR;
}

enum
{
    LAZY_NONE,
    LAZY_OP,
    OP_FUNC = 0x0001,
    LAZY_LEFT,
    LAZY_RIGHT
    OP_RIGHT_ASSOC = 0x0002,
};

#define OPRINIT(N, P, A, F) {N, F, P, A, LAZY_NONE, sizeof(N) - 1}
#define OPRINIT_LAZY(N, P, A, F, L) {N, F, P, A, L, sizeof(N) - 1}
#define OPRINIT_ATTR(N, P, ARITY, F, ATTR) {N, F, P, ARITY, ATTR, sizeof(N) - 1}
#define OPRINIT(N, P, ARITY, F) OPRINIT_ATTR(N, P, ARITY, F, 0)

static const struct Jim_ExprOperator Jim_ExprOperators[] = {
    OPRINIT("*", 110, 2, JimExprOpBin),
    OPRINIT("/", 110, 2, JimExprOpBin),
    OPRINIT("%", 110, 2, JimExprOpIntBin),

    OPRINIT("-", 100, 2, JimExprOpBin),
13294
13295
13296
13297
13298
13299
13300
13301
13302

13303
13304
13305
13306

13307
13308
13309


13310
13311
13312
13313
13314
13315
13316
13317

13318
13319
13320
13321
13322
13323
13324
13325
13326
13327
13328




13329
13330
13331
13332
13333
13334
13335
13336
13337
13338







13339
13340
13341
13342
13343
13344
13345
13346
13347
13348
13349
13350
13351
13352
13353
13354
13355
13356



















13357
13358
13359
13360

13361
13362
13363
13364
13365
13366
13367

13368
13369
13370
13371
13372
13373
13374
13375
13376

13377
13378
13379
13380
13381
13382
13383
13181
13182
13183
13184
13185
13186
13187


13188




13189



13190
13191


13192



13193

13194
13195
13196
13197
13198
13199
13200
13201




13202
13203
13204
13205
13206
13207
13208







13209
13210
13211
13212
13213
13214
13215
13216
13217
















13218
13219
13220
13221
13222
13223
13224
13225
13226
13227
13228
13229
13230
13231
13232
13233
13234
13235
13236
13237
13238
13239

13240
13241
13242
13243
13244
13245
13246

13247
13248
13249
13250
13251
13252
13253
13254
13255

13256
13257
13258
13259
13260
13261
13262
13263







-
-
+
-
-
-
-
+
-
-
-
+
+
-
-

-
-
-

-
+







-
-
-
-
+
+
+
+



-
-
-
-
-
-
-
+
+
+
+
+
+
+


-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



-
+






-
+








-
+







    OPRINIT("==", 70, 2, JimExprOpBin),
    OPRINIT("!=", 70, 2, JimExprOpBin),

    OPRINIT("&", 50, 2, JimExprOpIntBin),
    OPRINIT("^", 49, 2, JimExprOpIntBin),
    OPRINIT("|", 48, 2, JimExprOpIntBin),

    OPRINIT_LAZY("&&", 10, 2, NULL, LAZY_OP),
    OPRINIT_LAZY(NULL, 10, 2, JimExprOpAndLeft, LAZY_LEFT),
    OPRINIT("&&", 10, 2, JimExprOpAnd),
    OPRINIT_LAZY(NULL, 10, 2, JimExprOpAndOrRight, LAZY_RIGHT),

    OPRINIT_LAZY("||", 9, 2, NULL, LAZY_OP),
    OPRINIT_LAZY(NULL, 9, 2, JimExprOpOrLeft, LAZY_LEFT),
    OPRINIT("||", 9, 2, JimExprOpOr),
    OPRINIT_LAZY(NULL, 9, 2, JimExprOpAndOrRight, LAZY_RIGHT),

    OPRINIT_LAZY("?", 5, 2, JimExprOpNull, LAZY_OP),
    OPRINIT_ATTR("?", 5, 3, JimExprOpTernary, OP_RIGHT_ASSOC),
    OPRINIT_ATTR(":", 5, 3, NULL, OP_RIGHT_ASSOC),
    OPRINIT_LAZY(NULL, 5, 2, JimExprOpTernaryLeft, LAZY_LEFT),
    OPRINIT_LAZY(NULL, 5, 2, JimExprOpNull, LAZY_RIGHT),

    OPRINIT_LAZY(":", 5, 2, JimExprOpNull, LAZY_OP),
    OPRINIT_LAZY(NULL, 5, 2, JimExprOpColonLeft, LAZY_LEFT),
    OPRINIT_LAZY(NULL, 5, 2, JimExprOpNull, LAZY_RIGHT),

    OPRINIT("**", 250, 2, JimExprOpBin),
    OPRINIT_ATTR("**", 120, 2, JimExprOpBin, OP_RIGHT_ASSOC),

    OPRINIT("eq", 60, 2, JimExprOpStrBin),
    OPRINIT("ne", 60, 2, JimExprOpStrBin),

    OPRINIT("in", 55, 2, JimExprOpStrBin),
    OPRINIT("ni", 55, 2, JimExprOpStrBin),

    OPRINIT("!", 150, 1, JimExprOpNumUnary),
    OPRINIT("~", 150, 1, JimExprOpIntUnary),
    OPRINIT(NULL, 150, 1, JimExprOpNumUnary),
    OPRINIT(NULL, 150, 1, JimExprOpNumUnary),
    OPRINIT_ATTR("!", 150, 1, JimExprOpNumUnary, OP_RIGHT_ASSOC),
    OPRINIT_ATTR("~", 150, 1, JimExprOpIntUnary, OP_RIGHT_ASSOC),
    OPRINIT_ATTR(" -", 150, 1, JimExprOpNumUnary, OP_RIGHT_ASSOC),
    OPRINIT_ATTR(" +", 150, 1, JimExprOpNumUnary, OP_RIGHT_ASSOC),



    OPRINIT("int", 200, 1, JimExprOpNumUnary),
    OPRINIT("wide", 200, 1, JimExprOpNumUnary),
    OPRINIT("abs", 200, 1, JimExprOpNumUnary),
    OPRINIT("double", 200, 1, JimExprOpNumUnary),
    OPRINIT("round", 200, 1, JimExprOpNumUnary),
    OPRINIT("rand", 200, 0, JimExprOpNone),
    OPRINIT("srand", 200, 1, JimExprOpIntUnary),
    OPRINIT_ATTR("int", 200, 1, JimExprOpNumUnary, OP_FUNC),
    OPRINIT_ATTR("wide", 200, 1, JimExprOpNumUnary, OP_FUNC),
    OPRINIT_ATTR("abs", 200, 1, JimExprOpNumUnary, OP_FUNC),
    OPRINIT_ATTR("double", 200, 1, JimExprOpNumUnary, OP_FUNC),
    OPRINIT_ATTR("round", 200, 1, JimExprOpNumUnary, OP_FUNC),
    OPRINIT_ATTR("rand", 200, 0, JimExprOpNone, OP_FUNC),
    OPRINIT_ATTR("srand", 200, 1, JimExprOpIntUnary, OP_FUNC),

#ifdef JIM_MATH_FUNCTIONS
    OPRINIT("sin", 200, 1, JimExprOpDoubleUnary),
    OPRINIT("cos", 200, 1, JimExprOpDoubleUnary),
    OPRINIT("tan", 200, 1, JimExprOpDoubleUnary),
    OPRINIT("asin", 200, 1, JimExprOpDoubleUnary),
    OPRINIT("acos", 200, 1, JimExprOpDoubleUnary),
    OPRINIT("atan", 200, 1, JimExprOpDoubleUnary),
    OPRINIT("sinh", 200, 1, JimExprOpDoubleUnary),
    OPRINIT("cosh", 200, 1, JimExprOpDoubleUnary),
    OPRINIT("tanh", 200, 1, JimExprOpDoubleUnary),
    OPRINIT("ceil", 200, 1, JimExprOpDoubleUnary),
    OPRINIT("floor", 200, 1, JimExprOpDoubleUnary),
    OPRINIT("exp", 200, 1, JimExprOpDoubleUnary),
    OPRINIT("log", 200, 1, JimExprOpDoubleUnary),
    OPRINIT("log10", 200, 1, JimExprOpDoubleUnary),
    OPRINIT("sqrt", 200, 1, JimExprOpDoubleUnary),
    OPRINIT("pow", 200, 2, JimExprOpBin),
    OPRINIT_ATTR("sin", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
    OPRINIT_ATTR("cos", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
    OPRINIT_ATTR("tan", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
    OPRINIT_ATTR("asin", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
    OPRINIT_ATTR("acos", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
    OPRINIT_ATTR("atan", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
    OPRINIT_ATTR("atan2", 200, 2, JimExprOpBin, OP_FUNC),
    OPRINIT_ATTR("sinh", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
    OPRINIT_ATTR("cosh", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
    OPRINIT_ATTR("tanh", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
    OPRINIT_ATTR("ceil", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
    OPRINIT_ATTR("floor", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
    OPRINIT_ATTR("exp", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
    OPRINIT_ATTR("log", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
    OPRINIT_ATTR("log10", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
    OPRINIT_ATTR("sqrt", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
    OPRINIT_ATTR("pow", 200, 2, JimExprOpBin, OP_FUNC),
    OPRINIT_ATTR("hypot", 200, 2, JimExprOpBin, OP_FUNC),
    OPRINIT_ATTR("fmod", 200, 2, JimExprOpBin, OP_FUNC),
#endif
};
#undef OPRINIT
#undef OPRINIT_LAZY
#undef OPRINIT_ATTR

#define JIM_EXPR_OPERATORS_NUM \
    (sizeof(Jim_ExprOperators)/sizeof(struct Jim_ExprOperator))

static int JimParseExpression(struct JimParserCtx *pc)
{
    

    while (isspace(UCHAR(*pc->p)) || (*(pc->p) == '\\' && *(pc->p + 1) == '\n')) {
        if (*pc->p == '\n') {
            pc->linenr++;
        }
        pc->p++;
        pc->len--;
    }

    

    pc->tline = pc->linenr;
    pc->tstart = pc->p;

    if (pc->len == 0) {
        pc->tend = pc->p;
        pc->tt = JIM_TT_EOL;
        pc->eof = 1;
13399
13400
13401
13402
13403
13404
13405
13406

13407
13408
13409
13410
13411
13412
13413
13279
13280
13281
13282
13283
13284
13285

13286
13287
13288
13289
13290
13291
13292
13293







-
+







            break;
        case '[':
            return JimParseCmd(pc);
        case '$':
            if (JimParseVar(pc) == JIM_ERR)
                return JimParseExprOperator(pc);
            else {
                

                if (pc->tt == JIM_TT_EXPRSUGAR) {
                    return JIM_ERR;
                }
                return JIM_OK;
            }
            break;
        case '0':
13428
13429
13430
13431
13432
13433
13434








13435
13436
13437
13438
13439
13440
13441
13442
13443
13444
13445
13446
13447
13448

13449
13450
13451
13452

13453
13454

13455
13456
13457
13458

13459
13460
13461
13462
13463
13464
13465
13308
13309
13310
13311
13312
13313
13314
13315
13316
13317
13318
13319
13320
13321
13322
13323
13324
13325
13326
13327
13328
13329
13330
13331
13332
13333
13334
13335

13336
13337
13338
13339

13340
13341

13342
13343
13344
13345

13346
13347
13348
13349
13350
13351
13352
13353







+
+
+
+
+
+
+
+













-
+



-
+

-
+



-
+







            return JimParseBrace(pc);

        case 'N':
        case 'I':
        case 'n':
        case 'i':
            if (JimParseExprIrrational(pc) == JIM_ERR)
                if (JimParseExprBoolean(pc) == JIM_ERR)
                    return JimParseExprOperator(pc);
            break;
        case 't':
        case 'f':
        case 'o':
        case 'y':
            if (JimParseExprBoolean(pc) == JIM_ERR)
                return JimParseExprOperator(pc);
            break;
        default:
            return JimParseExprOperator(pc);
            break;
    }
    return JIM_OK;
}

static int JimParseExprNumber(struct JimParserCtx *pc)
{
    char *end;

    

    pc->tt = JIM_TT_EXPR_INT;

    jim_strtoull(pc->p, (char **)&pc->p);
    

    if (strchr("eENnIi.", *pc->p) || pc->p == pc->tstart) {
        if (strtod(pc->tstart, &end)) {  }
        if (strtod(pc->tstart, &end)) { }
        if (end == pc->tstart)
            return JIM_ERR;
        if (end > pc->p) {
            

            pc->tt = JIM_TT_EXPR_DOUBLE;
            pc->p = end;
        }
    }
    pc->tend = pc->p - 1;
    pc->len -= (pc->p - pc->tstart);
    return JIM_OK;
13479
13480
13481
13482
13483
13484
13485
13486






























13487
13488
13489

13490

13491
13492

13493
13494
13495

13496
13497

13498
13499
13500
13501
13502
13503



13504
13505
13506

13507
13508
13509
13510
13511


13512
13513
13514
13515
13516
13517
13518
13519
13520
13521
13522
13523
13524
13525
13526
13527

13528
13529
13530
13531
13532
13533
13534
13535
13536
13537
13538
13539
13540
13541
13542
13543
13544

13545
13546






13547
13548
13549
13550
13551
13552
13553
13367
13368
13369
13370
13371
13372
13373
13374
13375
13376
13377
13378
13379
13380
13381
13382
13383
13384
13385
13386
13387
13388
13389
13390
13391
13392
13393
13394
13395
13396
13397
13398
13399
13400
13401
13402
13403
13404
13405
13406
13407
13408

13409
13410

13411
13412


13413
13414

13415
13416
13417
13418



13419
13420
13421
13422
13423

13424
13425
13426
13427


13428
13429
13430
13431
13432
13433
13434
13435
13436
13437
13438
13439
13440
13441
13442
13443
13444

13445
13446
13447
13448









13449
13450
13451
13452

13453
13454
13455
13456
13457
13458
13459
13460
13461
13462
13463
13464
13465
13466
13467
13468








+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



+
-
+

-
+

-
-
+

-
+



-
-
-
+
+
+


-
+



-
-
+
+















-
+



-
-
-
-
-
-
-
-
-




-
+


+
+
+
+
+
+







            pc->tend = pc->p - 1;
            pc->tt = JIM_TT_EXPR_DOUBLE;
            return JIM_OK;
        }
    }
    return JIM_ERR;
}

static int JimParseExprBoolean(struct JimParserCtx *pc)
{
    const char *booleans[] = { "false", "no", "off", "true", "yes", "on", NULL };
    const int lengths[] = { 5, 2, 3, 4, 3, 2, 0 };
    int i;

    for (i = 0; booleans[i]; i++) {
        const char *boolean = booleans[i];
        int length = lengths[i];

        if (strncmp(boolean, pc->p, length) == 0) {
            pc->p += length;
            pc->len -= length;
            pc->tend = pc->p - 1;
            pc->tt = JIM_TT_EXPR_BOOLEAN;
            return JIM_OK;
        }
    }
    return JIM_ERR;
}

static const struct Jim_ExprOperator *JimExprOperatorInfoByOpcode(int opcode)
{
    static Jim_ExprOperator dummy_op;
    if (opcode < JIM_TT_EXPR_OP) {
        return &dummy_op;
    }
    return &Jim_ExprOperators[opcode - JIM_TT_EXPR_OP];
}

static int JimParseExprOperator(struct JimParserCtx *pc)
{
    int i;
    const struct Jim_ExprOperator *bestOp = NULL;
    int bestIdx = -1, bestLen = 0;
    int bestLen = 0;

    

    for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++) {
        const char * const opname = Jim_ExprOperators[i].name;
        const int oplen = Jim_ExprOperators[i].namelen;
        const struct Jim_ExprOperator *op = &Jim_ExprOperators[i];

        if (opname == NULL || opname[0] != pc->p[0]) {
        if (op->name[0] != pc->p[0]) {
            continue;
        }

        if (oplen > bestLen && strncmp(opname, pc->p, oplen) == 0) {
            bestIdx = i + JIM_TT_EXPR_OP;
            bestLen = oplen;
        if (op->namelen > bestLen && strncmp(op->name, pc->p, op->namelen) == 0) {
            bestOp = op;
            bestLen = op->namelen;
        }
    }
    if (bestIdx == -1) {
    if (bestOp == NULL) {
        return JIM_ERR;
    }

    
    if (bestIdx >= JIM_EXPROP_FUNC_FIRST) {

    if (bestOp->attr & OP_FUNC) {
        const char *p = pc->p + bestLen;
        int len = pc->len - bestLen;

        while (len && isspace(UCHAR(*p))) {
            len--;
            p++;
        }
        if (*p != '(') {
            return JIM_ERR;
        }
    }
    pc->tend = pc->p + bestLen - 1;
    pc->p += bestLen;
    pc->len -= bestLen;

    pc->tt = bestIdx;
    pc->tt = (bestOp - Jim_ExprOperators) + JIM_TT_EXPR_OP;
    return JIM_OK;
}

static const struct Jim_ExprOperator *JimExprOperatorInfoByOpcode(int opcode)
{
    static Jim_ExprOperator dummy_op;
    if (opcode < JIM_TT_EXPR_OP) {
        return &dummy_op;
    }
    return &Jim_ExprOperators[opcode - JIM_TT_EXPR_OP];
}

const char *jim_tt_name(int type)
{
    static const char * const tt_names[JIM_TT_EXPR_OP] =
        { "NIL", "STR", "ESC", "VAR", "ARY", "CMD", "SEP", "EOL", "EOF", "LIN", "WRD", "(((", ")))", ",,,", "INT",
            "DBL", "$()" };
            "DBL", "BOO", "$()" };
    if (type < JIM_TT_EXPR_OP) {
        return tt_names[type];
    }
    else if (type == JIM_EXPROP_UNARYMINUS) {
        return "-VE";
    }
    else if (type == JIM_EXPROP_UNARYPLUS) {
        return "+VE";
    }
    else {
        const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(type);
        static char buf[20];

        if (op->name) {
            return op->name;
13566
13567
13568
13569
13570
13571
13572
13573

13574
13575
13576
13577
13578





13579
13580

13581
13582
13583
13584
13585
13586
13587











13588
13589
13590
13591
13592
13593

13594
13595
13596
13597
13598
13599
13600

13601
13602
13603
13604
13605
13606
13607
13608
13609

13610
13611
13612
13613

13614
13615
13616

13617
13618
13619
13620
13621
13622
13623
13624
13625
13626
13627
13628
13629
13630
13631
13632
13633
13634
13635
13636
13637
13638
13639
13640
13641
13642
13643
13644
13645
13646

13647
13648
13649
13650
13651
13652
13653
13654
13655
13656
13657
13658
13659
13660
13661
13662
13663
13664
13665
13666
13667
13668
13669
13670
13671
13672
13673
13674
13675
13676
13677
13678
13679
13680
13681
13682
13683
13684
13685
13686
13687
13688
13689
13690
13691
13692
13693
13694
13695
13696
13697
13698
13699
13700
13701
13702
13703
13704
13705
13706
13707
13708
13709
13710
13711
13712
13713
13714
13715
13716
13717
13718
13719
13720
13721
13722
13723
13724
13725
13726
13727
13728
13729
13730
13731
13732
13733
13734
13735
13736
13737
13738
13739
13740
13741
13742
13743
13744
13745
13746
13747
13748
13749
13750
13751
13752
13753
13754
13755
13756
13757
13758
13759
13760
13761
13762
13763
13764
13765
13766
13767
13768
13769
13770
13771


13772
13773
13774
13775
13776
13777
13778
13779
13780
13781
13782
13783
13784
13785
13786
13787
13788
13789
13790
13791
13792
13793
13794
13795
13796
13797
13798









13799

13800
13801



















13802



13803
13804























































































































































































































































































13805
13806
13807
13808
13809
13810
13811
13812
13813
13814
13815
13816
13817
13818
13819
13820
13821
13822
13823
13824
13825
13826
13827
13828
13829
13830
13831
13832
13833
13834
13835
13836
13837
13838
13839
13840
13841
13842
13843
13844
13845
13846
13847
13848
13849
13850
13851

13852
13853

13854
13855
13856
13857
13858
13859
13860
13861
13862
13863
13864
13865
13866
13867
13868
13869
13870
13871
13872


13873
13874

13875
13876
13877
13878
13879
13880
13881
13882
13883
13884
13885
13886
13887
13888
13889
13890
13891
13892
13893
13894
13895
13896
13897
13898
13899
13900
13901
13902
13903
13904
13905
13906
13907
13908
13909
13910
13911
13912
13913
13914
13915
13916
13917
13918
13919
13920
13921
13922
13923
13924
13925
13926
13927
13928
13929
13930
13931
13932
13933
13934
13935
13936
13937
13938
13939
13940
13941
13942
13943
13944
13945
13946
13947
13948
13949
13950
13951
13952
13953
13954
13955
13956
13957
13958
13959
13960
13961
13962
13963
13964
13965
13966
13967
13968
13969
13970
13971
13972
13973
13974
13975
13976
13977
13978
13979
13980
13981
13982
13983
13984
13985
13986
13987
13988

13989
13990
13991
13992
13993
13994

13995
13996
13997
13998
13999
14000
14001
14002
14003
14004
14005
14006
14007

14008
14009
14010
14011
14012
14013
14014
14015
14016
14017
14018
14019
14020
14021
13481
13482
13483
13484
13485
13486
13487

13488
13489




13490
13491
13492
13493
13494
13495

13496
13497
13498





13499
13500
13501
13502
13503
13504
13505
13506
13507
13508
13509
13510
13511
13512
13513
13514

13515
13516
13517
13518
13519
13520
13521

13522
13523
13524
13525
13526
13527
13528
13529
13530

13531
13532
13533
13534

13535



13536






























13537





























































































































13538
13539
























13540


13541
13542
13543
13544
13545
13546
13547
13548
13549
13550
13551


13552
13553
13554
13555
13556
13557
13558
13559
13560
13561
13562
13563
13564
13565
13566
13567
13568
13569
13570
13571
13572
13573
13574


13575
13576
13577
13578
13579
13580
13581
13582
13583
13584
13585
13586
13587
13588
13589
13590
13591
13592
13593
13594
13595
13596
13597
13598
13599
13600
13601
13602
13603
13604
13605
13606
13607
13608
13609
13610
13611
13612
13613
13614
13615
13616
13617
13618
13619
13620
13621
13622
13623
13624
13625
13626
13627
13628
13629
13630
13631
13632
13633
13634
13635
13636
13637
13638
13639
13640
13641
13642
13643
13644
13645
13646
13647
13648
13649
13650
13651
13652
13653
13654
13655
13656
13657
13658
13659
13660
13661
13662
13663
13664
13665
13666
13667
13668
13669
13670
13671
13672
13673
13674
13675
13676
13677
13678
13679
13680
13681
13682
13683
13684
13685
13686
13687
13688
13689
13690
13691
13692
13693
13694
13695
13696
13697
13698
13699
13700
13701
13702
13703
13704
13705
13706
13707
13708
13709
13710
13711
13712
13713
13714
13715
13716
13717
13718
13719
13720
13721
13722
13723
13724
13725
13726
13727
13728
13729
13730
13731
13732
13733
13734
13735
13736
13737
13738
13739
13740
13741
13742
13743
13744
13745
13746
13747
13748
13749
13750
13751
13752
13753
13754
13755
13756
13757
13758
13759
13760
13761
13762
13763
13764
13765
13766
13767
13768
13769
13770
13771
13772
13773
13774
13775
13776
13777
13778
13779
13780
13781
13782
13783
13784
13785
13786
13787
13788
13789
13790
13791
13792
13793
13794
13795
13796
13797
13798
13799
13800
13801
13802
13803
13804
13805
13806
13807
13808
13809
13810
13811
13812
13813
13814
13815
13816
13817
13818
13819
13820
13821
13822
13823
13824
13825
13826
13827
13828
13829
13830
13831
13832
13833
13834
13835
13836
13837
13838
13839
13840
13841
13842
13843
13844
13845
13846
13847
13848
13849
13850
13851
13852
13853
13854
13855
13856












































13857


13858



















13859
13860


13861







































































































13862
13863
13864

13865
13866
13867
13868
13869
13870

13871
13872
13873
13874
13875
13876

13877
13878
13879
13880
13881
13882
13883
13884
13885
13886
13887
13888
13889

13890
13891
13892
13893
13894
13895
13896

13897
13898
13899
13900
13901
13902
13903







-
+

-
-
-
-
+
+
+
+
+

-
+


-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+





-
+






-
+








-
+



-
+
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

-
-
+
+
+
+
+
+
+
+
+

+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-



-






-
+





-
+












-
+






-







    FreeExprInternalRep,
    DupExprInternalRep,
    NULL,
    JIM_TYPE_REFERENCES,
};


typedef struct ExprByteCode
struct ExprTree
{
    ScriptToken *token;         
    int len;                    
    int inUse;                  
} ExprByteCode;
    struct JimExprNode *expr;
    struct JimExprNode *nodes;
    int len;
    int inUse;
};

static void ExprFreeByteCode(Jim_Interp *interp, ExprByteCode * expr)
static void ExprTreeFreeNodes(Jim_Interp *interp, struct JimExprNode *nodes, int num)
{
    int i;

    for (i = 0; i < expr->len; i++) {
        Jim_DecrRefCount(interp, expr->token[i].objPtr);
    }
    Jim_Free(expr->token);
    for (i = 0; i < num; i++) {
        if (nodes[i].objPtr) {
            Jim_DecrRefCount(interp, nodes[i].objPtr);
        }
    }
    Jim_Free(nodes);
}

static void ExprTreeFree(Jim_Interp *interp, struct ExprTree *expr)
{
    ExprTreeFreeNodes(interp, expr->nodes, expr->len);
    Jim_Free(expr);
}

static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
{
    ExprByteCode *expr = (void *)objPtr->internalRep.ptr;
    struct ExprTree *expr = (void *)objPtr->internalRep.ptr;

    if (expr) {
        if (--expr->inUse != 0) {
            return;
        }

        ExprFreeByteCode(interp, expr);
        ExprTreeFree(interp, expr);
    }
}

static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
{
    JIM_NOTUSED(interp);
    JIM_NOTUSED(srcPtr);

    

    dupPtr->typePtr = NULL;
}


struct ExprBuilder {
static int ExprCheckCorrectness(ExprByteCode * expr)
{
    int i;
    int parencount;
    int stacklen = 0;
    int ternary = 0;

    for (i = 0; i < expr->len; i++) {
        ScriptToken *t = &expr->token[i];
        const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(t->type);

        stacklen -= op->arity;
        if (stacklen < 0) {
            break;
        }
        if (t->type == JIM_EXPROP_TERNARY || t->type == JIM_EXPROP_TERNARY_LEFT) {
            ternary++;
        }
        else if (t->type == JIM_EXPROP_COLON || t->type == JIM_EXPROP_COLON_LEFT) {
            ternary--;
        }

        
        stacklen++;
    }
    if (stacklen != 1 || ternary != 0) {
        return JIM_ERR;
    }
    return JIM_OK;
}

static int ExprAddLazyOperator(Jim_Interp *interp, ExprByteCode * expr, ParseToken *t)
{
    int i;
    int level;

    int leftindex, arity, offset;

    
    leftindex = expr->len - 1;

    arity = 1;
    while (arity) {
        ScriptToken *tt = &expr->token[leftindex];

        if (tt->type >= JIM_TT_EXPR_OP) {
            arity += JimExprOperatorInfoByOpcode(tt->type)->arity;
        }
        arity--;
        if (--leftindex < 0) {
            return JIM_ERR;
        }
    }
    leftindex++;

    
    memmove(&expr->token[leftindex + 2], &expr->token[leftindex],
        sizeof(*expr->token) * (expr->len - leftindex));
    expr->len += 2;
    offset = (expr->len - leftindex) - 1;

    expr->token[leftindex + 1].type = t->type + 1;
    expr->token[leftindex + 1].objPtr = interp->emptyObj;

    expr->token[leftindex].type = JIM_TT_EXPR_INT;
    expr->token[leftindex].objPtr = Jim_NewIntObj(interp, offset);

    
    expr->token[expr->len].objPtr = interp->emptyObj;
    expr->token[expr->len].type = t->type + 2;
    expr->len++;

    
    for (i = leftindex - 1; i > 0; i--) {
        const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(expr->token[i].type);
        if (op->lazy == LAZY_LEFT) {
            if (JimWideValue(expr->token[i - 1].objPtr) + i - 1 >= leftindex) {
                JimWideValue(expr->token[i - 1].objPtr) += 2;
            }
        }
    }
    return JIM_OK;
}

static int ExprAddOperator(Jim_Interp *interp, ExprByteCode * expr, ParseToken *t)
{
    struct ScriptToken *token = &expr->token[expr->len];
    const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(t->type);

    if (op->lazy == LAZY_OP) {
        if (ExprAddLazyOperator(interp, expr, t) != JIM_OK) {
            Jim_SetResultFormatted(interp, "Expression has bad operands to %s", op->name);
            return JIM_ERR;
        }
    }
    else {
        token->objPtr = interp->emptyObj;
        token->type = t->type;
        expr->len++;
    }
    return JIM_OK;
}

static int ExprTernaryGetColonLeftIndex(ExprByteCode *expr, int right_index)
{
    int ternary_count = 1;

    right_index--;

    while (right_index > 1) {
        if (expr->token[right_index].type == JIM_EXPROP_TERNARY_LEFT) {
            ternary_count--;
        }
        else if (expr->token[right_index].type == JIM_EXPROP_COLON_RIGHT) {
            ternary_count++;
        }
        else if (expr->token[right_index].type == JIM_EXPROP_COLON_LEFT && ternary_count == 1) {
            return right_index;
        }
        right_index--;
    }

    
    return -1;
}

static int ExprTernaryGetMoveIndices(ExprByteCode *expr, int right_index, int *prev_right_index, int *prev_left_index)
{
    int i = right_index - 1;
    int ternary_count = 1;

    while (i > 1) {
        if (expr->token[i].type == JIM_EXPROP_TERNARY_LEFT) {
            if (--ternary_count == 0 && expr->token[i - 2].type == JIM_EXPROP_COLON_RIGHT) {
                *prev_right_index = i - 2;
                *prev_left_index = ExprTernaryGetColonLeftIndex(expr, *prev_right_index);
                return 1;
            }
        }
        else if (expr->token[i].type == JIM_EXPROP_COLON_RIGHT) {
            if (ternary_count == 0) {
                return 0;
            }
            ternary_count++;
        }
        i--;
    }
    return 0;
}

static void ExprTernaryReorderExpression(Jim_Interp *interp, ExprByteCode *expr)
{
    int i;

    for (i = expr->len - 1; i > 1; i--) {
        int prev_right_index;
        int prev_left_index;
        int j;
        ScriptToken tmp;

    ParseToken *token;
    ParseToken *first_token;
        if (expr->token[i].type != JIM_EXPROP_COLON_RIGHT) {
            continue;
        }

        
        if (ExprTernaryGetMoveIndices(expr, i, &prev_right_index, &prev_left_index) == 0) {
            continue;
        }

        tmp = expr->token[prev_right_index];
        for (j = prev_right_index; j < i; j++) {
            expr->token[j] = expr->token[j + 1];
        }
        expr->token[i] = tmp;

        JimWideValue(expr->token[prev_left_index-1].objPtr) += (i - prev_right_index);

        
        i++;
    }
}

static ExprByteCode *ExprCreateByteCode(Jim_Interp *interp, const ParseTokenList *tokenlist, Jim_Obj *fileNameObj)
{
    Jim_Stack stack;
    ExprByteCode *expr;
    int ok = 1;
    Jim_Obj *exprObjPtr;
    Jim_Obj *fileNameObj;
    struct JimExprNode *nodes;
    struct JimExprNode *next;
};

#ifdef DEBUG_SHOW_EXPR
static void JimShowExprNode(struct JimExprNode *node, int level)
{
    int i;
    for (i = 0; i < level; i++) {
    int prevtt = JIM_TT_NONE;
    int have_ternary = 0;
        printf("  ");
    }
    if (TOKEN_IS_EXPR_OP(node->type)) {
        printf("%s\n", jim_tt_name(node->type));
        if (node->left) {
            JimShowExprNode(node->left, level + 1);
        }
        if (node->right) {
            JimShowExprNode(node->right, level + 1);
        }
        if (node->ternary) {
            JimShowExprNode(node->ternary, level + 1);
        }
    }
    else {
        printf("[%s] %s\n", jim_tt_name(node->type), Jim_String(node->objPtr));
    }
}
#endif

#define EXPR_UNTIL_CLOSE 0x0001
#define EXPR_FUNC_ARGS   0x0002
#define EXPR_TERNARY     0x0004
    
    int count = tokenlist->count - 1;

static int ExprTreeBuildTree(Jim_Interp *interp, struct ExprBuilder *builder, int precedence, int flags, int exp_numterms)
{
    int rc;
    struct JimExprNode *node;

    int exp_stacklen = builder->stack.len + exp_numterms;

    if (builder->level++ > 200) {
        Jim_SetResultString(interp, "Expression too complex", -1);
        return JIM_ERR;
    }

    while (builder->token->type != JIM_TT_EOL) {
        ParseToken *t = builder->token++;
        int prevtt;

        if (t == builder->first_token) {
            prevtt = JIM_TT_NONE;
        }
        else {
            prevtt = t[-1].type;
        }

        if (t->type == JIM_TT_SUBEXPR_START) {
            if (builder->stack.len == exp_stacklen) {
                Jim_SetResultFormatted(interp, "unexpected open parenthesis in expression: \"%#s\"", builder->exprObjPtr);
                return JIM_ERR;
            }
            builder->parencount++;
            rc = ExprTreeBuildTree(interp, builder, 0, EXPR_UNTIL_CLOSE, 1);
            if (rc != JIM_OK) {
                return rc;
            }

        }
        else if (t->type == JIM_TT_SUBEXPR_END) {
            if (!(flags & EXPR_UNTIL_CLOSE)) {
                if (builder->stack.len == exp_stacklen && builder->level > 1) {
                    builder->token--;
                    builder->level--;
                    return JIM_OK;
                }
                Jim_SetResultFormatted(interp, "unexpected closing parenthesis in expression: \"%#s\"", builder->exprObjPtr);
                return JIM_ERR;
            }
            builder->parencount--;
            if (builder->stack.len == exp_stacklen) {

                break;
            }
        }
        else if (t->type == JIM_TT_SUBEXPR_COMMA) {
            if (!(flags & EXPR_FUNC_ARGS)) {
                if (builder->stack.len == exp_stacklen) {

                    builder->token--;
                    builder->level--;
                    return JIM_OK;
                }
                Jim_SetResultFormatted(interp, "unexpected comma in expression: \"%#s\"", builder->exprObjPtr);
                return JIM_ERR;
            }
            else {

                if (builder->stack.len > exp_stacklen) {
                    Jim_SetResultFormatted(interp, "too many arguments to math function");
                    return JIM_ERR;
                }
            }

        }
        else if (t->type == JIM_EXPROP_COLON) {
            if (!(flags & EXPR_TERNARY)) {
                if (builder->level != 1) {

                    builder->token--;
                    builder->level--;
                    return JIM_OK;
                }
                Jim_SetResultFormatted(interp, ": without ? in expression: \"%#s\"", builder->exprObjPtr);
                return JIM_ERR;
            }
            if (builder->stack.len == exp_stacklen) {

                builder->token--;
                builder->level--;
                return JIM_OK;
            }

        }
        else if (TOKEN_IS_EXPR_OP(t->type)) {
            const struct Jim_ExprOperator *op;


            if (TOKEN_IS_EXPR_OP(prevtt) || TOKEN_IS_EXPR_START(prevtt)) {
                if (t->type == JIM_EXPROP_SUB) {
                    t->type = JIM_EXPROP_UNARYMINUS;
                }
                else if (t->type == JIM_EXPROP_ADD) {
                    t->type = JIM_EXPROP_UNARYPLUS;
                }
            }

            op = JimExprOperatorInfoByOpcode(t->type);

            if (op->precedence < precedence || (!(op->attr & OP_RIGHT_ASSOC) && op->precedence == precedence)) {

                builder->token--;
                break;
            }

            if (op->attr & OP_FUNC) {
                if (builder->token->type != JIM_TT_SUBEXPR_START) {
                    Jim_SetResultString(interp, "missing arguments for math function", -1);
                    return JIM_ERR;
                }
                builder->token++;
                if (op->arity == 0) {
                    if (builder->token->type != JIM_TT_SUBEXPR_END) {
                        Jim_SetResultString(interp, "too many arguments for math function", -1);
                        return JIM_ERR;
                    }
                    builder->token++;
                    goto noargs;
                }
                builder->parencount++;


                rc = ExprTreeBuildTree(interp, builder, 0, EXPR_FUNC_ARGS | EXPR_UNTIL_CLOSE, op->arity);
            }
            else if (t->type == JIM_EXPROP_TERNARY) {

                rc = ExprTreeBuildTree(interp, builder, op->precedence, EXPR_TERNARY, 2);
            }
            else {
                rc = ExprTreeBuildTree(interp, builder, op->precedence, 0, 1);
            }

            if (rc != JIM_OK) {
                return rc;
            }

noargs:
            node = builder->next++;
            node->type = t->type;

            if (op->arity >= 3) {
                node->ternary = Jim_StackPop(&builder->stack);
                if (node->ternary == NULL) {
                    goto missingoperand;
                }
            }
            if (op->arity >= 2) {
                node->right = Jim_StackPop(&builder->stack);
                if (node->right == NULL) {
                    goto missingoperand;
                }
            }
            if (op->arity >= 1) {
                node->left = Jim_StackPop(&builder->stack);
                if (node->left == NULL) {
missingoperand:
                    Jim_SetResultFormatted(interp, "missing operand to %s in expression: \"%#s\"", op->name, builder->exprObjPtr);
                    builder->next--;
                    return JIM_ERR;

                }
            }


            Jim_StackPush(&builder->stack, node);
        }
        else {
            Jim_Obj *objPtr = NULL;




            if (!TOKEN_IS_EXPR_START(prevtt) && !TOKEN_IS_EXPR_OP(prevtt)) {
                Jim_SetResultFormatted(interp, "missing operator in expression: \"%#s\"", builder->exprObjPtr);
                return JIM_ERR;
            }


            if (t->type == JIM_TT_EXPR_INT || t->type == JIM_TT_EXPR_DOUBLE) {
                char *endptr;
                if (t->type == JIM_TT_EXPR_INT) {
                    objPtr = Jim_NewIntObj(interp, jim_strtoull(t->token, &endptr));
                }
                else {
                    objPtr = Jim_NewDoubleObj(interp, strtod(t->token, &endptr));
                }
                if (endptr != t->token + t->len) {

                    Jim_FreeNewObj(interp, objPtr);
                    objPtr = NULL;
                }
            }

            if (!objPtr) {

                objPtr = Jim_NewStringObj(interp, t->token, t->len);
                if (t->type == JIM_TT_CMD) {

                    JimSetSourceInfo(interp, objPtr, builder->fileNameObj, t->line);
                }
            }


            node = builder->next++;
            node->objPtr = objPtr;
            Jim_IncrRefCount(node->objPtr);
            node->type = t->type;
            Jim_StackPush(&builder->stack, node);
        }
    }

    if (builder->stack.len == exp_stacklen) {
        builder->level--;
        return JIM_OK;
    }

    if ((flags & EXPR_FUNC_ARGS)) {
        Jim_SetResultFormatted(interp, "too %s arguments for math function", (builder->stack.len < exp_stacklen) ? "few" : "many");
    }
    else {
        if (builder->stack.len < exp_stacklen) {
            if (builder->level == 0) {
                Jim_SetResultFormatted(interp, "empty expression");
            }
            else {
                Jim_SetResultFormatted(interp, "syntax error in expression \"%#s\": premature end of expression", builder->exprObjPtr);
            }
        }
        else {
            Jim_SetResultFormatted(interp, "extra terms after expression");
        }
    }

    return JIM_ERR;
}

static struct ExprTree *ExprTreeCreateTree(Jim_Interp *interp, const ParseTokenList *tokenlist, Jim_Obj *exprObjPtr, Jim_Obj *fileNameObj)
{
    struct ExprTree *expr;
    struct ExprBuilder builder;
    int rc;
    struct JimExprNode *top = NULL;

    builder.parencount = 0;
    builder.level = 0;
    builder.token = builder.first_token = tokenlist->list;
    builder.exprObjPtr = exprObjPtr;
    builder.fileNameObj = fileNameObj;

    builder.nodes = malloc(sizeof(struct JimExprNode) * (tokenlist->count - 1));
    memset(builder.nodes, 0, sizeof(struct JimExprNode) * (tokenlist->count - 1));
    builder.next = builder.nodes;
    Jim_InitStack(&builder.stack);

    rc = ExprTreeBuildTree(interp, &builder, 0, 0, 1);

    if (rc == JIM_OK) {
        top = Jim_StackPop(&builder.stack);

        if (builder.parencount) {
            Jim_SetResultString(interp, "missing close parenthesis", -1);
            rc = JIM_ERR;
        }
    }


    Jim_FreeStack(&builder.stack);

    if (rc != JIM_OK) {
        ExprTreeFreeNodes(interp, builder.nodes, builder.next - builder.nodes);
        return NULL;
    }

    expr = Jim_Alloc(sizeof(*expr));
    expr->inUse = 1;
    expr->len = 0;

    Jim_InitStack(&stack);

    for (i = 0; i < tokenlist->count; i++) {
        ParseToken *t = &tokenlist->list[i];
        const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(t->type);

        if (op->lazy == LAZY_OP) {
            count += 2;
            
            if (t->type == JIM_EXPROP_TERNARY) {
                have_ternary = 1;
            }
        }
    }

    expr->token = Jim_Alloc(sizeof(ScriptToken) * count);

    for (i = 0; i < tokenlist->count && ok; i++) {
        ParseToken *t = &tokenlist->list[i];

        
        struct ScriptToken *token = &expr->token[expr->len];

        if (t->type == JIM_TT_EOL) {
            break;
        }

        switch (t->type) {
            case JIM_TT_STR:
            case JIM_TT_ESC:
            case JIM_TT_VAR:
            case JIM_TT_DICTSUGAR:
            case JIM_TT_EXPRSUGAR:
            case JIM_TT_CMD:
                token->type = t->type;
strexpr:
                token->objPtr = Jim_NewStringObj(interp, t->token, t->len);
                if (t->type == JIM_TT_CMD) {
                    
                    JimSetSourceInfo(interp, token->objPtr, fileNameObj, t->line);
                }
                expr->len++;
    expr->expr = top;
                break;

    expr->nodes = builder.nodes;
            case JIM_TT_EXPR_INT:
            case JIM_TT_EXPR_DOUBLE:
                {
                    char *endptr;
                    if (t->type == JIM_TT_EXPR_INT) {
                        token->objPtr = Jim_NewIntObj(interp, jim_strtoull(t->token, &endptr));
                    }
                    else {
                        token->objPtr = Jim_NewDoubleObj(interp, strtod(t->token, &endptr));
                    }
                    if (endptr != t->token + t->len) {
                        
                        Jim_FreeNewObj(interp, token->objPtr);
                        token->type = JIM_TT_STR;
                        goto strexpr;
                    }
                    token->type = t->type;
                    expr->len++;
                }
    expr->len = builder.next - builder.nodes;

                break;

    assert(expr->len <= tokenlist->count - 1);
            case JIM_TT_SUBEXPR_START:
                Jim_StackPush(&stack, t);
                prevtt = JIM_TT_NONE;
                continue;

            case JIM_TT_SUBEXPR_COMMA:
                
                continue;

            case JIM_TT_SUBEXPR_END:
                ok = 0;
                while (Jim_StackLen(&stack)) {
                    ParseToken *tt = Jim_StackPop(&stack);

                    if (tt->type == JIM_TT_SUBEXPR_START) {
                        ok = 1;
                        break;
                    }

                    if (ExprAddOperator(interp, expr, tt) != JIM_OK) {
                        goto err;
                    }
                }
                if (!ok) {
                    Jim_SetResultString(interp, "Unexpected close parenthesis", -1);
                    goto err;
                }
                break;


            default:{
                    
                    const struct Jim_ExprOperator *op;
                    ParseToken *tt;

                    
                    if (prevtt == JIM_TT_NONE || prevtt >= JIM_TT_EXPR_OP) {
                        if (t->type == JIM_EXPROP_SUB) {
                            t->type = JIM_EXPROP_UNARYMINUS;
                        }
                        else if (t->type == JIM_EXPROP_ADD) {
                            t->type = JIM_EXPROP_UNARYPLUS;
                        }
                    }

                    op = JimExprOperatorInfoByOpcode(t->type);

                    
                    while ((tt = Jim_StackPeek(&stack)) != NULL) {
                        const struct Jim_ExprOperator *tt_op =
                            JimExprOperatorInfoByOpcode(tt->type);

                        

                        if (op->arity != 1 && tt_op->precedence >= op->precedence) {
                            if (ExprAddOperator(interp, expr, tt) != JIM_OK) {
                                ok = 0;
                                goto err;
                            }
                            Jim_StackPop(&stack);
                        }
                        else {
                            break;
                        }
                    }
                    Jim_StackPush(&stack, t);
                    break;
                }
        }
        prevtt = t->type;
    }

    
    while (Jim_StackLen(&stack)) {
        ParseToken *tt = Jim_StackPop(&stack);

        if (tt->type == JIM_TT_SUBEXPR_START) {
            ok = 0;
            Jim_SetResultString(interp, "Missing close parenthesis", -1);
            goto err;
        }
        if (ExprAddOperator(interp, expr, tt) != JIM_OK) {
            ok = 0;
            goto err;
        }
    }

    if (have_ternary) {
        ExprTernaryReorderExpression(interp, expr);
    }

  err:
    
    Jim_FreeStack(&stack);

    for (i = 0; i < expr->len; i++) {
        Jim_IncrRefCount(expr->token[i].objPtr);
    }

    if (!ok) {
        ExprFreeByteCode(interp, expr);
        return NULL;
    }

    return expr;
}


static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
{
    int exprTextLen;
    const char *exprText;
    struct JimParserCtx parser;
    struct ExprByteCode *expr;
    struct ExprTree *expr;
    ParseTokenList tokenlist;
    int line;
    Jim_Obj *fileNameObj;
    int rc = JIM_ERR;

    

    if (objPtr->typePtr == &sourceObjType) {
        fileNameObj = objPtr->internalRep.sourceValue.fileNameObj;
        line = objPtr->internalRep.sourceValue.lineNumber;
    }
    else {
        fileNameObj = interp->emptyObj;
        line = 1;
    }
    Jim_IncrRefCount(fileNameObj);

    exprText = Jim_GetString(objPtr, &exprTextLen);

    

    ScriptTokenListInit(&tokenlist);

    JimParserInit(&parser, exprText, exprTextLen, line);
    while (!parser.eof) {
        if (JimParseExpression(&parser) != JIM_OK) {
            ScriptTokenListFree(&tokenlist);
          invalidexpr:
            Jim_SetResultFormatted(interp, "syntax error in expression: \"%#s\"", objPtr);
            expr = NULL;
            goto err;
        }

        ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt,
            parser.tline);
14034
14035
14036
14037
14038
14039
14040
14041
14042


14043
14044

14045
14046
14047
14048
14049
14050
14051
14052
14053
14054
14055

14056
14057
14058

14059
14060
14061
14062
14063
14064
14065
14066
14067
14068
14069
14070
14071
14072
14073

14074
14075
14076
14077
14078
14079
14080
14081

14082
14083
14084
14085
14086
14087
14088

14089
14090
14091
14092

14093
14094
14095
14096
14097
14098
14099






14100
14101
14102
14103
14104
14105
14106
14107
14108
14109
14110
14111









































































14112
14113
14114
14115
14116
14117

14118
14119
14120
14121
14122
14123
14124
14125
14126
14127

14128
14129

14130
14131
14132
14133
14134
14135
14136
14137


14138
14139
14140

14141
14142
14143
14144
14145
14146
14147
14148

14149
14150

14151
14152
14153
14154
14155

14156
14157
14158
14159
14160
14161
14162
13916
13917
13918
13919
13920
13921
13922


13923
13924
13925

13926
13927
13928
13929
13930
13931
13932
13933




13934



13935



13936
13937






13938
13939
13940

13941
13942
13943
13944
13945
13946
13947
13948

13949
13950
13951
13952
13953
13954
13955

13956
13957
13958
13959

13960
13961






13962
13963
13964
13965
13966
13967
13968
13969
13970
13971
13972







13973
13974
13975
13976
13977
13978
13979
13980
13981
13982
13983
13984
13985
13986
13987
13988
13989
13990
13991
13992
13993
13994
13995
13996
13997
13998
13999
14000
14001
14002
14003
14004
14005
14006
14007
14008
14009
14010
14011
14012
14013
14014
14015
14016
14017
14018
14019
14020
14021
14022
14023
14024
14025
14026
14027
14028
14029
14030
14031
14032
14033
14034
14035
14036
14037
14038
14039
14040
14041
14042
14043
14044
14045
14046

14047
14048
14049

14050
14051
14052
14053
14054
14055
14056
14057
14058
14059

14060
14061

14062

14063
14064
14065
14066
14067


14068
14069
14070
14071

14072

14073
14074
14075
14076
14077
14078

14079
14080

14081
14082
14083
14084
14085

14086
14087
14088
14089
14090
14091
14092
14093







-
-
+
+

-
+







-
-
-
-
+
-
-
-
+
-
-
-


-
-
-
-
-
-



-
+







-
+






-
+



-
+

-
-
-
-
-
-
+
+
+
+
+
+





-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-



-
+









-
+

-
+
-





-
-
+
+


-
+
-






-
+

-
+




-
+








    if (JimParseCheckMissing(interp, parser.missing.ch) == JIM_ERR) {
        ScriptTokenListFree(&tokenlist);
        Jim_DecrRefCount(interp, fileNameObj);
        return JIM_ERR;
    }

    
    expr = ExprCreateByteCode(interp, &tokenlist, fileNameObj);

    expr = ExprTreeCreateTree(interp, &tokenlist, objPtr, fileNameObj);

    

    ScriptTokenListFree(&tokenlist);

    if (!expr) {
        goto err;
    }

#ifdef DEBUG_SHOW_EXPR
    {
        int i;

        printf("==== Expr ====\n");
    printf("==== Expr ====\n");
        for (i = 0; i < expr->len; i++) {
            ScriptToken *t = &expr->token[i];

    JimShowExprNode(expr->expr, 0);
            printf("[%2d] %s '%s'\n", i, jim_tt_name(t->type), Jim_String(t->objPtr));
        }
    }
#endif

    
    if (ExprCheckCorrectness(expr) != JIM_OK) {
        ExprFreeByteCode(interp, expr);
        goto invalidexpr;
    }

    rc = JIM_OK;

  err:
    

    Jim_DecrRefCount(interp, fileNameObj);
    Jim_FreeIntRep(interp, objPtr);
    Jim_SetIntRepPtr(objPtr, expr);
    objPtr->typePtr = &exprObjType;
    return rc;
}

static ExprByteCode *JimGetExpression(Jim_Interp *interp, Jim_Obj *objPtr)
static struct ExprTree *JimGetExpression(Jim_Interp *interp, Jim_Obj *objPtr)
{
    if (objPtr->typePtr != &exprObjType) {
        if (SetExprFromAny(interp, objPtr) != JIM_OK) {
            return NULL;
        }
    }
    return (ExprByteCode *) Jim_GetIntRepPtr(objPtr);
    return (struct ExprTree *) Jim_GetIntRepPtr(objPtr);
}

#ifdef JIM_OPTIMIZATION
static Jim_Obj *JimExprIntValOrVar(Jim_Interp *interp, const ScriptToken *token)
static Jim_Obj *JimExprIntValOrVar(Jim_Interp *interp, struct JimExprNode *node)
{
    if (token->type == JIM_TT_EXPR_INT)
        return token->objPtr;
    else if (token->type == JIM_TT_VAR)
        return Jim_GetVariable(interp, token->objPtr, JIM_NONE);
    else if (token->type == JIM_TT_DICTSUGAR)
        return JimExpandDictSugar(interp, token->objPtr);
    if (node->type == JIM_TT_EXPR_INT)
        return node->objPtr;
    else if (node->type == JIM_TT_VAR)
        return Jim_GetVariable(interp, node->objPtr, JIM_NONE);
    else if (node->type == JIM_TT_DICTSUGAR)
        return JimExpandDictSugar(interp, node->objPtr);
    else
        return NULL;
}
#endif

#define JIM_EE_STATICSTACK_LEN 10

int Jim_EvalExpression(Jim_Interp *interp, Jim_Obj *exprObjPtr, Jim_Obj **exprResultPtrPtr)
{
    ExprByteCode *expr;
    Jim_Obj *staticStack[JIM_EE_STATICSTACK_LEN];
    int i;

static int JimExprEvalTermNode(Jim_Interp *interp, struct JimExprNode *node)
{
    if (TOKEN_IS_EXPR_OP(node->type)) {
        const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(node->type);
        return op->funcop(interp, node);
    }
    else {
        Jim_Obj *objPtr;


        switch (node->type) {
            case JIM_TT_EXPR_INT:
            case JIM_TT_EXPR_DOUBLE:
            case JIM_TT_EXPR_BOOLEAN:
            case JIM_TT_STR:
                Jim_SetResult(interp, node->objPtr);
                return JIM_OK;

            case JIM_TT_VAR:
                objPtr = Jim_GetVariable(interp, node->objPtr, JIM_ERRMSG);
                if (objPtr) {
                    Jim_SetResult(interp, objPtr);
                    return JIM_OK;
                }
                return JIM_ERR;

            case JIM_TT_DICTSUGAR:
                objPtr = JimExpandDictSugar(interp, node->objPtr);
                if (objPtr) {
                    Jim_SetResult(interp, objPtr);
                    return JIM_OK;
                }
                return JIM_ERR;

            case JIM_TT_ESC:
                if (Jim_SubstObj(interp, node->objPtr, &objPtr, JIM_NONE) == JIM_OK) {
                    Jim_SetResult(interp, objPtr);
                    return JIM_OK;
                }
                return JIM_ERR;

            case JIM_TT_CMD:
                return Jim_EvalObj(interp, node->objPtr);

            default:

                return JIM_ERR;
        }
    }
}

static int JimExprGetTerm(Jim_Interp *interp, struct JimExprNode *node, Jim_Obj **objPtrPtr)
{
    int rc = JimExprEvalTermNode(interp, node);
    if (rc == JIM_OK) {
        *objPtrPtr = Jim_GetResult(interp);
        Jim_IncrRefCount(*objPtrPtr);
    }
    return rc;
}

static int JimExprGetTermBoolean(Jim_Interp *interp, struct JimExprNode *node)
{
    if (JimExprEvalTermNode(interp, node) == JIM_OK) {
        return ExprBool(interp, Jim_GetResult(interp));
    }
    return -1;
}

int Jim_EvalExpression(Jim_Interp *interp, Jim_Obj *exprObjPtr)
{
    struct ExprTree *expr;
    int retcode = JIM_OK;
    struct JimExprState e;

    expr = JimGetExpression(interp, exprObjPtr);
    if (!expr) {
        return JIM_ERR;         
        return JIM_ERR;
    }

#ifdef JIM_OPTIMIZATION
    {
        Jim_Obj *objPtr;


        switch (expr->len) {
            case 1:
                objPtr = JimExprIntValOrVar(interp, &expr->token[0]);
                objPtr = JimExprIntValOrVar(interp, expr->expr);
                if (objPtr) {
                    Jim_IncrRefCount(objPtr);
                    Jim_SetResult(interp, objPtr);
                    *exprResultPtrPtr = objPtr;
                    return JIM_OK;
                }
                break;

            case 2:
                if (expr->token[1].type == JIM_EXPROP_NOT) {
                    objPtr = JimExprIntValOrVar(interp, &expr->token[0]);
                if (expr->expr->type == JIM_EXPROP_NOT) {
                    objPtr = JimExprIntValOrVar(interp, expr->expr->left);

                    if (objPtr && JimIsWide(objPtr)) {
                        *exprResultPtrPtr = JimWideValue(objPtr) ? interp->falseObj : interp->trueObj;
                        Jim_SetResult(interp, JimWideValue(objPtr) ? interp->falseObj : interp->trueObj);
                        Jim_IncrRefCount(*exprResultPtrPtr);
                        return JIM_OK;
                    }
                }
                break;

            case 3:
                objPtr = JimExprIntValOrVar(interp, &expr->token[0]);
                objPtr = JimExprIntValOrVar(interp, expr->expr->left);
                if (objPtr && JimIsWide(objPtr)) {
                    Jim_Obj *objPtr2 = JimExprIntValOrVar(interp, &expr->token[1]);
                    Jim_Obj *objPtr2 = JimExprIntValOrVar(interp, expr->expr->right);
                    if (objPtr2 && JimIsWide(objPtr2)) {
                        jim_wide wideValueA = JimWideValue(objPtr);
                        jim_wide wideValueB = JimWideValue(objPtr2);
                        int cmpRes;
                        switch (expr->token[2].type) {
                        switch (expr->expr->type) {
                            case JIM_EXPROP_LT:
                                cmpRes = wideValueA < wideValueB;
                                break;
                            case JIM_EXPROP_LTE:
                                cmpRes = wideValueA <= wideValueB;
                                break;
                            case JIM_EXPROP_GT:
14170
14171
14172
14173
14174
14175
14176
14177

14178
14179
14180
14181
14182
14183
14184
14185
14186
14187
14188
14189
14190

14191
14192
14193
14194
14195
14196
14197
14198
14199
14200
14201
14202
14203
14204
14205
14206
14207
14208
14209
14210
14211
14212
14213
14214
14215
14216
14217


14218
14219
14220
14221
14222
14223
14224
14225
14226
14227
14228
14229
14230
14231
14232
14233
14234
14235
14236
14237
14238
14239
14240
14241
14242
14243
14244
14245
14246
14247
14248
14249
14250
14251
14252
14253
14254
14255
14256
14257
14258
14259
14260
14261
14262
14263
14264
14265
14266
14267
14268
14269
14270
14271
14272
14273
14274
14275

14276
14277
14278
14279
14280
14281

14282
14283

14284
14285
14286
14287
14288
14289





14290
14291
14292
14293
14294







14295
14296

14297
14298

14299
14300
14301
14302
14303
14304
14305
14306
14307
14308
14309
14310
14311






14312
14313
14314
14315
14316
14317
14318
14319
14320
14321
14322
14323
14324








14325
14326
14327
14328
14329
14330
14331
14101
14102
14103
14104
14105
14106
14107

14108

14109
14110
14111
14112
14113
14114
14115
14116
14117
14118
14119

14120



























14121
14122







































14123
14124











14125
14126
14127
14128
14129

14130



14131


14132


14133






14134
14135
14136
14137
14138





14139
14140
14141
14142
14143
14144
14145


14146


14147
14148
14149
14150
14151
14152
14153
14154






14155
14156
14157
14158
14159
14160
14161
14162
14163
14164
14165








14166
14167
14168
14169
14170
14171
14172
14173
14174
14175
14176
14177
14178
14179
14180







-
+
-











-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-


-
-
-
-
-
-
-
-
-
-
-





-
+
-
-
-

-
-
+
-
-
+
-
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
-
-
+
-
-
+







-
-
-
-
-
-
+
+
+
+
+
+





-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+







                                break;
                            case JIM_EXPROP_NUMNE:
                                cmpRes = wideValueA != wideValueB;
                                break;
                            default:
                                goto noopt;
                        }
                        *exprResultPtrPtr = cmpRes ? interp->trueObj : interp->falseObj;
                        Jim_SetResult(interp, cmpRes ? interp->trueObj : interp->falseObj);
                        Jim_IncrRefCount(*exprResultPtrPtr);
                        return JIM_OK;
                    }
                }
                break;
        }
    }
noopt:
#endif

    expr->inUse++;

    


    if (expr->len > JIM_EE_STATICSTACK_LEN)
        e.stack = Jim_Alloc(sizeof(Jim_Obj *) * expr->len);
    else
        e.stack = staticStack;

    e.stacklen = 0;

    
    for (i = 0; i < expr->len && retcode == JIM_OK; i++) {
        Jim_Obj *objPtr;

        switch (expr->token[i].type) {
            case JIM_TT_EXPR_INT:
            case JIM_TT_EXPR_DOUBLE:
            case JIM_TT_STR:
                ExprPush(&e, expr->token[i].objPtr);
                break;

            case JIM_TT_VAR:
                objPtr = Jim_GetVariable(interp, expr->token[i].objPtr, JIM_ERRMSG);
                if (objPtr) {
                    ExprPush(&e, objPtr);
                }
                else {
                    retcode = JIM_ERR;
                }
    retcode = JimExprEvalTermNode(interp, expr->expr);

                break;

            case JIM_TT_DICTSUGAR:
                objPtr = JimExpandDictSugar(interp, expr->token[i].objPtr);
                if (objPtr) {
                    ExprPush(&e, objPtr);
                }
                else {
                    retcode = JIM_ERR;
                }
                break;

            case JIM_TT_ESC:
                retcode = Jim_SubstObj(interp, expr->token[i].objPtr, &objPtr, JIM_NONE);
                if (retcode == JIM_OK) {
                    ExprPush(&e, objPtr);
                }
                break;

            case JIM_TT_CMD:
                retcode = Jim_EvalObj(interp, expr->token[i].objPtr);
                if (retcode == JIM_OK) {
                    ExprPush(&e, Jim_GetResult(interp));
                }
                break;

            default:{
                    
                    e.skip = 0;
                    e.opcode = expr->token[i].type;

                    retcode = JimExprOperatorInfoByOpcode(e.opcode)->funcop(interp, &e);
                    
                    i += e.skip;
                    continue;
                }
        }
    }

    expr->inUse--;

    if (retcode == JIM_OK) {
        *exprResultPtrPtr = ExprPop(&e);
    }
    else {
        for (i = 0; i < e.stacklen; i++) {
            Jim_DecrRefCount(interp, e.stack[i]);
        }
    }
    if (e.stack != staticStack) {
        Jim_Free(e.stack);
    }
    return retcode;
}

int Jim_GetBoolFromExpr(Jim_Interp *interp, Jim_Obj *exprObjPtr, int *boolPtr)
{
    int retcode;
    int retcode = Jim_EvalExpression(interp, exprObjPtr);
    jim_wide wideValue;
    double doubleValue;
    Jim_Obj *exprResultPtr;

    retcode = Jim_EvalExpression(interp, exprObjPtr, &exprResultPtr);
    if (retcode != JIM_OK)
    if (retcode == JIM_OK) {
        return retcode;

        switch (ExprBool(interp, Jim_GetResult(interp))) {
    if (JimGetWideNoErr(interp, exprResultPtr, &wideValue) != JIM_OK) {
        if (Jim_GetDouble(interp, exprResultPtr, &doubleValue) != JIM_OK) {
            Jim_DecrRefCount(interp, exprResultPtr);
            return JIM_ERR;
        }
        else {
            case 0:
                *boolPtr = 0;
                break;

            case 1:
            Jim_DecrRefCount(interp, exprResultPtr);
            *boolPtr = doubleValue != 0;
            return JIM_OK;
        }
    }
                *boolPtr = 1;
                break;

            case -1:
                retcode = JIM_ERR;
                break;
        }
    *boolPtr = wideValue != 0;

    }
    Jim_DecrRefCount(interp, exprResultPtr);
    return JIM_OK;
    return retcode;
}




typedef struct ScanFmtPartDescr
{
    char *arg;                  
    char *prefix;               
    size_t width;               
    int pos;                    
    char type;                  
    char modifier;              
    const char *arg;
    const char *prefix;
    size_t width;
    int pos;
    char type;
    char modifier;
} ScanFmtPartDescr;


typedef struct ScanFmtStringObj
{
    jim_wide size;              
    char *stringRep;            
    size_t count;               
    size_t convCount;           
    size_t maxPos;              
    const char *error;          
    char *scratch;              
    ScanFmtPartDescr descr[1];  
    jim_wide size;
    char *stringRep;
    size_t count;
    size_t convCount;
    size_t maxPos;
    const char *error;
    char *scratch;
    ScanFmtPartDescr descr[1];
} ScanFmtStringObj;


static void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
static void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
static void UpdateStringOfScanFmt(Jim_Obj *objPtr);

14362
14363
14364
14365
14366
14367
14368
14369
14370


14371
14372
14373
14374
14375

14376
14377
14378
14379
14380
14381
14382
14383
14384
14385
14386








14387
14388
14389
14390
14391
14392
14393
14394
14395
14396
14397
14398
14399
14400
14401
14402
14403


14404
14405
14406
14407
14408
14409
14410
14411
14412
14413
14414
14415
14416
14417

14418
14419

14420
14421
14422

14423
14424

14425
14426
14427
14428
14429


14430
14431
14432

14433
14434
14435
14436
14437
14438
14439

14440
14441
14442
14443
14444
14445

14446
14447
14448
14449
14450
14451
14452
14453
14454




14455


14456
14457
14458
14459
14460
14461
14462
14463
14464

14465
14466
14467
14468

14469
14470
14471

14472
14473
14474
14475
14476
14477
14478
14479
14480
14481
14482
14483
14484
14485
14486
14487
14488
14489
14490

14491
14492
14493
14494
14495
14496
14497
14498
14499
14500
14501
14502
14503
14504
14505
14506


14507





14508
14509
14510
14511
14512
14513
14514
14211
14212
14213
14214
14215
14216
14217


14218
14219
14220
14221
14222
14223

14224
14225
14226
14227








14228
14229
14230
14231
14232
14233
14234
14235
14236
14237
14238
14239
14240
14241
14242
14243
14244
14245
14246
14247
14248
14249
14250


14251
14252
14253
14254
14255
14256
14257
14258
14259
14260
14261
14262
14263
14264
14265

14266
14267

14268
14269
14270

14271
14272

14273
14274
14275
14276


14277
14278
14279
14280

14281
14282
14283
14284
14285
14286
14287

14288
14289
14290
14291
14292
14293

14294
14295
14296
14297
14298
14299
14300
14301
14302
14303
14304
14305
14306
14307

14308
14309
14310
14311
14312
14313
14314
14315
14316
14317

14318
14319
14320
14321

14322
14323
14324

14325
14326
14327
14328
14329
14330
14331
14332
14333
14334
14335
14336
14337
14338
14339
14340
14341
14342
14343

14344
14345
14346
14347
14348
14349
14350
14351
14352
14353
14354
14355
14356
14357
14358


14359
14360
14361
14362
14363
14364
14365
14366
14367
14368
14369
14370
14371
14372
14373







-
-
+
+




-
+



-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+















-
-
+
+













-
+

-
+


-
+

-
+



-
-
+
+


-
+






-
+





-
+









+
+
+
+
-
+
+








-
+



-
+


-
+


















-
+














-
-
+
+

+
+
+
+
+









static int SetScanFmtFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
{
    ScanFmtStringObj *fmtObj;
    char *buffer;
    int maxCount, i, approxSize, lastPos = -1;
    const char *fmt = objPtr->bytes;
    int maxFmtLen = objPtr->length;
    const char *fmt = Jim_String(objPtr);
    int maxFmtLen = Jim_Length(objPtr);
    const char *fmtEnd = fmt + maxFmtLen;
    int curr;

    Jim_FreeIntRep(interp, objPtr);
    

    for (i = 0, maxCount = 0; i < maxFmtLen; ++i)
        if (fmt[i] == '%')
            ++maxCount;
    
    approxSize = sizeof(ScanFmtStringObj)       
        +(maxCount + 1) * sizeof(ScanFmtPartDescr)      
        +maxFmtLen * sizeof(char) + 3 + 1       
        + maxFmtLen * sizeof(char) + 1  
        + maxFmtLen * sizeof(char)      
        +(maxCount + 1) * sizeof(char)  
        +1;                     

    approxSize = sizeof(ScanFmtStringObj)
        +(maxCount + 1) * sizeof(ScanFmtPartDescr)
        +maxFmtLen * sizeof(char) + 3 + 1
        + maxFmtLen * sizeof(char) + 1
        + maxFmtLen * sizeof(char)
        +(maxCount + 1) * sizeof(char)
        +1;
    fmtObj = (ScanFmtStringObj *) Jim_Alloc(approxSize);
    memset(fmtObj, 0, approxSize);
    fmtObj->size = approxSize;
    fmtObj->maxPos = 0;
    fmtObj->scratch = (char *)&fmtObj->descr[maxCount + 1];
    fmtObj->stringRep = fmtObj->scratch + maxFmtLen + 3 + 1;
    memcpy(fmtObj->stringRep, fmt, maxFmtLen);
    buffer = fmtObj->stringRep + maxFmtLen + 1;
    objPtr->internalRep.ptr = fmtObj;
    objPtr->typePtr = &scanFmtStringObjType;
    for (i = 0, curr = 0; fmt < fmtEnd; ++fmt) {
        int width = 0, skip;
        ScanFmtPartDescr *descr = &fmtObj->descr[curr];

        fmtObj->count++;
        descr->width = 0;       
        
        descr->width = 0;

        if (*fmt != '%' || fmt[1] == '%') {
            descr->type = 0;
            descr->prefix = &buffer[i];
            for (; fmt < fmtEnd; ++fmt) {
                if (*fmt == '%') {
                    if (fmt[1] != '%')
                        break;
                    ++fmt;
                }
                buffer[i++] = *fmt;
            }
            buffer[i++] = 0;
        }
        

        ++fmt;
        

        if (fmt >= fmtEnd)
            goto done;
        descr->pos = 0;         
        descr->pos = 0;
        if (*fmt == '*') {
            descr->pos = -1;    
            descr->pos = -1;
            ++fmt;
        }
        else
            fmtObj->convCount++;        
        
            fmtObj->convCount++;

        if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
            fmt += skip;
            

            if (descr->pos != -1 && *fmt == '$') {
                int prev;

                ++fmt;
                descr->pos = width;
                width = 0;
                

                if ((lastPos == 0 && descr->pos > 0)
                    || (lastPos > 0 && descr->pos == 0)) {
                    fmtObj->error = "cannot mix \"%\" and \"%n$\" conversion specifiers";
                    return JIM_ERR;
                }
                

                for (prev = 0; prev < curr; ++prev) {
                    if (fmtObj->descr[prev].pos == -1)
                        continue;
                    if (fmtObj->descr[prev].pos == descr->pos) {
                        fmtObj->error =
                            "variable is assigned by multiple \"%n$\" conversion specifiers";
                        return JIM_ERR;
                    }
                }
                if (descr->pos < 0) {
                    fmtObj->error =
                        "\"%n$\" conversion specifier is negative";
                    return JIM_ERR;
                
                }

                if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
                    descr->width = width;
                    fmt += skip;
                }
                if (descr->pos > 0 && (size_t) descr->pos > fmtObj->maxPos)
                    fmtObj->maxPos = descr->pos;
            }
            else {
                

                descr->width = width;
            }
        }
        

        if (lastPos == -1)
            lastPos = descr->pos;
        

        if (*fmt == '[') {
            int swapped = 1, beg = i, end, j;

            descr->type = '[';
            descr->arg = &buffer[i];
            ++fmt;
            if (*fmt == '^')
                buffer[i++] = *fmt++;
            if (*fmt == ']')
                buffer[i++] = *fmt++;
            while (*fmt && *fmt != ']')
                buffer[i++] = *fmt++;
            if (*fmt != ']') {
                fmtObj->error = "unmatched [ in format string";
                return JIM_ERR;
            }
            end = i;
            buffer[i++] = 0;
            

            while (swapped) {
                swapped = 0;
                for (j = beg + 1; j < end - 1; ++j) {
                    if (buffer[j] == '-' && buffer[j - 1] > buffer[j + 1]) {
                        char tmp = buffer[j - 1];

                        buffer[j - 1] = buffer[j + 1];
                        buffer[j + 1] = tmp;
                        swapped = 1;
                    }
                }
            }
        }
        else {
            
            if (strchr("hlL", *fmt) != 0)

            if (fmt < fmtEnd && strchr("hlL", *fmt))
                descr->modifier = tolower((int)*fmt++);

            if (fmt >= fmtEnd) {
                fmtObj->error = "missing scan conversion character";
                return JIM_ERR;
            }

            descr->type = *fmt;
            if (strchr("efgcsndoxui", *fmt) == 0) {
                fmtObj->error = "bad scan conversion character";
                return JIM_ERR;
            }
            else if (*fmt == 'c' && descr->width != 0) {
14541
14542
14543
14544
14545
14546
14547
14548

14549
14550
14551
14552
14553
14554
14555
14400
14401
14402
14403
14404
14405
14406

14407
14408
14409
14410
14411
14412
14413
14414







-
+







    char *p = buffer;

    while (*str) {
        int c;
        int n;

        if (!sdescr && isspace(UCHAR(*str)))
            break;              
            break;

        n = utf8_tounicode(str, &c);
        if (sdescr && !JimCharsetMatch(sdescr, c, JIM_CHARSET_SCAN))
            break;
        while (n--)
            *p++ = *str++;
    }
14564
14565
14566
14567
14568
14569
14570
14571

14572
14573
14574
14575

14576
14577
14578
14579
14580

14581
14582

14583
14584
14585

14586
14587
14588

14589
14590

14591
14592
14593
14594

14595
14596
14597

14598
14599

14600
14601
14602
14603

14604
14605
14606
14607
14608
14609
14610
14611
14612
14613

14614
14615
14616
14617
14618
14619
14620
14621
14622

14623
14624
14625
14626
14627
14628
14629
14630
14631

14632
14633
14634
14635
14636
14637

14638
14639
14640
14641
14642
14643
14644
14645
14646

14647
14648
14649

14650
14651
14652
14653
14654
14655
14656
14657
14658
14659
14660
14661
14662
14663
14664
14665
14666
14667
14668
14669
14670

14671
14672

14673
14674
14675
14676
14677
14678
14679
14423
14424
14425
14426
14427
14428
14429

14430
14431
14432
14433

14434
14435
14436
14437
14438

14439
14440

14441
14442
14443

14444
14445
14446

14447
14448

14449
14450
14451
14452

14453
14454
14455

14456
14457

14458
14459
14460
14461

14462
14463
14464
14465
14466
14467
14468
14469
14470
14471

14472
14473
14474
14475
14476
14477
14478
14479
14480

14481
14482
14483
14484
14485
14486
14487
14488
14489

14490
14491
14492
14493
14494
14495

14496
14497
14498
14499
14500
14501
14502
14503
14504

14505
14506
14507

14508
14509
14510
14511
14512
14513
14514
14515
14516
14517
14518
14519
14520
14521
14522
14523
14524
14525
14526
14527
14528

14529
14530

14531
14532
14533
14534
14535
14536
14537
14538







-
+



-
+




-
+

-
+


-
+


-
+

-
+



-
+


-
+

-
+



-
+









-
+








-
+








-
+





-
+








-
+


-
+




















-
+

-
+







    const char *tok;
    const ScanFmtPartDescr *descr = &fmtObj->descr[idx];
    size_t scanned = 0;
    size_t anchor = pos;
    int i;
    Jim_Obj *tmpObj = NULL;

    

    *valObjPtr = 0;
    if (descr->prefix) {
        for (i = 0; pos < strLen && descr->prefix[i]; ++i) {
            

            if (isspace(UCHAR(descr->prefix[i])))
                while (pos < strLen && isspace(UCHAR(str[pos])))
                    ++pos;
            else if (descr->prefix[i] != str[pos])
                break;          
                break;
            else
                ++pos;          
                ++pos;
        }
        if (pos >= strLen) {
            return -1;          
            return -1;
        }
        else if (descr->prefix[i] != 0)
            return 0;           
            return 0;
    }
    

    if (descr->type != 'c' && descr->type != '[' && descr->type != 'n')
        while (isspace(UCHAR(str[pos])))
            ++pos;
    

    scanned = pos - anchor;

    

    if (descr->type == 'n') {
        

        *valObjPtr = Jim_NewIntObj(interp, anchor + scanned);
    }
    else if (pos >= strLen) {
        

        return -1;
    }
    else if (descr->type == 'c') {
            int c;
            scanned += utf8_tounicode(&str[pos], &c);
            *valObjPtr = Jim_NewIntObj(interp, c);
            return scanned;
    }
    else {
        

        if (descr->width > 0) {
            size_t sLen = utf8_strlen(&str[pos], strLen - pos);
            size_t tLen = descr->width > sLen ? sLen : descr->width;

            tmpObj = Jim_NewStringObjUtf8(interp, str + pos, tLen);
            tok = tmpObj->bytes;
        }
        else {
            

            tok = &str[pos];
        }
        switch (descr->type) {
            case 'd':
            case 'o':
            case 'x':
            case 'u':
            case 'i':{
                    char *endp; 
                    char *endp;
                    jim_wide w;

                    int base = descr->type == 'o' ? 8
                        : descr->type == 'x' ? 16 : descr->type == 'i' ? 0 : 10;

                    

                    if (base == 0) {
                        w = jim_strtoull(tok, &endp);
                    }
                    else {
                        w = strtoull(tok, &endp, base);
                    }

                    if (endp != tok) {
                        

                        *valObjPtr = Jim_NewIntObj(interp, w);

                        

                        scanned += endp - tok;
                    }
                    else {
                        scanned = *tok ? 0 : -1;
                    }
                    break;
                }
            case 's':
            case '[':{
                    *valObjPtr = JimScanAString(interp, descr->arg, tok);
                    scanned += Jim_Length(*valObjPtr);
                    break;
                }
            case 'e':
            case 'f':
            case 'g':{
                    char *endp;
                    double value = strtod(tok, &endp);

                    if (endp != tok) {
                        

                        *valObjPtr = Jim_NewDoubleObj(interp, value);
                        

                        scanned += endp - tok;
                    }
                    else {
                        scanned = *tok ? 0 : -1;
                    }
                    break;
                }
14694
14695
14696
14697
14698
14699
14700
14701

14702
14703
14704
14705

14706
14707
14708
14709
14710
14711

14712
14713
14714

14715
14716
14717
14718
14719
14720
14721

14722
14723
14724
14725
14726

14727
14728
14729

14730
14731
14732

14733
14734
14735

14736
14737
14738

14739
14740
14741

14742
14743
14744
14745
14746

14747
14748
14749

14750
14751
14752
14753
14754
14755

14756
14757
14758
14759
14760
14761
14762
14553
14554
14555
14556
14557
14558
14559

14560
14561
14562
14563

14564
14565
14566
14567
14568
14569

14570
14571
14572

14573
14574
14575
14576
14577
14578
14579

14580
14581
14582
14583
14584

14585
14586
14587

14588
14589
14590

14591
14592
14593

14594
14595
14596

14597
14598
14599

14600
14601
14602
14603
14604

14605
14606
14607

14608
14609
14610
14611
14612
14613

14614
14615
14616
14617
14618
14619
14620
14621







-
+



-
+





-
+


-
+






-
+




-
+


-
+


-
+


-
+


-
+


-
+




-
+


-
+





-
+







    int strLen = Jim_Utf8Length(interp, strObjPtr);
    Jim_Obj *resultList = 0;
    Jim_Obj **resultVec = 0;
    int resultc;
    Jim_Obj *emptyStr = 0;
    ScanFmtStringObj *fmtObj;

    

    JimPanic((fmtObjPtr->typePtr != &scanFmtStringObjType, "Jim_ScanString() for non-scan format"));

    fmtObj = (ScanFmtStringObj *) fmtObjPtr->internalRep.ptr;
    

    if (fmtObj->error != 0) {
        if (flags & JIM_ERRMSG)
            Jim_SetResultString(interp, fmtObj->error, -1);
        return 0;
    }
    

    emptyStr = Jim_NewEmptyStringObj(interp);
    Jim_IncrRefCount(emptyStr);
    

    resultList = Jim_NewListObj(interp, NULL, 0);
    if (fmtObj->maxPos > 0) {
        for (i = 0; i < fmtObj->maxPos; ++i)
            Jim_ListAppendElement(interp, resultList, emptyStr);
        JimListGetElements(interp, resultList, &resultc, &resultVec);
    }
    

    for (i = 0, pos = 0; i < fmtObj->count; ++i) {
        ScanFmtPartDescr *descr = &(fmtObj->descr[i]);
        Jim_Obj *value = 0;

        

        if (descr->type == 0)
            continue;
        

        if (scanned > 0)
            scanned = ScanOneEntry(interp, str, pos, strLen, fmtObj, i, &value);
        

        if (scanned == -1 && i == 0)
            goto eof;
        

        pos += scanned;

        

        if (value == 0)
            value = Jim_NewEmptyStringObj(interp);
        

        if (descr->pos == -1) {
            Jim_FreeNewObj(interp, value);
        }
        else if (descr->pos == 0)
            

            Jim_ListAppendElement(interp, resultList, value);
        else if (resultVec[descr->pos - 1] == emptyStr) {
            

            Jim_DecrRefCount(interp, resultVec[descr->pos - 1]);
            Jim_IncrRefCount(value);
            resultVec[descr->pos - 1] = value;
        }
        else {
            

            Jim_FreeNewObj(interp, value);
            goto err;
        }
    }
    Jim_DecrRefCount(interp, emptyStr);
    return resultList;
  eof:
14790
14791
14792
14793
14794
14795
14796
14797

14798
14799
14800
14801

14802
14803
14804
14805
14806
14807
14808
14809
14810
14811
14812
14813
14814
14815
14816
14817
14818
14819

14820
14821
14822
14823
14824

14825
14826
14827

14828
14829
14830
14831
14832
14833
14834
14649
14650
14651
14652
14653
14654
14655

14656
14657
14658
14659

14660
14661
14662
14663
14664
14665
14666
14667
14668
14669
14670
14671
14672
14673
14674
14675
14676
14677

14678
14679
14680
14681
14682

14683
14684
14685

14686
14687
14688
14689
14690
14691
14692
14693







-
+



-
+

















-
+




-
+


-
+








static void JimRandomBytes(Jim_Interp *interp, void *dest, unsigned int len)
{
    Jim_PrngState *prng;
    unsigned char *destByte = (unsigned char *)dest;
    unsigned int si, sj, x;

    

    if (interp->prngState == NULL)
        JimPrngInit(interp);
    prng = interp->prngState;
    

    for (x = 0; x < len; x++) {
        prng->i = (prng->i + 1) & 0xff;
        si = prng->sbox[prng->i];
        prng->j = (prng->j + si) & 0xff;
        sj = prng->sbox[prng->j];
        prng->sbox[prng->i] = sj;
        prng->sbox[prng->j] = si;
        *destByte++ = prng->sbox[(si + sj) & 0xff];
    }
}


static void JimPrngSeed(Jim_Interp *interp, unsigned char *seed, int seedLen)
{
    int i;
    Jim_PrngState *prng;

    

    if (interp->prngState == NULL)
        JimPrngInit(interp);
    prng = interp->prngState;

    

    for (i = 0; i < 256; i++)
        prng->sbox[i] = i;
    

    for (i = 0; i < seedLen; i++) {
        unsigned char t;

        t = prng->sbox[i & 0xFF];
        prng->sbox[i & 0xFF] = prng->sbox[seed[i]];
        prng->sbox[seed[i]] = t;
    }
14851
14852
14853
14854
14855
14856
14857
14858

14859
14860
14861
14862
14863
14864
14865
14866
14867
14868
14869
14870
14871
14872

14873
14874
14875
14876
14877

14878
14879
14880
14881
14882
14883
14884
14885
14886
14887


14888
14889
14890
14891
14892
14893
14894
14895
14896
14897
14898
14899

14900
14901
14902
14903
14904

14905
14906
14907
14908
14909
14910
14911
14912
14913
14914

14915
14916
14917
14918
14919
14920
14921
14922
14923
14924
14925
14926

14927
14928
14929
14930
14931
14932
14933
14934
14935
14936
14937
14938
14939
14940
14941
14942
14943

14944
14945

14946
14947
14948
14949
14950
14951
14952
14953

14954
14955
14956
14957
14958
14959
14960
14961
14962
14963
14964
14965
14966

14967
14968
14969
14970
14971
14972

14973
14974
14975
14976
14977
14978
14979
14710
14711
14712
14713
14714
14715
14716

14717
14718
14719
14720
14721
14722
14723
14724
14725
14726
14727
14728
14729
14730

14731
14732
14733
14734
14735

14736
14737
14738
14739
14740
14741
14742
14743
14744


14745
14746
14747
14748
14749
14750
14751
14752
14753
14754
14755
14756
14757

14758
14759
14760
14761
14762

14763
14764
14765
14766
14767
14768
14769
14770
14771
14772
14773
14774
14775
14776
14777
14778
14779
14780
14781
14782
14783
14784
14785

14786
14787
14788
14789
14790
14791
14792
14793
14794
14795
14796
14797
14798
14799
14800
14801
14802
14803
14804
14805

14806
14807
14808
14809
14810
14811
14812
14813
14814
14815
14816
14817
14818
14819
14820
14821
14822
14823
14824
14825
14826
14827

14828
14829
14830
14831
14832
14833

14834
14835
14836
14837
14838
14839
14840
14841







-
+













-
+




-
+








-
-
+
+











-
+




-
+










+











-
+

















+

-
+








+












-
+





-
+







    }
    if (argc == 3) {
        if (Jim_GetWide(interp, argv[2], &increment) != JIM_OK)
            return JIM_ERR;
    }
    intObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED);
    if (!intObjPtr) {
        

        wideValue = 0;
    }
    else if (Jim_GetWide(interp, intObjPtr, &wideValue) != JIM_OK) {
        return JIM_ERR;
    }
    if (!intObjPtr || Jim_IsShared(intObjPtr)) {
        intObjPtr = Jim_NewIntObj(interp, wideValue + increment);
        if (Jim_SetVariable(interp, argv[1], intObjPtr) != JIM_OK) {
            Jim_FreeNewObj(interp, intObjPtr);
            return JIM_ERR;
        }
    }
    else {
        

        Jim_InvalidateStringRep(intObjPtr);
        JimWideValue(intObjPtr) = wideValue + increment;

        if (argv[1]->typePtr != &variableObjType) {
            

            Jim_SetVariable(interp, argv[1], intObjPtr);
        }
    }
    Jim_SetResult(interp, intObjPtr);
    return JIM_OK;
}


#define JIM_EVAL_SARGV_LEN 8    
#define JIM_EVAL_SINTV_LEN 8    
#define JIM_EVAL_SARGV_LEN 8
#define JIM_EVAL_SINTV_LEN 8


static int JimUnknown(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
    int retcode;

    if (interp->unknown_called > 50) {
        return JIM_ERR;
    }


    

    if (Jim_GetCommand(interp, interp->unknown, JIM_NONE) == NULL)
        return JIM_ERR;

    interp->unknown_called++;
    

    retcode = Jim_EvalObjPrefix(interp, interp->unknown, argc, argv);
    interp->unknown_called--;

    return retcode;
}

static int JimInvokeCommand(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
{
    int retcode;
    Jim_Cmd *cmdPtr;
    void *prevPrivData;

#if 0
    printf("invoke");
    int j;
    for (j = 0; j < objc; j++) {
        printf(" '%s'", Jim_String(objv[j]));
    }
    printf("\n");
#endif

    if (interp->framePtr->tailcallCmd) {
        

        cmdPtr = interp->framePtr->tailcallCmd;
        interp->framePtr->tailcallCmd = NULL;
    }
    else {
        cmdPtr = Jim_GetCommand(interp, objv[0], JIM_ERRMSG);
        if (cmdPtr == NULL) {
            return JimUnknown(interp, objc, objv);
        }
        JimIncrCmdRefCount(cmdPtr);
    }

    if (interp->evalDepth == interp->maxEvalDepth) {
        Jim_SetResultString(interp, "Infinite eval recursion", -1);
        retcode = JIM_ERR;
        goto out;
    }
    interp->evalDepth++;
    prevPrivData = interp->cmdPrivData;

    

    Jim_SetEmptyResult(interp);
    if (cmdPtr->isproc) {
        retcode = JimCallProcedure(interp, cmdPtr, objc, objv);
    }
    else {
        interp->cmdPrivData = cmdPtr->u.native.privData;
        retcode = cmdPtr->u.native.cmdProc(interp, objc, objv);
    }
    interp->cmdPrivData = prevPrivData;
    interp->evalDepth--;

out:
    JimDecrCmdRefCount(interp, cmdPtr);

    return retcode;
}

int Jim_EvalObjVector(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
{
    int i, retcode;

    

    for (i = 0; i < objc; i++)
        Jim_IncrRefCount(objv[i]);

    retcode = JimInvokeCommand(interp, objc, objv);

    

    for (i = 0; i < objc; i++)
        Jim_DecrRefCount(interp, objv[i]);

    return retcode;
}

int Jim_EvalObjPrefix(Jim_Interp *interp, Jim_Obj *prefix, int objc, Jim_Obj *const *objv)
14987
14988
14989
14990
14991
14992
14993
14994

14995
14996
14997
14998
14999
15000
15001
15002

15003
15004
15005
15006

15007
15008

15009
15010
15011
15012
15013
15014
15015
14849
14850
14851
14852
14853
14854
14855

14856
14857
14858
14859
14860
14861
14862
14863

14864
14865
14866
14867

14868
14869

14870
14871
14872
14873
14874
14875
14876
14877







-
+







-
+



-
+

-
+







    Jim_Free(nargv);
    return ret;
}

static void JimAddErrorToStack(Jim_Interp *interp, ScriptObj *script)
{
    if (!interp->errorFlag) {
        

        interp->errorFlag = 1;
        Jim_IncrRefCount(script->fileNameObj);
        Jim_DecrRefCount(interp, interp->errorFileNameObj);
        interp->errorFileNameObj = script->fileNameObj;
        interp->errorLine = script->linenr;

        JimResetStackTrace(interp);
        

        interp->addStackTrace++;
    }

    

    if (interp->addStackTrace > 0) {
        


        JimAppendStackTrace(interp, Jim_String(interp->errorProc), script->fileNameObj, script->linenr);

        if (Jim_Length(script->fileNameObj)) {
            interp->addStackTrace = 0;
        }

15040
15041
15042
15043
15044
15045
15046
15047

15048
15049
15050

15051
15052
15053
15054
15055
15056
15057
14902
14903
14904
14905
14906
14907
14908

14909
14910
14911

14912
14913
14914
14915
14916
14917
14918
14919







-
+


-
+







        case JIM_TT_CMD:
            switch (Jim_EvalObj(interp, token->objPtr)) {
                case JIM_OK:
                case JIM_RETURN:
                    objPtr = interp->result;
                    break;
                case JIM_BREAK:
                    

                    return JIM_BREAK;
                case JIM_CONTINUE:
                    

                    return JIM_CONTINUE;
                default:
                    return JIM_ERR;
            }
            break;
        default:
            JimPanic((1,
15082
15083
15084
15085
15086
15087
15088
15089

15090
15091
15092
15093
15094


15095
15096
15097
15098
15099
15100
15101


15102
15103
15104
15105
15106
15107
15108
15109
15110
15111
15112
15113
15114
15115
15116

15117
15118


15119
15120
15121
15122
15123
15124
15125
15126

15127
15128
15129
15130
15131
15132
15133

15134
15135
15136
15137
15138
15139
15140
15141
15142
15143
15144
15145
15146
15147
15148

15149
15150
15151
15152
15153
15154
15155
14944
14945
14946
14947
14948
14949
14950

14951
14952
14953
14954


14955
14956
14957
14958
14959
14960
14961


14962
14963
14964
14965
14966
14967
14968
14969
14970
14971
14972
14973
14974
14975
14976
14977

14978
14979

14980
14981
14982
14983
14984
14985
14986
14987
14988

14989
14990
14991
14992
14993
14994
14995

14996
14997
14998
14999
15000
15001
15002
15003
15004
15005
15006
15007
15008
15009
15010

15011
15012
15013
15014
15015
15016
15017
15018







-
+



-
-
+
+





-
-
+
+














-
+

-
+
+







-
+






-
+














-
+







    for (i = 0; i < tokens; i++) {
        switch (JimSubstOneToken(interp, &token[i], &intv[i])) {
            case JIM_OK:
            case JIM_RETURN:
                break;
            case JIM_BREAK:
                if (flags & JIM_SUBST_FLAG) {
                    

                    tokens = i;
                    continue;
                }
                
                


            case JIM_CONTINUE:
                if (flags & JIM_SUBST_FLAG) {
                    intv[i] = NULL;
                    continue;
                }
                
                


            default:
                while (i--) {
                    Jim_DecrRefCount(interp, intv[i]);
                }
                if (intv != sintv) {
                    Jim_Free(intv);
                }
                return NULL;
        }
        Jim_IncrRefCount(intv[i]);
        Jim_String(intv[i]);
        totlen += intv[i]->length;
    }

    

    if (tokens == 1 && intv[0] && intv == sintv) {
        Jim_DecrRefCount(interp, intv[0]);

        intv[0]->refCount--;
        return intv[0];
    }

    objPtr = Jim_NewStringObjNoAlloc(interp, NULL, 0);

    if (tokens == 4 && token[0].type == JIM_TT_ESC && token[1].type == JIM_TT_ESC
        && token[2].type == JIM_TT_VAR) {
        

        objPtr->typePtr = &interpolatedObjType;
        objPtr->internalRep.dictSubstValue.varNameObjPtr = token[0].objPtr;
        objPtr->internalRep.dictSubstValue.indexObjPtr = intv[2];
        Jim_IncrRefCount(intv[2]);
    }
    else if (tokens && intv[0] && intv[0]->typePtr == &sourceObjType) {
        

        JimSetSourceInfo(interp, objPtr, intv[0]->internalRep.sourceValue.fileNameObj, intv[0]->internalRep.sourceValue.lineNumber);
    }


    s = objPtr->bytes = Jim_Alloc(totlen + 1);
    objPtr->length = totlen;
    for (i = 0; i < tokens; i++) {
        if (intv[i]) {
            memcpy(s, intv[i]->bytes, intv[i]->length);
            s += intv[i]->length;
            Jim_DecrRefCount(interp, intv[i]);
        }
    }
    objPtr->bytes[totlen] = '\0';
    

    if (intv != sintv) {
        Jim_Free(intv);
    }

    return objPtr;
}

15185
15186
15187
15188
15189
15190
15191
15192

15193
15194
15195
15196
15197
15198
15199
15048
15049
15050
15051
15052
15053
15054

15055
15056
15057
15058
15059
15060
15061
15062







-
+







    Jim_Obj *sargv[JIM_EVAL_SARGV_LEN], **argv = NULL;
    Jim_Obj *prevScriptObj;

    if (Jim_IsList(scriptObjPtr) && scriptObjPtr->bytes == NULL) {
        return JimEvalObjList(interp, scriptObjPtr);
    }

    Jim_IncrRefCount(scriptObjPtr);     
    Jim_IncrRefCount(scriptObjPtr);
    script = JimGetScript(interp, scriptObjPtr);
    if (!JimScriptValid(interp, script)) {
        Jim_DecrRefCount(interp, scriptObjPtr);
        return JIM_ERR;
    }

    Jim_SetEmptyResult(interp);
15221
15222
15223
15224
15225
15226
15227
15228

15229
15230
15231
15232
15233
15234
15235
15236
15237
15238
15239

15240
15241
15242
15243

15244
15245
15246
15247

15248
15249
15250
15251
15252
15253
15254
15084
15085
15086
15087
15088
15089
15090

15091
15092
15093
15094
15095
15096
15097
15098
15099
15100
15101

15102
15103
15104
15105

15106
15107
15108
15109

15110
15111
15112
15113
15114
15115
15116
15117







-
+










-
+



-
+



-
+







            return JIM_OK;
        }
    }
#endif

    script->inUse++;

    

    prevScriptObj = interp->currentScriptObj;
    interp->currentScriptObj = scriptObjPtr;

    interp->errorFlag = 0;
    argv = sargv;

    for (i = 0; i < script->len && retcode == JIM_OK; ) {
        int argc;
        int j;

        

        argc = token[i].objPtr->internalRep.scriptLineValue.argc;
        script->linenr = token[i].objPtr->internalRep.scriptLineValue.line;

        

        if (argc > JIM_EVAL_SARGV_LEN)
            argv = Jim_Alloc(sizeof(Jim_Obj *) * argc);

        

        i++;

        for (j = 0; j < argc; j++) {
            long wordtokens = 1;
            int expand = 0;
            Jim_Obj *wordObjPtr = NULL;

15300
15301
15302
15303
15304
15305
15306
15307

15308
15309
15310
15311
15312
15313
15314
15315
15316
15317
15318
15319
15320

15321
15322
15323
15324
15325

15326
15327
15328
15329
15330
15331
15332
15333

15334
15335
15336
15337
15338
15339
15340

15341
15342

15343
15344
15345
15346
15347
15348

15349
15350
15351
15352
15353
15354
15355
15356
15357
15358
15359

15360
15361
15362
15363

15364
15365

15366
15367
15368
15369

15370
15371
15372
15373
15374
15375
15376
15377
15378
15379
15380
15381
15382
15383

15384
15385
15386

15387
15388
15389
15390
15391
15392
15393
15394
15395
15396
15397

15398
15399
15400
15401
15402
15403
15404
15405
15406
15407
15408
15409
15410
15411

15412
15413
15414
15415
15416
15417
15418
15419
15420

15421
15422
15423
15424
15425
15426

15427
15428
15429
15430
15431
15432
15433
15434
15435
15436
15437
15438
15439
15440
15441
15442
15443
15444
15445
15446
15447
15448
15449
15450
15451
15452
15453
15454
15455

15456
15457
15458
15459
15460
15461
15462
15463
15464
15465
15466
15467

15468
15469
15470
15471
15472
15473

15474
15475
15476
15477

15478
15479
15480
15481
15482
15483
15484
15485
15486
15487
15488
15489
15490
15491

15492
15493
15494
15495
15496
15497
15498
15499

15500
15501
15502
15503

15504
15505
15506
15507
15508
15509

15510
15511
15512
15513
15514
15515
15516
15517

15518
15519
15520
15521
15522
15523
15524
15525
15526

15527
15528
15529

15530
15531
15532
15533
15534

15535
15536
15537
15538
15539
15540
15541
15542

15543
15544
15545
15546
15547
15548
15549
15550
15551
15552
15553
15554
15555

15556
15557
15558
15559
15560

15561
15562
15563
15564
15565
15566
15567
15568

15569
15570
15571
15572
15573


15574
15575
15576
15577

15578
15579
15580
15581
15582
15583
15584
15585
15586
15587
15588
15589
15590
15591
15592
15593

15594
15595
15596
15597
15598
15599
15600

15601
15602
15603
15604
15605
15606
15607
15163
15164
15165
15166
15167
15168
15169

15170
15171
15172
15173
15174
15175
15176
15177
15178
15179
15180
15181
15182

15183
15184
15185
15186
15187

15188
15189
15190
15191
15192
15193
15194
15195

15196
15197
15198
15199
15200
15201
15202

15203
15204

15205
15206
15207
15208
15209
15210

15211
15212
15213
15214
15215
15216
15217
15218
15219
15220
15221

15222
15223
15224
15225

15226
15227

15228
15229
15230
15231

15232
15233
15234
15235
15236
15237
15238
15239
15240
15241
15242
15243
15244
15245

15246
15247
15248

15249
15250
15251
15252
15253
15254
15255
15256
15257
15258
15259

15260
15261
15262
15263
15264
15265
15266
15267
15268
15269
15270
15271
15272
15273

15274
15275
15276
15277
15278
15279
15280
15281
15282

15283
15284
15285
15286
15287
15288

15289
15290
15291
15292
15293
15294
15295
15296
15297
15298
15299
15300
15301
15302
15303
15304
15305
15306
15307
15308

15309
15310
15311
15312
15313
15314
15315
15316

15317
15318
15319
15320
15321
15322
15323
15324
15325
15326
15327
15328

15329
15330
15331
15332
15333
15334

15335
15336
15337
15338

15339
15340
15341
15342
15343
15344
15345
15346
15347
15348
15349
15350
15351
15352

15353
15354
15355
15356
15357
15358
15359
15360

15361
15362
15363
15364

15365
15366
15367
15368
15369
15370

15371
15372
15373
15374
15375
15376
15377
15378

15379
15380
15381
15382
15383
15384
15385
15386
15387

15388
15389
15390

15391
15392
15393
15394
15395

15396
15397
15398
15399
15400
15401
15402
15403

15404
15405
15406
15407
15408
15409
15410
15411
15412
15413
15414
15415
15416

15417
15418
15419
15420
15421

15422
15423
15424
15425
15426
15427
15428
15429

15430
15431
15432
15433
15434

15435
15436
15437
15438
15439

15440
15441
15442
15443
15444
15445
15446
15447
15448
15449
15450
15451
15452
15453
15454
15455

15456
15457
15458
15459
15460
15461
15462

15463
15464
15465
15466
15467
15468
15469
15470







-
+












-
+




-
+







-
+






-
+

-
+





-
+










-
+



-
+

-
+



-
+













-
+


-
+










-
+













-
+








-
+





-
+



















-








-
+











-
+





-
+



-
+













-
+







-
+



-
+





-
+







-
+








-
+


-
+




-
+







-
+












-
+




-
+







-
+




-
+
+



-
+















-
+






-
+







            Jim_IncrRefCount(wordObjPtr);
            i += wordtokens;

            if (!expand) {
                argv[j] = wordObjPtr;
            }
            else {
                

                int len = Jim_ListLength(interp, wordObjPtr);
                int newargc = argc + len - 1;
                int k;

                if (len > 1) {
                    if (argv == sargv) {
                        if (newargc > JIM_EVAL_SARGV_LEN) {
                            argv = Jim_Alloc(sizeof(*argv) * newargc);
                            memcpy(argv, sargv, sizeof(*argv) * j);
                        }
                    }
                    else {
                        

                        argv = Jim_Realloc(argv, sizeof(*argv) * newargc);
                    }
                }

                

                for (k = 0; k < len; k++) {
                    argv[j++] = wordObjPtr->internalRep.listValue.ele[k];
                    Jim_IncrRefCount(wordObjPtr->internalRep.listValue.ele[k]);
                }

                Jim_DecrRefCount(interp, wordObjPtr);

                

                j--;
                argc += len - 1;
            }
        }

        if (retcode == JIM_OK && argc) {
            

            retcode = JimInvokeCommand(interp, argc, argv);
            

            if (Jim_CheckSignal(interp)) {
                retcode = JIM_SIGNAL;
            }
        }

        

        while (j-- > 0) {
            Jim_DecrRefCount(interp, argv[j]);
        }

        if (argv != sargv) {
            Jim_Free(argv);
            argv = sargv;
        }
    }

    

    if (retcode == JIM_ERR) {
        JimAddErrorToStack(interp, script);
    }
    

    else if (retcode != JIM_RETURN || interp->returnCode != JIM_ERR) {
        

        interp->addStackTrace = 0;
    }

    

    interp->currentScriptObj = prevScriptObj;

    Jim_FreeIntRep(interp, scriptObjPtr);
    scriptObjPtr->typePtr = &scriptObjType;
    Jim_SetIntRepPtr(scriptObjPtr, script);
    Jim_DecrRefCount(interp, scriptObjPtr);

    return retcode;
}

static int JimSetProcArg(Jim_Interp *interp, Jim_Obj *argNameObj, Jim_Obj *argValObj)
{
    int retcode;
    

    const char *varname = Jim_String(argNameObj);
    if (*varname == '&') {
        

        Jim_Obj *objPtr;
        Jim_CallFrame *savedCallFrame = interp->framePtr;

        interp->framePtr = interp->framePtr->parent;
        objPtr = Jim_GetVariable(interp, argValObj, JIM_ERRMSG);
        interp->framePtr = savedCallFrame;
        if (!objPtr) {
            return JIM_ERR;
        }

        

        objPtr = Jim_NewStringObj(interp, varname + 1, -1);
        Jim_IncrRefCount(objPtr);
        retcode = Jim_SetVariableLink(interp, objPtr, argValObj, interp->framePtr->parent);
        Jim_DecrRefCount(interp, objPtr);
    }
    else {
        retcode = Jim_SetVariable(interp, argNameObj, argValObj);
    }
    return retcode;
}

static void JimSetProcWrongArgs(Jim_Interp *interp, Jim_Obj *procNameObj, Jim_Cmd *cmd)
{
    

    Jim_Obj *argmsg = Jim_NewStringObj(interp, "", 0);
    int i;

    for (i = 0; i < cmd->u.proc.argListLen; i++) {
        Jim_AppendString(interp, argmsg, " ", 1);

        if (i == cmd->u.proc.argsPos) {
            if (cmd->u.proc.arglist[i].defaultObjPtr) {
                

                Jim_AppendString(interp, argmsg, "?", 1);
                Jim_AppendObj(interp, argmsg, cmd->u.proc.arglist[i].defaultObjPtr);
                Jim_AppendString(interp, argmsg, " ...?", -1);
            }
            else {
                

                Jim_AppendString(interp, argmsg, "?arg...?", -1);
            }
        }
        else {
            if (cmd->u.proc.arglist[i].defaultObjPtr) {
                Jim_AppendString(interp, argmsg, "?", 1);
                Jim_AppendObj(interp, argmsg, cmd->u.proc.arglist[i].nameObjPtr);
                Jim_AppendString(interp, argmsg, "?", 1);
            }
            else {
                const char *arg = Jim_String(cmd->u.proc.arglist[i].nameObjPtr);
                if (*arg == '&') {
                    arg++;
                }
                Jim_AppendString(interp, argmsg, arg, -1);
            }
        }
    }
    Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s%#s\"", procNameObj, argmsg);
    Jim_FreeNewObj(interp, argmsg);
}

#ifdef jim_ext_namespace
int Jim_EvalNamespace(Jim_Interp *interp, Jim_Obj *scriptObj, Jim_Obj *nsObj)
{
    Jim_CallFrame *callFramePtr;
    int retcode;

    

    callFramePtr = JimCreateCallFrame(interp, interp->framePtr, nsObj);
    callFramePtr->argv = &interp->emptyObj;
    callFramePtr->argc = 0;
    callFramePtr->procArgsObjPtr = NULL;
    callFramePtr->procBodyObjPtr = scriptObj;
    callFramePtr->staticVars = NULL;
    callFramePtr->fileNameObj = interp->emptyObj;
    callFramePtr->line = 0;
    Jim_IncrRefCount(scriptObj);
    interp->framePtr = callFramePtr;

    

    if (interp->framePtr->level == interp->maxCallFrameDepth) {
        Jim_SetResultString(interp, "Too many nested calls. Infinite recursion?", -1);
        retcode = JIM_ERR;
    }
    else {
        

        retcode = Jim_EvalObj(interp, scriptObj);
    }

    

    interp->framePtr = interp->framePtr->parent;
    JimFreeCallFrame(interp, callFramePtr, JIM_FCF_REUSE);

    return retcode;
}
#endif

static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc, Jim_Obj *const *argv)
{
    Jim_CallFrame *callFramePtr;
    int i, d, retcode, optargs;
    ScriptObj *script;

    

    if (argc - 1 < cmd->u.proc.reqArity ||
        (cmd->u.proc.argsPos < 0 && argc - 1 > cmd->u.proc.reqArity + cmd->u.proc.optArity)) {
        JimSetProcWrongArgs(interp, argv[0], cmd);
        return JIM_ERR;
    }

    if (Jim_Length(cmd->u.proc.bodyObjPtr) == 0) {
        

        return JIM_OK;
    }

    

    if (interp->framePtr->level == interp->maxCallFrameDepth) {
        Jim_SetResultString(interp, "Too many nested calls. Infinite recursion?", -1);
        return JIM_ERR;
    }

    

    callFramePtr = JimCreateCallFrame(interp, interp->framePtr, cmd->u.proc.nsObj);
    callFramePtr->argv = argv;
    callFramePtr->argc = argc;
    callFramePtr->procArgsObjPtr = cmd->u.proc.argListObjPtr;
    callFramePtr->procBodyObjPtr = cmd->u.proc.bodyObjPtr;
    callFramePtr->staticVars = cmd->u.proc.staticVars;

    

    script = JimGetScript(interp, interp->currentScriptObj);
    callFramePtr->fileNameObj = script->fileNameObj;
    callFramePtr->line = script->linenr;

    Jim_IncrRefCount(cmd->u.proc.argListObjPtr);
    Jim_IncrRefCount(cmd->u.proc.bodyObjPtr);
    interp->framePtr = callFramePtr;

    

    optargs = (argc - 1 - cmd->u.proc.reqArity);

    

    i = 1;
    for (d = 0; d < cmd->u.proc.argListLen; d++) {
        Jim_Obj *nameObjPtr = cmd->u.proc.arglist[d].nameObjPtr;
        if (d == cmd->u.proc.argsPos) {
            

            Jim_Obj *listObjPtr;
            int argsLen = 0;
            if (cmd->u.proc.reqArity + cmd->u.proc.optArity < argc - 1) {
                argsLen = argc - 1 - (cmd->u.proc.reqArity + cmd->u.proc.optArity);
            }
            listObjPtr = Jim_NewListObj(interp, &argv[i], argsLen);

            

            if (cmd->u.proc.arglist[d].defaultObjPtr) {
                nameObjPtr =cmd->u.proc.arglist[d].defaultObjPtr;
            }
            retcode = Jim_SetVariable(interp, nameObjPtr, listObjPtr);
            if (retcode != JIM_OK) {
                goto badargset;
            }

            i += argsLen;
            continue;
        }

        

        if (cmd->u.proc.arglist[d].defaultObjPtr == NULL || optargs-- > 0) {
            retcode = JimSetProcArg(interp, nameObjPtr, argv[i++]);
        }
        else {
            

            retcode = Jim_SetVariable(interp, nameObjPtr, cmd->u.proc.arglist[d].defaultObjPtr);
        }
        if (retcode != JIM_OK) {
            goto badargset;
        }
    }

    

    retcode = Jim_EvalObj(interp, cmd->u.proc.bodyObjPtr);

badargset:

    

    retcode = JimInvokeDefer(interp, retcode);
    interp->framePtr = interp->framePtr->parent;
    JimFreeCallFrame(interp, callFramePtr, JIM_FCF_REUSE);

    

    if (interp->framePtr->tailcallObj) {
        do {
            Jim_Obj *tailcallObj = interp->framePtr->tailcallObj;

            interp->framePtr->tailcallObj = NULL;

            if (retcode == JIM_EVAL) {
                retcode = Jim_EvalObjList(interp, tailcallObj);
                if (retcode == JIM_RETURN) {
                    interp->returnLevel++;
                }
            }
            Jim_DecrRefCount(interp, tailcallObj);
        } while (interp->framePtr->tailcallObj);

        

        if (interp->framePtr->tailcallCmd) {
            JimDecrCmdRefCount(interp, interp->framePtr->tailcallCmd);
            interp->framePtr->tailcallCmd = NULL;
        }
    }

    

    if (retcode == JIM_RETURN) {
        if (--interp->returnLevel <= 0) {
            retcode = interp->returnCode;
            interp->returnCode = JIM_OK;
            interp->returnLevel = 0;
        }
    }
15709
15710
15711
15712
15713
15714
15715
15716

15717
15718
15719
15720
15721
15722
15723
15724
15725

15726
15727
15728
15729
15730
15731
15732
15572
15573
15574
15575
15576
15577
15578

15579
15580
15581
15582
15583
15584
15585
15586
15587

15588
15589
15590
15591
15592
15593
15594
15595







-
+








-
+







    Jim_IncrRefCount(scriptObjPtr);

    prevScriptObj = interp->currentScriptObj;
    interp->currentScriptObj = scriptObjPtr;

    retcode = Jim_EvalObj(interp, scriptObjPtr);

    

    if (retcode == JIM_RETURN) {
        if (--interp->returnLevel <= 0) {
            retcode = interp->returnCode;
            interp->returnCode = JIM_OK;
            interp->returnLevel = 0;
        }
    }
    if (retcode == JIM_ERR) {
        

        interp->addStackTrace++;
    }

    interp->currentScriptObj = prevScriptObj;

    Jim_DecrRefCount(interp, scriptObjPtr);

15748
15749
15750
15751
15752
15753
15754
15755

15756
15757
15758
15759
15760
15761
15762
15611
15612
15613
15614
15615
15616
15617

15618
15619
15620
15621
15622
15623
15624
15625







-
+







        JimParseCmd(pc);
        return;
    }
    if (*pc->p == '$' && !(flags & JIM_SUBST_NOVAR)) {
        if (JimParseVar(pc) == JIM_OK) {
            return;
        }
        

        pc->tstart = pc->p;
        flags |= JIM_SUBST_NOVAR;
    }
    while (pc->len) {
        if (*pc->p == '$' && !(flags & JIM_SUBST_NOVAR)) {
            break;
        }
15779
15780
15781
15782
15783
15784
15785
15786

15787
15788
15789
15790
15791
15792
15793

15794
15795
15796
15797
15798
15799
15800

15801
15802
15803
15804
15805
15806
15807

15808
15809
15810
15811
15812
15813
15814
15815
15816
15817
15818
15819
15820
15821
15822

15823
15824
15825
15826
15827
15828
15829
15830
15831
15832
15833
15834
15835
15836
15837
15838
15839
15840

15841
15842
15843
15844
15845
15846
15847
15848
15849
15850
15851
15852
15853
15854
15855




15856

15857
15858

15859
15860
15861
15862
15863
15864
15865
15866
15867
15868
15869
15870
15871
15872
15873
15874
15875
15876
15877
15878
15879
15880
15881

15882
15883
15884
15885
15886
15887
15888
15642
15643
15644
15645
15646
15647
15648

15649
15650
15651
15652
15653
15654
15655

15656
15657
15658
15659
15660
15661
15662

15663
15664
15665
15666
15667
15668
15669

15670
15671
15672
15673
15674
15675
15676
15677
15678
15679
15680
15681
15682
15683
15684

15685
15686
15687
15688
15689
15690
15691
15692
15693
15694
15695
15696
15697
15698
15699
15700
15701
15702

15703
15704
15705
15706
15707
15708
15709
15710
15711
15712
15713
15714
15715
15716
15717
15718
15719
15720
15721
15722

15723
15724

15725
15726
15727
15728
15729
15730
15731

15732

15733
15734
15735
15736
15737
15738
15739
15740
15741
15742
15743
15744
15745

15746
15747
15748
15749
15750
15751
15752
15753







-
+






-
+






-
+






-
+














-
+

















-
+















+
+
+
+
-
+

-
+






-

-













-
+







{
    int scriptTextLen;
    const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
    struct JimParserCtx parser;
    struct ScriptObj *script = Jim_Alloc(sizeof(*script));
    ParseTokenList tokenlist;

    

    ScriptTokenListInit(&tokenlist);

    JimParserInit(&parser, scriptText, scriptTextLen, 1);
    while (1) {
        JimParseSubst(&parser, flags);
        if (parser.eof) {
            

            break;
        }
        ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt,
            parser.tline);
    }

    

    script->inUse = 1;
    script->substFlags = flags;
    script->fileNameObj = interp->emptyObj;
    Jim_IncrRefCount(script->fileNameObj);
    SubstObjAddTokens(interp, script, &tokenlist);

    

    ScriptTokenListFree(&tokenlist);

#ifdef DEBUG_SHOW_SUBST
    {
        int i;

        printf("==== Subst ====\n");
        for (i = 0; i < script->len; i++) {
            printf("[%2d] %s '%s'\n", i, jim_tt_name(script->token[i].type),
                Jim_String(script->token[i].objPtr));
        }
    }
#endif

    

    Jim_FreeIntRep(interp, objPtr);
    Jim_SetIntRepPtr(objPtr, script);
    objPtr->typePtr = &scriptObjType;
    return JIM_OK;
}

static ScriptObj *Jim_GetSubst(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
{
    if (objPtr->typePtr != &scriptObjType || ((ScriptObj *)Jim_GetIntRepPtr(objPtr))->substFlags != flags)
        SetSubstFromAny(interp, objPtr, flags);
    return (ScriptObj *) Jim_GetIntRepPtr(objPtr);
}

int Jim_SubstObj(Jim_Interp *interp, Jim_Obj *substObjPtr, Jim_Obj **resObjPtrPtr, int flags)
{
    ScriptObj *script = Jim_GetSubst(interp, substObjPtr, flags);

    Jim_IncrRefCount(substObjPtr);      
    Jim_IncrRefCount(substObjPtr);
    script->inUse++;

    *resObjPtrPtr = JimInterpolateTokens(interp, script->token, script->len, flags);

    script->inUse--;
    Jim_DecrRefCount(interp, substObjPtr);
    if (*resObjPtrPtr == NULL) {
        return JIM_ERR;
    }
    return JIM_OK;
}

void Jim_WrongNumArgs(Jim_Interp *interp, int argc, Jim_Obj *const *argv, const char *msg)
{
    Jim_Obj *objPtr;
    Jim_Obj *listObjPtr;

    JimPanic((argc == 0, "Jim_WrongNumArgs() called with argc=0"));

    Jim_Obj *listObjPtr = Jim_NewListObj(interp, argv, argc);
    listObjPtr = Jim_NewListObj(interp, argv, argc);

    if (*msg) {
    if (msg && *msg) {
        Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, msg, -1));
    }
    Jim_IncrRefCount(listObjPtr);
    objPtr = Jim_ListJoin(interp, listObjPtr, " ", 1);
    Jim_DecrRefCount(interp, listObjPtr);

    Jim_IncrRefCount(objPtr);
    Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s\"", objPtr);
    Jim_DecrRefCount(interp, objPtr);
}

typedef void JimHashtableIteratorCallbackType(Jim_Interp *interp, Jim_Obj *listObjPtr,
    Jim_HashEntry *he, int type);

#define JimTrivialMatch(pattern)    (strpbrk((pattern), "*[?\\") == NULL)

static Jim_Obj *JimHashtablePatternMatch(Jim_Interp *interp, Jim_HashTable *ht, Jim_Obj *patternObjPtr,
    JimHashtableIteratorCallbackType *callback, int type)
{
    Jim_HashEntry *he;
    Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);

    

    if (patternObjPtr && JimTrivialMatch(Jim_String(patternObjPtr))) {
        he = Jim_FindHashEntry(ht, Jim_String(patternObjPtr));
        if (he) {
            callback(interp, listObjPtr, he, type);
        }
    }
    else {
15905
15906
15907
15908
15909
15910
15911
15912

15913
15914
15915
15916
15917
15918
15919
15770
15771
15772
15773
15774
15775
15776

15777
15778
15779
15780
15781
15782
15783
15784







-
+







static void JimCommandMatch(Jim_Interp *interp, Jim_Obj *listObjPtr,
    Jim_HashEntry *he, int type)
{
    Jim_Cmd *cmdPtr = Jim_GetHashEntryVal(he);
    Jim_Obj *objPtr;

    if (type == JIM_CMDLIST_PROCS && !cmdPtr->isproc) {
        

        return;
    }

    objPtr = Jim_NewStringObj(interp, he->key, -1);
    Jim_IncrRefCount(objPtr);

    if (type != JIM_CMDLIST_CHANNELS || Jim_AioFilehandle(interp, objPtr)) {
15965
15966
15967
15968
15969
15970
15971
15972

15973
15974
15975
15976
15977
15978
15979
15830
15831
15832
15833
15834
15835
15836

15837
15838
15839
15840
15841
15842
15843
15844







-
+







{
    Jim_CallFrame *targetCallFrame;

    targetCallFrame = JimGetCallFrameByInteger(interp, levelObjPtr);
    if (targetCallFrame == NULL) {
        return JIM_ERR;
    }
    

    if (targetCallFrame == interp->topFramePtr) {
        Jim_SetResultFormatted(interp, "bad level \"%#s\"", levelObjPtr);
        return JIM_ERR;
    }
    if (info_level_cmd) {
        *objPtrPtr = Jim_NewListObj(interp, targetCallFrame->argv, targetCallFrame->argc);
    }
16093
16094
16095
16096
16097
16098
16099
16100





16101

16102
16103
16104
16105
16106
16107
16108
15958
15959
15960
15961
15962
15963
15964

15965
15966
15967
15968
15969
15970
15971
15972
15973
15974
15975
15976
15977
15978







-
+
+
+
+
+

+







    for (i = 2; i < argc; i++) {
        if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK) {
            doubleRes = (double)res;
            goto trydouble;
        }
        if (op == JIM_EXPROP_SUB)
            res -= wideValue;
        else
        else {
            if (wideValue == 0) {
                Jim_SetResultString(interp, "Division by zero", -1);
                return JIM_ERR;
            }
            res /= wideValue;
        }
    }
    Jim_SetResultInt(interp, res);
    return JIM_OK;
  trydouble:
    for (; i < argc; i++) {
        if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
            return JIM_ERR;
16152
16153
16154
16155
16156
16157
16158
16159

16160
16161
16162
16163
16164
16165
16166
16022
16023
16024
16025
16026
16027
16028

16029
16030
16031
16032
16033
16034
16035
16036







-
+








        objPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
        if (!objPtr)
            return JIM_ERR;
        Jim_SetResult(interp, objPtr);
        return JIM_OK;
    }
    

    if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
        return JIM_ERR;
    Jim_SetResult(interp, argv[2]);
    return JIM_OK;
}

static int Jim_UnsetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16195
16196
16197
16198
16199
16200
16201
16202

16203
16204
16205
16206
16207
16208
16209
16065
16066
16067
16068
16069
16070
16071

16072
16073
16074
16075
16076
16077
16078
16079







-
+







static int Jim_WhileCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
    if (argc != 3) {
        Jim_WrongNumArgs(interp, 1, argv, "condition body");
        return JIM_ERR;
    }

    

    while (1) {
        int boolean, retval;

        if ((retval = Jim_GetBoolFromExpr(interp, argv[1], &boolean)) != JIM_OK)
            return retval;
        if (!boolean)
            break;
16235
16236
16237
16238
16239
16240
16241
16242

16243
16244
16245
16246
16247
16248
16249
16250
16251
16252
16253

16254
16255
16256
16257
16258

16259
16260
16261
16262

16263
16264
16265
16266
16267


16268
16269
16270
16271
16272
16273

16274
16275
16276

16277
16278
16279
16280
16281
16282


16283







16284
16285
16286
16287
16288
16289


16290
16291
16292
16293
16294
16295



16296
16297
16298
16299
16300

16301
16302

16303
16304
16305
16306
16307


16308
16309
16310
16311
16312
16313
16314
16315

16316
16317
16318


16319
16320

16321
16322
16323
16324
16325
16326
16327
16328
16329
16330
16331
16332

16333
16334
16335
16336
16337
16338
16339

16340
16341
16342
16343
16344
16345
16346
16105
16106
16107
16108
16109
16110
16111

16112
16113
16114
16115
16116
16117
16118
16119
16120
16121
16122

16123
16124
16125
16126
16127

16128
16129
16130
16131

16132
16133
16134
16135


16136
16137


16138
16139
16140

16141
16142
16143

16144
16145
16146
16147
16148
16149
16150
16151
16152

16153
16154
16155
16156
16157
16158
16159
16160
16161
16162
16163


16164
16165
16166
16167
16168



16169
16170
16171
16172
16173
16174
16175

16176
16177

16178
16179
16180
16181


16182
16183
16184
16185
16186
16187
16188
16189
16190

16191
16192


16193
16194
16195

16196
16197
16198
16199
16200
16201
16202
16203
16204
16205
16206
16207

16208
16209
16210
16211
16212
16213
16214

16215
16216
16217
16218
16219
16220
16221
16222







-
+










-
+




-
+



-
+



-
-
+
+
-
-



-
+


-
+






+
+
-
+
+
+
+
+
+
+




-
-
+
+



-
-
-
+
+
+




-
+

-
+



-
-
+
+







-
+

-
-
+
+

-
+











-
+






-
+







    Jim_Obj *stopVarNamePtr = NULL;

    if (argc != 5) {
        Jim_WrongNumArgs(interp, 1, argv, "start test next body");
        return JIM_ERR;
    }

    

    if ((retval = Jim_EvalObj(interp, argv[1])) != JIM_OK) {
        return retval;
    }

    retval = Jim_GetBoolFromExpr(interp, argv[2], &boolean);


#ifdef JIM_OPTIMIZATION
    if (retval == JIM_OK && boolean) {
        ScriptObj *incrScript;
        ExprByteCode *expr;
        struct ExprTree *expr;
        jim_wide stop, currentVal;
        Jim_Obj *objPtr;
        int cmpOffset;

        

        expr = JimGetExpression(interp, argv[2]);
        incrScript = JimGetScript(interp, argv[3]);

        

        if (incrScript == NULL || incrScript->len != 3 || !expr || expr->len != 3) {
            goto evalstart;
        }
        
        if (incrScript->token[1].type != JIM_TT_ESC ||

        if (incrScript->token[1].type != JIM_TT_ESC) {
            expr->token[0].type != JIM_TT_VAR ||
            (expr->token[1].type != JIM_TT_EXPR_INT && expr->token[1].type != JIM_TT_VAR)) {
            goto evalstart;
        }

        if (expr->token[2].type == JIM_EXPROP_LT) {
        if (expr->expr->type == JIM_EXPROP_LT) {
            cmpOffset = 0;
        }
        else if (expr->token[2].type == JIM_EXPROP_LTE) {
        else if (expr->expr->type == JIM_EXPROP_LTE) {
            cmpOffset = 1;
        }
        else {
            goto evalstart;
        }

        if (expr->expr->left->type != JIM_TT_VAR) {
            goto evalstart;
        
        }

        if (expr->expr->right->type != JIM_TT_VAR && expr->expr->right->type != JIM_TT_EXPR_INT) {
            goto evalstart;
        }


        if (!Jim_CompareStringImmediate(interp, incrScript->token[1].objPtr, "incr")) {
            goto evalstart;
        }

        
        if (!Jim_StringEqObj(incrScript->token[2].objPtr, expr->token[0].objPtr)) {

        if (!Jim_StringEqObj(incrScript->token[2].objPtr, expr->expr->left->objPtr)) {
            goto evalstart;
        }

        
        if (expr->token[1].type == JIM_TT_EXPR_INT) {
            if (Jim_GetWide(interp, expr->token[1].objPtr, &stop) == JIM_ERR) {

        if (expr->expr->right->type == JIM_TT_EXPR_INT) {
            if (Jim_GetWide(interp, expr->expr->right->objPtr, &stop) == JIM_ERR) {
                goto evalstart;
            }
        }
        else {
            stopVarNamePtr = expr->token[1].objPtr;
            stopVarNamePtr = expr->expr->right->objPtr;
            Jim_IncrRefCount(stopVarNamePtr);
            

            stop = 0;
        }

        
        varNamePtr = expr->token[0].objPtr;

        varNamePtr = expr->expr->left->objPtr;
        Jim_IncrRefCount(varNamePtr);

        objPtr = Jim_GetVariable(interp, varNamePtr, JIM_NONE);
        if (objPtr == NULL || Jim_GetWide(interp, objPtr, &currentVal) != JIM_OK) {
            goto testcond;
        }

        

        while (retval == JIM_OK) {
            
            



            

            if (stopVarNamePtr) {
                objPtr = Jim_GetVariable(interp, stopVarNamePtr, JIM_NONE);
                if (objPtr == NULL || Jim_GetWide(interp, objPtr, &stop) != JIM_OK) {
                    goto testcond;
                }
            }

            if (currentVal >= stop + cmpOffset) {
                break;
            }

            

            retval = Jim_EvalObj(interp, argv[4]);
            if (retval == JIM_OK || retval == JIM_CONTINUE) {
                retval = JIM_OK;

                objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);

                

                if (objPtr == NULL) {
                    retval = JIM_ERR;
                    goto out;
                }
                if (!Jim_IsShared(objPtr) && objPtr->typePtr == &intObjType) {
                    currentVal = ++JimWideValue(objPtr);
                    Jim_InvalidateStringRep(objPtr);
16356
16357
16358
16359
16360
16361
16362
16363

16364
16365
16366
16367
16368


16369
16370
16371
16372


16373
16374
16375
16376
16377

16378
16379
16380
16381
16382
16383
16384
16232
16233
16234
16235
16236
16237
16238

16239
16240
16241
16242


16243
16244
16245
16246


16247
16248
16249
16250
16251
16252

16253
16254
16255
16256
16257
16258
16259
16260







-
+



-
-
+
+


-
-
+
+




-
+







        }
        goto out;
    }
  evalstart:
#endif

    while (boolean && (retval == JIM_OK || retval == JIM_CONTINUE)) {
        

        retval = Jim_EvalObj(interp, argv[4]);

        if (retval == JIM_OK || retval == JIM_CONTINUE) {
            
          evalnext:

JIM_IF_OPTIM(evalnext:)
            retval = Jim_EvalObj(interp, argv[3]);
            if (retval == JIM_OK || retval == JIM_CONTINUE) {
                
              testcond:

JIM_IF_OPTIM(testcond:)
                retval = Jim_GetBoolFromExpr(interp, argv[2], &boolean);
            }
        }
    }
  out:
JIM_IF_OPTIM(out:)
    if (stopVarNamePtr) {
        Jim_DecrRefCount(interp, stopVarNamePtr);
    }
    if (varNamePtr) {
        Jim_DecrRefCount(interp, varNamePtr);
    }

16416
16417
16418
16419
16420
16421
16422
16423

16424
16425
16426
16427
16428
16429
16430
16292
16293
16294
16295
16296
16297
16298

16299
16300
16301
16302
16303
16304
16305
16306







-
+







    while (((i < limit && incr > 0) || (i > limit && incr < 0)) && retval == JIM_OK) {
        retval = Jim_EvalObj(interp, bodyObjPtr);
        if (retval == JIM_OK || retval == JIM_CONTINUE) {
            Jim_Obj *objPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);

            retval = JIM_OK;

            

            i += incr;

            if (objPtr && !Jim_IsShared(objPtr) && objPtr->typePtr == &intObjType) {
                if (argv[1]->typePtr != &variableObjType) {
                    if (Jim_SetVariable(interp, argv[1], objPtr) != JIM_OK) {
                        return JIM_ERR;
                    }
16481
16482
16483
16484
16485
16486
16487
16488

16489
16490
16491
16492
16493
16494
16495
16496
16497
16498


16499
16500
16501
16502
16503
16504
16505
16506
16507
16508
16509
16510
16511
16512
16513
16514

16515
16516
16517
16518
16519
16520
16521
16522
16523
16524
16525
16526

16527
16528
16529
16530
16531
16532
16533

16534
16535
16536
16537

16538
16539
16540
16541

16542
16543
16544
16545
16546

16547
16548
16549

16550
16551
16552
16553
16554
16555
16556
16357
16358
16359
16360
16361
16362
16363

16364
16365
16366
16367
16368
16369
16370
16371
16372


16373
16374
16375
16376
16377
16378
16379
16380
16381
16382
16383
16384
16385
16386
16387
16388
16389

16390
16391
16392
16393
16394
16395
16396
16397
16398
16399
16400
16401

16402
16403
16404
16405
16406
16407
16408

16409
16410
16411
16412

16413
16414
16415
16416

16417
16418
16419
16420
16421

16422
16423
16424

16425
16426
16427
16428
16429
16430
16431
16432







-
+








-
-
+
+















-
+











-
+






-
+



-
+



-
+




-
+


-
+







}


static int JimForeachMapHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int doMap)
{
    int result = JIM_OK;
    int i, numargs;
    Jim_ListIter twoiters[2];   
    Jim_ListIter twoiters[2];
    Jim_ListIter *iters;
    Jim_Obj *script;
    Jim_Obj *resultObj;

    if (argc < 4 || argc % 2 != 0) {
        Jim_WrongNumArgs(interp, 1, argv, "varList list ?varList list ...? script");
        return JIM_ERR;
    }
    script = argv[argc - 1];    
    numargs = (argc - 1 - 1);    
    script = argv[argc - 1];
    numargs = (argc - 1 - 1);

    if (numargs == 2) {
        iters = twoiters;
    }
    else {
        iters = Jim_Alloc(numargs * sizeof(*iters));
    }
    for (i = 0; i < numargs; i++) {
        JimListIterInit(&iters[i], argv[i + 1]);
        if (i % 2 == 0 && JimListIterDone(interp, &iters[i])) {
            result = JIM_ERR;
        }
    }
    if (result != JIM_OK) {
        Jim_SetResultString(interp, "foreach varlist is empty", -1);
        return result;
        goto empty_varlist;
    }

    if (doMap) {
        resultObj = Jim_NewListObj(interp, NULL, 0);
    }
    else {
        resultObj = interp->emptyObj;
    }
    Jim_IncrRefCount(resultObj);

    while (1) {
        

        for (i = 0; i < numargs; i += 2) {
            if (!JimListIterDone(interp, &iters[i + 1])) {
                break;
            }
        }
        if (i == numargs) {
            

            break;
        }

        

        for (i = 0; i < numargs; i += 2) {
            Jim_Obj *varName;

            

            JimListIterInit(&iters[i], argv[i + 1]);
            while ((varName = JimListIterNext(interp, &iters[i])) != NULL) {
                Jim_Obj *valObj = JimListIterNext(interp, &iters[i + 1]);
                if (!valObj) {
                    

                    valObj = interp->emptyObj;
                }
                

                Jim_IncrRefCount(valObj);
                result = Jim_SetVariable(interp, varName, valObj);
                Jim_DecrRefCount(interp, valObj);
                if (result != JIM_OK) {
                    goto err;
                }
            }
16570
16571
16572
16573
16574
16575
16576

16577
16578
16579
16580
16581
16582
16583
16446
16447
16448
16449
16450
16451
16452
16453
16454
16455
16456
16457
16458
16459
16460







+







        }
    }
  out:
    result = JIM_OK;
    Jim_SetResult(interp, resultObj);
  err:
    Jim_DecrRefCount(interp, resultObj);
  empty_varlist:
    if (numargs > 2) {
        Jim_Free(iters);
    }
    return result;
}


16628
16629
16630
16631
16632
16633
16634
16635

16636
16637
16638
16639
16640
16641

16642
16643
16644
16645
16646

16647
16648
16649
16650
16651

16652
16653
16654
16655
16656
16657
16658

16659
16660
16661
16662
16663
16664
16665

16666
16667
16668
16669
16670
16671
16672
16505
16506
16507
16508
16509
16510
16511

16512
16513
16514
16515
16516
16517

16518
16519
16520
16521
16522

16523
16524
16525
16526
16527

16528
16529
16530
16531
16532
16533
16534

16535
16536
16537
16538
16539
16540
16541

16542
16543
16544
16545
16546
16547
16548
16549







-
+





-
+




-
+




-
+






-
+






-
+








static int Jim_IfCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
    int boolean, retval, current = 1, falsebody = 0;

    if (argc >= 3) {
        while (1) {
            

            if (current >= argc)
                goto err;
            if ((retval = Jim_GetBoolFromExpr(interp, argv[current++], &boolean))
                != JIM_OK)
                return retval;
            

            if (current >= argc)
                goto err;
            if (Jim_CompareStringImmediate(interp, argv[current], "then"))
                current++;
            

            if (current >= argc)
                goto err;
            if (boolean)
                return Jim_EvalObj(interp, argv[current]);
            

            if (++current >= argc) {
                Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
                return JIM_OK;
            }
            falsebody = current++;
            if (Jim_CompareStringImmediate(interp, argv[falsebody], "else")) {
                

                if (current != argc - 1)
                    goto err;
                return Jim_EvalObj(interp, argv[current]);
            }
            else if (Jim_CompareStringImmediate(interp, argv[falsebody], "elseif"))
                continue;
            

            else if (falsebody != argc - 1)
                goto err;
            return Jim_EvalObj(interp, argv[falsebody]);
        }
        return JIM_OK;
    }
  err:
16696
16697
16698
16699
16700
16701
16702
16703
16704
16705
16706
16707
16708

16709
16710
16711


16712
16713
16714
16715
16716
16717
16718
16573
16574
16575
16576
16577
16578
16579



16580
16581
16582
16583
16584


16585
16586
16587
16588
16589
16590
16591
16592
16593







-
-
-



+

-
-
+
+







    if (rc != JIM_OK || Jim_GetLong(interp, Jim_GetResult(interp), &eq) != JIM_OK) {
        eq = -rc;
    }

    return eq;
}

enum
{ SWITCH_EXACT, SWITCH_GLOB, SWITCH_RE, SWITCH_CMD };


static int Jim_SwitchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
    enum { SWITCH_EXACT, SWITCH_GLOB, SWITCH_RE, SWITCH_CMD };
    int matchOpt = SWITCH_EXACT, opt = 1, patCount, i;
    Jim_Obj *command = 0, *const *caseList = 0, *strObj;
    Jim_Obj *script = 0;
    Jim_Obj *command = NULL, *scriptObj = NULL, *strObj;
    Jim_Obj **caseList;

    if (argc < 3) {
      wrongnumargs:
        Jim_WrongNumArgs(interp, 1, argv, "?options? string "
            "pattern body ... ?default body?   or   " "{pattern body ?pattern body ...?}");
        return JIM_ERR;
    }
16745
16746
16747
16748
16749
16750
16751
16752
16753
16754

16755
16756
16757
16758

16759
16760
16761

16762
16763
16764
16765
16766
16767
16768
16769

16770
16771
16772
16773

16774
16775
16776
16777

16778
16779
16780
16781
16782
16783
16784

16785
16786
16787

16788
16789
16790
16791
16792

16793
16794
16795
16796
16797
16798

16799
16800
16801
16802
16803



16804
16805
16806
16807
16808
16809


16810
16811
16812
16813
16814
16815
16816
16620
16621
16622
16623
16624
16625
16626



16627

16628
16629

16630
16631
16632

16633
16634
16635
16636
16637
16638
16639
16640

16641
16642
16643
16644

16645
16646
16647
16648

16649
16650
16651
16652
16653



16654

16655

16656
16657
16658
16659
16660

16661
16662
16663
16664
16665
16666

16667
16668
16669



16670
16671
16672
16673
16674
16675
16676


16677
16678
16679
16680
16681
16682
16683
16684
16685







-
-
-
+
-


-
+


-
+







-
+



-
+



-
+




-
-
-
+
-

-
+




-
+





-
+


-
-
-
+
+
+




-
-
+
+







        }
        if ((argc - opt) < 2)
            goto wrongnumargs;
    }
    strObj = argv[opt++];
    patCount = argc - opt;
    if (patCount == 1) {
        Jim_Obj **vector;

        JimListGetElements(interp, argv[opt], &patCount, &vector);
        JimListGetElements(interp, argv[opt], &patCount, &caseList);
        caseList = vector;
    }
    else
        caseList = &argv[opt];
        caseList = (Jim_Obj **)&argv[opt];
    if (patCount == 0 || patCount % 2 != 0)
        goto wrongnumargs;
    for (i = 0; script == 0 && i < patCount; i += 2) {
    for (i = 0; scriptObj == NULL && i < patCount; i += 2) {
        Jim_Obj *patObj = caseList[i];

        if (!Jim_CompareStringImmediate(interp, patObj, "default")
            || i < (patCount - 2)) {
            switch (matchOpt) {
                case SWITCH_EXACT:
                    if (Jim_StringEqObj(strObj, patObj))
                        script = caseList[i + 1];
                        scriptObj = caseList[i + 1];
                    break;
                case SWITCH_GLOB:
                    if (Jim_StringMatchObj(interp, patObj, strObj, 0))
                        script = caseList[i + 1];
                        scriptObj = caseList[i + 1];
                    break;
                case SWITCH_RE:
                    command = Jim_NewStringObj(interp, "regexp", -1);
                    

                case SWITCH_CMD:{
                        int rc = Jim_CommandMatchObj(interp, command, patObj, strObj, 0);

                        if (argc - opt == 1) {
                            Jim_Obj **vector;

                            JimListGetElements(interp, argv[opt], &patCount, &vector);
                            JimListGetElements(interp, argv[opt], &patCount, &caseList);
                            caseList = vector;
                        }
                        

                        if (rc < 0) {
                            return -rc;
                        }
                        if (rc)
                            script = caseList[i + 1];
                            scriptObj = caseList[i + 1];
                        break;
                    }
            }
        }
        else {
            script = caseList[i + 1];
            scriptObj = caseList[i + 1];
        }
    }
    for (; i < patCount && Jim_CompareStringImmediate(interp, script, "-"); i += 2)
        script = caseList[i + 1];
    if (script && Jim_CompareStringImmediate(interp, script, "-")) {
    for (; i < patCount && Jim_CompareStringImmediate(interp, scriptObj, "-"); i += 2)
        scriptObj = caseList[i + 1];
    if (scriptObj && Jim_CompareStringImmediate(interp, scriptObj, "-")) {
        Jim_SetResultFormatted(interp, "no body specified for pattern \"%#s\"", caseList[i - 2]);
        return JIM_ERR;
    }
    Jim_SetEmptyResult(interp);
    if (script) {
        return Jim_EvalObj(interp, script);
    if (scriptObj) {
        return Jim_EvalObj(interp, scriptObj);
    }
    return JIM_OK;
}


static int Jim_ListCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
16918
16919
16920
16921
16922
16923
16924
16925

16926
16927
16928
16929
16930
16931
16932
16787
16788
16789
16790
16791
16792
16793

16794
16795
16796
16797
16798
16799
16800
16801







-
+







                opt_all = 1;
                break;
            case OPT_COMMAND:
                if (i >= argc - 2) {
                    goto wrongargs;
                }
                commandObj = argv[++i];
                

            case OPT_EXACT:
            case OPT_GLOB:
            case OPT_REGEXP:
                opt_match = option;
                break;
        }
    }
16966
16967
16968
16969
16970
16971
16972
16973

16974
16975
16976
16977
16978
16979

16980
16981
16982
16983
16984
16985
16986
16835
16836
16837
16838
16839
16840
16841

16842
16843
16844
16845
16846
16847

16848
16849
16850
16851
16852
16853
16854
16855







-
+





-
+







                    }
                    rc = JIM_ERR;
                    goto done;
                }
                break;
        }

        

        if (!eq && opt_bool && opt_not && !opt_all) {
            continue;
        }

        if ((!opt_bool && eq == !opt_not) || (opt_bool && (eq || opt_all))) {
            

            Jim_Obj *resultObj;

            if (opt_bool) {
                resultObj = Jim_NewIntObj(interp, eq ^ opt_not);
            }
            else if (!opt_inline) {
                resultObj = Jim_NewIntObj(interp, i);
16999
17000
17001
17002
17003
17004
17005
17006

17007
17008
17009
17010
17011
17012
17013
16868
16869
16870
16871
16872
16873
16874

16875
16876
16877
16878
16879
16880
16881
16882







-
+







        }
    }

    if (opt_all) {
        Jim_SetResult(interp, listObjPtr);
    }
    else {
        

        if (opt_bool) {
            Jim_SetResultBool(interp, opt_not);
        }
        else if (!opt_inline) {
            Jim_SetResultInt(interp, -1);
        }
    }
17028
17029
17030
17031
17032
17033
17034
17035

17036
17037
17038
17039
17040
17041
17042
16897
16898
16899
16900
16901
16902
16903

16904
16905
16906
16907
16908
16909
16910
16911







-
+








    if (argc < 2) {
        Jim_WrongNumArgs(interp, 1, argv, "varName ?value value ...?");
        return JIM_ERR;
    }
    listObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED);
    if (!listObjPtr) {
        

        listObjPtr = Jim_NewListObj(interp, NULL, 0);
        new_obj = 1;
    }
    else if (Jim_IsShared(listObjPtr)) {
        listObjPtr = Jim_DuplicateObj(interp, listObjPtr);
        new_obj = 1;
    }
17101
17102
17103
17104
17105
17106
17107
17108
17109

17110
17111
17112
17113
17114

17115
17116
17117
17118
17119
17120

17121
17122
17123
17124
17125

17126
17127
17128

17129
17130
17131
17132
17133
17134
17135
17136
17137
17138
17139
17140
17141
17142
17143

17144
17145
17146
17147
17148
17149
17150
17151
17152
17153
17154
17155
17156
17157
17158
17159
17160
17161
17162

17163
17164
17165
17166
17167
17168
17169
16970
16971
16972
16973
16974
16975
16976


16977





16978
16979





16980
16981

16982
16983

16984
16985
16986

16987
16988
16989
16990
16991
16992
16993
16994
16995
16996
16997
16998
16999
17000
17001

17002
17003
17004
17005
17006
17007
17008
17009
17010
17011
17012
17013
17014
17015
17016
17017
17018
17019
17020
17021
17022
17023
17024
17025
17026
17027
17028
17029







-
-
+
-
-
-
-
-
+

-
-
-
-
-
+

-


-
+


-
+














-
+



















+







    len = Jim_ListLength(interp, listObj);

    first = JimRelToAbsIndex(len, first);
    last = JimRelToAbsIndex(len, last);
    JimRelToAbsRange(len, &first, &last, &rangeLen);


    
    if (first < len) {
    if (first > len) {
        
    }
    else if (len == 0) {
        
        first = 0;
        first = len;
    }
    else {
        Jim_SetResultString(interp, "list doesn't contain element ", -1);
        Jim_AppendObj(interp, Jim_GetResult(interp), argv[2]);
        return JIM_ERR;
    }


    
    newListObj = Jim_NewListObj(interp, listObj->internalRep.listValue.ele, first);

    

    ListInsertElements(newListObj, -1, argc - 4, argv + 4);

    

    ListInsertElements(newListObj, -1, len - first - rangeLen, listObj->internalRep.listValue.ele + first + rangeLen);

    Jim_SetResult(interp, newListObj);
    return JIM_OK;
}


static int Jim_LsetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
    if (argc < 3) {
        Jim_WrongNumArgs(interp, 1, argv, "listVar ?index...? newVal");
        return JIM_ERR;
    }
    else if (argc == 3) {
        

        if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
            return JIM_ERR;
        Jim_SetResult(interp, argv[2]);
        return JIM_OK;
    }
    return Jim_ListSetIndex(interp, argv[1], argv + 2, argc - 3, argv[argc - 1]);
}


static int Jim_LsortCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const argv[])
{
    static const char * const options[] = {
        "-ascii", "-nocase", "-increasing", "-decreasing", "-command", "-integer", "-real", "-index", "-unique", NULL
    };
    enum
    { OPT_ASCII, OPT_NOCASE, OPT_INCREASING, OPT_DECREASING, OPT_COMMAND, OPT_INTEGER, OPT_REAL, OPT_INDEX, OPT_UNIQUE };
    Jim_Obj *resObj;
    int i;
    int retCode;
    int shared;

    struct lsort_info info;

    if (argc < 2) {
        Jim_WrongNumArgs(interp, 1, argv, "?options? list");
        return JIM_ERR;
    }
17221
17222
17223
17224
17225
17226
17227


17228

17229
17230
17231
17232
17233

17234
17235
17236
17237
17238
17239
17240
17081
17082
17083
17084
17085
17086
17087
17088
17089

17090
17091
17092
17093
17094

17095
17096
17097
17098
17099
17100
17101
17102







+
+
-
+




-
+







                    return JIM_ERR;
                }
                info.indexed = 1;
                i++;
                break;
        }
    }
    resObj = argv[argc - 1];
    if ((shared = Jim_IsShared(resObj)))
    resObj = Jim_DuplicateObj(interp, argv[argc - 1]);
        resObj = Jim_DuplicateObj(interp, resObj);
    retCode = ListSortElements(interp, resObj, &info);
    if (retCode == JIM_OK) {
        Jim_SetResult(interp, resObj);
    }
    else {
    else if (shared) {
        Jim_FreeNewObj(interp, resObj);
    }
    return retCode;
}


static int Jim_AppendCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17251
17252
17253
17254
17255
17256
17257
17258

17259
17260
17261
17262
17263
17264
17265
17266
17267
17268
17269
17270
17271
17272
17273
17274
17275
17276
17277
17278

17279
17280
17281
17282
17283
17284
17285
17113
17114
17115
17116
17117
17118
17119

17120
17121
17122
17123
17124
17125
17126
17127
17128
17129
17130
17131
17132
17133
17134
17135
17136
17137
17138
17139
17140
17141
17142
17143
17144
17145
17146
17147
17148







-
+




















+







        if (!stringObjPtr)
            return JIM_ERR;
    }
    else {
        int new_obj = 0;
        stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED);
        if (!stringObjPtr) {
            

            stringObjPtr = Jim_NewEmptyStringObj(interp);
            new_obj = 1;
        }
        else if (Jim_IsShared(stringObjPtr)) {
            new_obj = 1;
            stringObjPtr = Jim_DuplicateObj(interp, stringObjPtr);
        }
        for (i = 2; i < argc; i++) {
            Jim_AppendObj(interp, stringObjPtr, argv[i]);
        }
        if (Jim_SetVariable(interp, argv[1], stringObjPtr) != JIM_OK) {
            if (new_obj) {
                Jim_FreeNewObj(interp, stringObjPtr);
            }
            return JIM_ERR;
        }
    }
    Jim_SetResult(interp, stringObjPtr);
    return JIM_OK;
}



static int Jim_DebugCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
#if !defined(JIM_DEBUG_COMMAND)
    Jim_SetResultString(interp, "unsupported", -1);
    return JIM_ERR;
17300
17301
17302
17303
17304
17305
17306
17307

17308
17309
17310
17311
17312
17313
17314
17315
17316
17317
17318
17319
17320
17321

17322
17323
17324

17325
17326
17327
17328
17329
17330
17331
17332
17333
17334
17335
17336
17337
17338
17339
17340
17341

17342
17343
17344
17345
17346
17347
17348
17349
17350
17351
17352
17353
17354
17355
17356
17357
17358
17359
17360
17361
17362
17363
17364
17365

17366
17367
17368
17369
17370
17371
17372

17373
17374
17375
17376
17377
17378
17379
17380
17381
17382
17383
17384
17385
17386
17387
17388
17389
17163
17164
17165
17166
17167
17168
17169

17170
17171
17172
17173
17174
17175
17176
17177
17178
17179
17180
17181
17182
17183

17184
17185
17186

17187
17188
17189
17190
17191
17192
17193
17194
17195
17196
17197
17198
17199
17200
17201
17202
17203

17204
17205
17206
17207
17208
17209
17210
17211
17212
17213
17214
17215
17216
17217
17218
17219
17220
17221
17222
17223

17224
17225
17226

17227
17228
17229
17230
17231
17232
17233

17234
17235
17236
17237
17238
17239
17240
17241
17242


17243
17244
17245
17246
17247
17248
17249







-
+













-
+


-
+
















-
+



















-



-
+






-
+








-
-







        rc = Jim_EvalObj(interp, argv[1]);
    }
    else {
        rc = Jim_EvalObj(interp, Jim_ConcatObj(interp, argc - 1, argv + 1));
    }

    if (rc == JIM_ERR) {
        

        interp->addStackTrace++;
    }
    return rc;
}


static int Jim_UplevelCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
    if (argc >= 2) {
        int retcode;
        Jim_CallFrame *savedCallFrame, *targetCallFrame;
        const char *str;

        

        savedCallFrame = interp->framePtr;

        

        str = Jim_String(argv[1]);
        if ((str[0] >= '0' && str[0] <= '9') || str[0] == '#') {
            targetCallFrame = Jim_GetCallFrameByLevel(interp, argv[1]);
            argc--;
            argv++;
        }
        else {
            targetCallFrame = Jim_GetCallFrameByLevel(interp, NULL);
        }
        if (targetCallFrame == NULL) {
            return JIM_ERR;
        }
        if (argc < 2) {
            Jim_WrongNumArgs(interp, 1, argv - 1, "?level? command ?arg ...?");
            return JIM_ERR;
        }
        

        interp->framePtr = targetCallFrame;
        if (argc == 2) {
            retcode = Jim_EvalObj(interp, argv[1]);
        }
        else {
            retcode = Jim_EvalObj(interp, Jim_ConcatObj(interp, argc - 1, argv + 1));
        }
        interp->framePtr = savedCallFrame;
        return retcode;
    }
    else {
        Jim_WrongNumArgs(interp, 1, argv, "?level? command ?arg ...?");
        return JIM_ERR;
    }
}


static int Jim_ExprCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
    Jim_Obj *exprResultPtr;
    int retcode;

    if (argc == 2) {
        retcode = Jim_EvalExpression(interp, argv[1], &exprResultPtr);
        retcode = Jim_EvalExpression(interp, argv[1]);
    }
    else if (argc > 2) {
        Jim_Obj *objPtr;

        objPtr = Jim_ConcatObj(interp, argc - 1, argv + 1);
        Jim_IncrRefCount(objPtr);
        retcode = Jim_EvalExpression(interp, objPtr, &exprResultPtr);
        retcode = Jim_EvalExpression(interp, objPtr);
        Jim_DecrRefCount(interp, objPtr);
    }
    else {
        Jim_WrongNumArgs(interp, 1, argv, "expression ?...?");
        return JIM_ERR;
    }
    if (retcode != JIM_OK)
        return retcode;
    Jim_SetResult(interp, exprResultPtr);
    Jim_DecrRefCount(interp, exprResultPtr);
    return JIM_OK;
}


static int Jim_BreakCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
    if (argc != 1) {
17436
17437
17438
17439
17440
17441
17442
17443

17444
17445
17446
17447

17448
17449
17450
17451
17452
17453
17454
17455
17456
17457
17458
17459
17460
17461
17462
17463
17464
17465
17466
17467
17468

17469
17470
17471
17472
17473
17474
17475
17476
17477
17478

17479
17480
17481
17482

17483
17484
17485
17486
17487
17488

17489
17490
17491
17492
17493
17494
17495
17496
17497
17498
17499

17500
17501
17502
17503
17504
17505
17506
17296
17297
17298
17299
17300
17301
17302

17303
17304
17305
17306

17307
17308
17309
17310
17311
17312
17313
17314
17315
17316
17317
17318
17319
17320
17321
17322
17323
17324
17325
17326
17327

17328
17329
17330
17331
17332
17333
17334
17335
17336
17337

17338
17339
17340
17341

17342
17343
17344
17345
17346
17347

17348
17349
17350
17351
17352
17353
17354
17355
17356
17357
17358

17359
17360
17361
17362
17363
17364
17365
17366







-
+



-
+




















-
+









-
+



-
+





-
+










-
+







    }

    if (i != argc - 1 && i != argc) {
        Jim_WrongNumArgs(interp, 1, argv,
            "?-code code? ?-errorinfo stacktrace? ?-level level? ?result?");
    }

    

    if (stackTraceObj && returnCode == JIM_ERR) {
        JimSetStackTrace(interp, stackTraceObj);
    }
    

    if (errorCodeObj && returnCode == JIM_ERR) {
        Jim_SetGlobalVariableStr(interp, "errorCode", errorCodeObj);
    }
    interp->returnCode = returnCode;
    interp->returnLevel = level;

    if (i == argc - 1) {
        Jim_SetResult(interp, argv[i]);
    }
    return JIM_RETURN;
}


static int Jim_TailcallCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
    if (interp->framePtr->level == 0) {
        Jim_SetResultString(interp, "tailcall can only be called from a proc or lambda", -1);
        return JIM_ERR;
    }
    else if (argc >= 2) {
        

        Jim_CallFrame *cf = interp->framePtr->parent;

        Jim_Cmd *cmdPtr = Jim_GetCommand(interp, argv[1], JIM_ERRMSG);
        if (cmdPtr == NULL) {
            return JIM_ERR;
        }

        JimPanic((cf->tailcallCmd != NULL, "Already have a tailcallCmd"));

        

        JimIncrCmdRefCount(cmdPtr);
        cf->tailcallCmd = cmdPtr;

        

        JimPanic((cf->tailcallObj != NULL, "Already have a tailcallobj"));

        cf->tailcallObj = Jim_NewListObj(interp, argv + 1, argc - 1);
        Jim_IncrRefCount(cf->tailcallObj);

        

        return JIM_EVAL;
    }
    return JIM_OK;
}

static int JimAliasCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
    Jim_Obj *cmdList;
    Jim_Obj *prefixListObj = Jim_CmdPrivData(interp);

    

    cmdList = Jim_DuplicateObj(interp, prefixListObj);
    Jim_ListInsertElements(interp, cmdList, Jim_ListLength(interp, cmdList), argc - 1, argv + 1);

    return JimEvalObjList(interp, cmdList);
}

static void JimAliasCmdDelete(Jim_Interp *interp, void *privData)
17550
17551
17552
17553
17554
17555
17556
17557

17558
17559
17560
17561
17562
17563

17564
17565
17566
17567
17568

17569
17570
17571
17572
17573
17574
17575
17576
17577
17578
17579
17580
17581
17582
17583
17584
17585

17586
17587
17588
17589
17590
17591

17592
17593
17594
17595
17596
17597
17598
17410
17411
17412
17413
17414
17415
17416

17417
17418
17419
17420
17421
17422

17423
17424
17425
17426
17427

17428
17429
17430
17431
17432
17433
17434
17435
17436
17437
17438
17439
17440
17441
17442
17443
17444

17445
17446
17447
17448
17449
17450

17451
17452
17453
17454
17455
17456
17457
17458







-
+





-
+




-
+
















-
+





-
+







        cmd = JimCreateProcedureCmd(interp, argv[2], NULL, argv[3], NULL);
    }
    else {
        cmd = JimCreateProcedureCmd(interp, argv[2], argv[3], argv[4], NULL);
    }

    if (cmd) {
        

        Jim_Obj *qualifiedCmdNameObj;
        const char *cmdname = JimQualifyName(interp, Jim_String(argv[1]), &qualifiedCmdNameObj);

        JimCreateCommand(interp, cmdname, cmd);

        

        JimUpdateProcNamespace(interp, cmd, cmdname);

        JimFreeQualifiedName(interp, qualifiedCmdNameObj);

        

        Jim_SetResult(interp, argv[1]);
        return JIM_OK;
    }
    return JIM_ERR;
}


static int Jim_LocalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
    int retcode;

    if (argc < 2) {
        Jim_WrongNumArgs(interp, 1, argv, "cmd ?args ...?");
        return JIM_ERR;
    }

    

    interp->local++;
    retcode = Jim_EvalObjVector(interp, argc - 1, argv + 1);
    interp->local--;


    

    if (retcode == 0) {
        Jim_Obj *cmdNameObj = Jim_GetResult(interp);

        if (Jim_GetCommand(interp, cmdNameObj, JIM_ERRMSG) == NULL) {
            return JIM_ERR;
        }
        if (interp->framePtr->localCommands == NULL) {
17617
17618
17619
17620
17621
17622
17623
17624

17625
17626
17627
17628

17629
17630
17631

17632
17633
17634
17635
17636
17637
17638
17477
17478
17479
17480
17481
17482
17483

17484
17485
17486
17487

17488
17489
17490

17491
17492
17493
17494
17495
17496
17497
17498







-
+



-
+


-
+







        int retcode;

        Jim_Cmd *cmdPtr = Jim_GetCommand(interp, argv[1], JIM_ERRMSG);
        if (cmdPtr == NULL || !cmdPtr->isproc || !cmdPtr->prevCmd) {
            Jim_SetResultFormatted(interp, "no previous command: \"%#s\"", argv[1]);
            return JIM_ERR;
        }
        

        cmdPtr->u.proc.upcall++;
        JimIncrCmdRefCount(cmdPtr);

        

        retcode = Jim_EvalObjVector(interp, argc - 1, argv + 1);

        

        cmdPtr->u.proc.upcall--;
        JimDecrCmdRefCount(interp, cmdPtr);

        return retcode;
    }
}

17655
17656
17657
17658
17659
17660
17661
17662

17663
17664
17665
17666
17667
17668
17669
17670
17671
17672
17673
17674
17675

17676
17677
17678
17679
17680
17681
17682
17515
17516
17517
17518
17519
17520
17521

17522
17523
17524
17525
17526
17527
17528
17529
17530
17531
17532
17533
17534

17535
17536
17537
17538
17539
17540
17541
17542







-
+












-
+







        if (len != 2 && len != 3) {
            Jim_SetResultFormatted(interp, "can't interpret \"%#s\" as a lambda expression", argv[1]);
            return JIM_ERR;
        }

        if (len == 3) {
#ifdef jim_ext_namespace
            

            nsObj = JimQualifyNameObj(interp, Jim_ListGetIndex(interp, argv[1], 2));
#else
            Jim_SetResultString(interp, "namespaces not enabled", -1);
            return JIM_ERR;
#endif
        }
        argListObjPtr = Jim_ListGetIndex(interp, argv[1], 0);
        bodyObjPtr = Jim_ListGetIndex(interp, argv[1], 1);

        cmd = JimCreateProcedureCmd(interp, argListObjPtr, NULL, bodyObjPtr, nsObj);

        if (cmd) {
            

            nargv = Jim_Alloc((argc - 2 + 1) * sizeof(*nargv));
            nargv[0] = Jim_NewStringObj(interp, "apply lambdaExpr", -1);
            Jim_IncrRefCount(nargv[0]);
            memcpy(&nargv[1], argv + 2, (argc - 2) * sizeof(*nargv));
            ret = JimCallProcedure(interp, cmd, argc - 2 + 1, nargv);
            Jim_DecrRefCount(interp, nargv[0]);
            Jim_Free(nargv);
17698
17699
17700
17701
17702
17703
17704
17705

17706
17707
17708
17709
17710
17711
17712
17713
17714
17715
17716
17717
17718

17719
17720
17721
17722
17723
17724

17725
17726
17727
17728
17729
17730
17731
17732
17733
17734
17735
17736
17737
17738
17739
17740
17741

17742
17743

17744
17745

17746
17747
17748
17749
17750
17751
17752
17558
17559
17560
17561
17562
17563
17564

17565
17566
17567
17568
17569
17570
17571
17572
17573
17574
17575
17576
17577

17578
17579
17580
17581
17582
17583

17584
17585
17586
17587
17588
17589
17590
17591
17592
17593
17594
17595
17596
17597
17598
17599
17600

17601
17602

17603
17604

17605
17606
17607
17608
17609
17610
17611
17612







-
+












-
+





-
+
















-
+

-
+

-
+









static int Jim_UpvarCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
    int i;
    Jim_CallFrame *targetCallFrame;

    

    if (argc > 3 && (argc % 2 == 0)) {
        targetCallFrame = Jim_GetCallFrameByLevel(interp, argv[1]);
        argc--;
        argv++;
    }
    else {
        targetCallFrame = Jim_GetCallFrameByLevel(interp, NULL);
    }
    if (targetCallFrame == NULL) {
        return JIM_ERR;
    }

    

    if (argc < 3) {
        Jim_WrongNumArgs(interp, 1, argv, "?level? otherVar localVar ?otherVar localVar ...?");
        return JIM_ERR;
    }

    

    for (i = 1; i < argc; i += 2) {
        if (Jim_SetVariableLink(interp, argv[i + 1], argv[i], targetCallFrame) != JIM_OK)
            return JIM_ERR;
    }
    return JIM_OK;
}


static int Jim_GlobalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
    int i;

    if (argc < 2) {
        Jim_WrongNumArgs(interp, 1, argv, "varName ?varName ...?");
        return JIM_ERR;
    }
    

    if (interp->framePtr->level == 0)
        return JIM_OK;          
        return JIM_OK;
    for (i = 1; i < argc; i++) {
        

        const char *name = Jim_String(argv[i]);
        if (name[0] != ':' || name[1] != ':') {
            if (Jim_SetVariableLink(interp, argv[i], argv[i], interp->topFramePtr) != JIM_OK)
                return JIM_ERR;
        }
    }
    return JIM_OK;
17765
17766
17767
17768
17769
17770
17771
17772

17773
17774
17775
17776

17777
17778
17779
17780
17781
17782



17783
17784
17785
17786
17787
17788
17789
17790
17791
17792
17793
17794
17795
17796
17797
17798
17799

17800
17801
17802
17803
17804
17805
17806
17625
17626
17627
17628
17629
17630
17631

17632
17633
17634
17635

17636
17637
17638
17639



17640
17641
17642
17643
17644
17645
17646
17647
17648
17649
17650
17651
17652
17653
17654
17655
17656
17657
17658

17659
17660
17661
17662
17663
17664
17665
17666







-
+



-
+



-
-
-
+
+
+
















-
+







        Jim_SetResultString(interp, "list must contain an even number of elements", -1);
        return NULL;
    }

    str = Jim_String(objPtr);
    strLen = Jim_Utf8Length(interp, objPtr);

    

    resultObjPtr = Jim_NewStringObj(interp, "", 0);
    while (strLen) {
        for (i = 0; i < numMaps; i += 2) {
            Jim_Obj *objPtr;
            Jim_Obj *eachObjPtr;
            const char *k;
            int kl;

            objPtr = Jim_ListGetIndex(interp, mapListObjPtr, i);
            k = Jim_String(objPtr);
            kl = Jim_Utf8Length(interp, objPtr);
            eachObjPtr = Jim_ListGetIndex(interp, mapListObjPtr, i);
            k = Jim_String(eachObjPtr);
            kl = Jim_Utf8Length(interp, eachObjPtr);

            if (strLen >= kl && kl) {
                int rc;
                rc = JimStringCompareLen(str, k, kl, nocase);
                if (rc == 0) {
                    if (noMatchStart) {
                        Jim_AppendString(interp, resultObjPtr, noMatchStart, str - noMatchStart);
                        noMatchStart = NULL;
                    }
                    Jim_AppendObj(interp, resultObjPtr, Jim_ListGetIndex(interp, mapListObjPtr, i + 1));
                    str += utf8_index(str, kl);
                    strLen -= kl;
                    break;
                }
            }
        }
        if (i == numMaps) {     
        if (i == numMaps) {
            int c;
            if (noMatchStart == NULL)
                noMatchStart = str;
            str += utf8_tounicode(str, &c);
            strLen--;
        }
    }
17836
17837
17838
17839
17840
17841
17842
17843

17844
17845
17846
17847
17848
17849
17850
17851
17852
17853
17854
17855
17856
17857
17858
17859
17860
17861
17862
17863
17864

17865
17866
17867
17868
17869
17870
17871
17872
17873
17874
17875
17876
17877
17878
17879
17880
17881
17882
17883

17884
17885
17886
17887
17888
17889
17890
17891
17892
17893
17894
17895
17896

17897
17898
17899
17900
17901

17902
17903
17904
17905
17906
17907
17908
17909
17910
17911
17912
17913
17914
17915
17916

17917
17918
17919
17920
17921
17922
17923
17696
17697
17698
17699
17700
17701
17702

17703
17704
17705
17706
17707
17708
17709
17710
17711
17712
17713
17714
17715
17716
17717
17718
17719
17720
17721
17722
17723

17724
17725
17726
17727
17728
17729
17730
17731
17732
17733
17734
17735
17736
17737
17738
17739
17740
17741
17742

17743
17744
17745
17746
17747
17748
17749
17750
17751
17752
17753
17754
17755

17756
17757
17758
17759
17760

17761
17762
17763
17764
17765
17766
17767
17768
17769
17770
17771
17772
17773
17774
17775

17776
17777
17778
17779
17780
17781
17782
17783







-
+




















-
+


















-
+












-
+




-
+














-
+








    if (argc < 2) {
        Jim_WrongNumArgs(interp, 1, argv, "option ?arguments ...?");
        return JIM_ERR;
    }
    if (Jim_GetEnum(interp, argv[1], options, &option, NULL,
            JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK)
        return JIM_ERR;
        return Jim_CheckShowCommands(interp, argv[1], options);

    switch (option) {
        case OPT_LENGTH:
        case OPT_BYTELENGTH:
            if (argc != 3) {
                Jim_WrongNumArgs(interp, 2, argv, "string");
                return JIM_ERR;
            }
            if (option == OPT_LENGTH) {
                len = Jim_Utf8Length(interp, argv[2]);
            }
            else {
                len = Jim_Length(argv[2]);
            }
            Jim_SetResultInt(interp, len);
            return JIM_OK;

        case OPT_CAT:{
                Jim_Obj *objPtr;
                if (argc == 3) {
                    

                    objPtr = argv[2];
                }
                else {
                    int i;

                    objPtr = Jim_NewStringObj(interp, "", 0);

                    for (i = 2; i < argc; i++) {
                        Jim_AppendObj(interp, objPtr, argv[i]);
                    }
                }
                Jim_SetResult(interp, objPtr);
                return JIM_OK;
            }

        case OPT_COMPARE:
        case OPT_EQUAL:
            {
                

                long opt_length = -1;
                int n = argc - 4;
                int i = 2;
                while (n > 0) {
                    int subopt;
                    if (Jim_GetEnum(interp, argv[i++], nocase_length_options, &subopt, NULL,
                            JIM_ENUM_ABBREV) != JIM_OK) {
badcompareargs:
                        Jim_WrongNumArgs(interp, 2, argv, "?-nocase? ?-length int? string1 string2");
                        return JIM_ERR;
                    }
                    if (subopt == 0) {
                        

                        opt_case = 0;
                        n--;
                    }
                    else {
                        

                        if (n < 2) {
                            goto badcompareargs;
                        }
                        if (Jim_GetLong(interp, argv[i++], &opt_length) != JIM_OK) {
                            return JIM_ERR;
                        }
                        n -= 2;
                    }
                }
                if (n) {
                    goto badcompareargs;
                }
                argv += argc - 2;
                if (opt_length < 0 && option != OPT_COMPARE && opt_case) {
                    

                    Jim_SetResultBool(interp, Jim_StringEqObj(argv[0], argv[1]));
                }
                else {
                    if (opt_length >= 0) {
                        n = JimStringCompareLen(Jim_String(argv[0]), Jim_String(argv[1]), opt_length, !opt_case);
                    }
                    else {
18023
18024
18025
18026
18027
18028
18029
18030
18031
18032
18033
18034
18035
18036
18037
17883
17884
17885
17886
17887
17888
17889

17890
17891
17892
17893
17894
17895
17896







-







                Jim_SetResult(interp, objPtr);
                return JIM_OK;
            }

        case OPT_REVERSE:{
                char *buf, *p;
                const char *str;
                int len;
                int i;

                if (argc != 3) {
                    Jim_WrongNumArgs(interp, 2, argv, "string");
                    return JIM_ERR;
                }

18067
18068
18069
18070
18071
18072
18073
18074

18075
18076
18077
18078
18079
18080
18081
17926
17927
17928
17929
17930
17931
17932

17933
17934
17935
17936
17937
17938
17939
17940







-
+







                if (idx != INT_MIN && idx != INT_MAX) {
                    idx = JimRelToAbsIndex(len, idx);
                }
                if (idx < 0 || idx >= len || str == NULL) {
                    Jim_SetResultString(interp, "", 0);
                }
                else if (len == Jim_Length(argv[2])) {
                    

                    Jim_SetResultString(interp, str + idx, 1);
                }
                else {
                    int c;
                    int i = utf8_index(str, idx);
                    Jim_SetResultString(interp, str + i, utf8_tounicode(str + i, &c));
                }
18221
18222
18223
18224
18225
18226
18227
18228

18229
18230
18231
18232
18233
18234
18235
18236
18237
18238
18239

18240
18241
18242
18243
18244
18245
18246
18080
18081
18082
18083
18084
18085
18086

18087
18088
18089
18090
18091
18092
18093
18094
18095
18096
18097

18098
18099
18100
18101
18102
18103
18104
18105







-
+










-
+








static int Jim_CatchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
    int exitCode = 0;
    int i;
    int sig = 0;

    

    jim_wide ignore_mask = (1 << JIM_EXIT) | (1 << JIM_EVAL) | (1 << JIM_SIGNAL);
    static const int max_ignore_code = sizeof(ignore_mask) * 8;

    Jim_SetGlobalVariableStr(interp, "errorCode", Jim_NewStringObj(interp, "NONE", -1));

    for (i = 1; i < argc - 1; i++) {
        const char *arg = Jim_String(argv[i]);
        jim_wide option;
        int ignore;

        

        if (strcmp(arg, "--") == 0) {
            i++;
            break;
        }
        if (*arg != '-') {
            break;
        }
18283
18284
18285
18286
18287
18288
18289
18290

18291
18292
18293
18294
18295

18296
18297
18298
18299
18300

18301
18302

18303
18304
18305
18306
18307

18308
18309
18310
18311
18312
18313
18314
18142
18143
18144
18145
18146
18147
18148

18149
18150
18151
18152
18153

18154
18155
18156
18157
18158

18159
18160

18161
18162
18163
18164
18165

18166
18167
18168
18169
18170
18171
18172
18173







-
+




-
+




-
+

-
+




-
+








    if ((ignore_mask & (1 << JIM_SIGNAL)) == 0) {
        sig++;
    }

    interp->signal_level += sig;
    if (Jim_CheckSignal(interp)) {
        

        exitCode = JIM_SIGNAL;
    }
    else {
        exitCode = Jim_EvalObj(interp, argv[0]);
        

        interp->errorFlag = 0;
    }
    interp->signal_level -= sig;

    

    if (exitCode >= 0 && exitCode < max_ignore_code && (((unsigned jim_wide)1 << exitCode) & ignore_mask)) {
        

        return exitCode;
    }

    if (sig && exitCode == JIM_SIGNAL) {
        

        if (interp->signal_set_result) {
            interp->signal_set_result(interp, interp->sigmask);
        }
        else {
            Jim_SetResultInt(interp, interp->sigmask);
        }
        interp->sigmask = 0;
18343
18344
18345
18346
18347
18348
18349
18350
18351
18352
18353
18354
18355
18356
18357
18358
18359
18360
18361
18362
18363
18364
18365
18366
18367
18368
18369
18370
18371
18372
18373
18374
18375
18376
18377
18378
18379
18380
18381
18382
18383
18384
18385
18386
18387
18388
18389
18390
18391
18392
18393
18394
18395
18396
18397
18398
18399
18400
18401
18402
18403
18404
18405
18406
18407
18408
18409
18410
18411
18412
18413
18414
18415
18416
18417
18418
18419
18420
18421
18422
18423
18424
18425
18426
18427
18428
18429
18430
18431
18432
18433
18434
18435
18436
18437
18438
18439
18440
18441
18442
18443
18444
18445
18446
18447
18448
18449
18450
18451
18452
18453
18454
18455
18456
18457
18458
18459
18460
18461
18462
18463
18464
18465
18466
18467
18468
18469
18470
18471
18472
18473
18474
18475
18476
18477
18478
18479
18480
18481

18482
18483
18484
18485
18486
18487
18488

18489
18490

18491
18492

18493
18494
18495
18496
18497


18498


18499
18500


18501
18502

18503
18504
18505
18506
18507








18508
18509
18510
18511



18512
18513
18514

18515
18516

18517
18518
18519
18520




18521
18522

18523
18524
18525
18526

18527
18528
18529
18530
18531
18532
18533
18534
18535
18536



























18537
18538
18539
18540
18541





18542
18543
18544
18545
18546
18547
18548
18549
18550



18551
18552
18553




18554
18555
18556
18557
18558
18559
18560
18561











18562
18563




18564





18565
18566
18567
18568
18569
18570
18571
18572
18573
18574
18575
18576
18577

18578




















































18579
18580
18581

18582
18583
18584
18585
18586
18587
18588
18589
18590
18591
18592
18593
18594
18595
18596
18597
18598
18599
18600
18601

18602
18603
18604
18605
18606
18607
18608
18202
18203
18204
18205
18206
18207
18208



















































































































18209
18210
18211
18212
18213
18214
18215
18216
18217
18218
18219
18220
18221
18222
18223
18224

18225







18226


18227


18228


18229
18230

18231
18232
18233
18234
18235


18236
18237


18238





18239
18240
18241
18242
18243
18244
18245
18246




18247
18248
18249



18250


18251




18252
18253
18254
18255


18256




18257
18258
18259
18260
18261
18262
18263
18264
18265
18266
18267
18268
18269
18270
18271
18272
18273
18274
18275
18276
18277
18278
18279
18280
18281
18282
18283
18284
18285
18286
18287
18288
18289
18290
18291
18292
18293
18294
18295
18296
18297
18298
18299
18300
18301
18302
18303
18304
18305
18306
18307
18308
18309
18310
18311


18312
18313
18314
18315
18316
18317
18318
18319
18320
18321








18322
18323
18324
18325
18326
18327
18328
18329
18330
18331
18332


18333
18334
18335
18336
18337
18338
18339
18340
18341
18342
18343
18344
18345
18346
18347
18348
18349
18350
18351
18352
18353
18354
18355
18356

18357
18358
18359
18360
18361
18362
18363
18364
18365
18366
18367
18368
18369
18370
18371
18372
18373
18374
18375
18376
18377
18378
18379
18380
18381
18382
18383
18384
18385
18386
18387
18388
18389
18390
18391
18392
18393
18394
18395
18396
18397
18398
18399
18400
18401
18402
18403
18404
18405
18406
18407
18408
18409
18410
18411
18412
18413
18414
18415
18416
18417
18418
18419
18420
18421
18422
18423
18424
18425
18426
18427
18428
18429
18430
18431

18432
18433
18434
18435
18436
18437
18438
18439







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
















-
+
-
-
-
-
-
-
-
+
-
-
+
-
-
+
-
-


-
+
+

+
+
-
-
+
+
-
-
+
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
-
-
-
+
-
-
+
-
-
-
-
+
+
+
+
-
-
+
-
-
-
-
+










+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





+
+
+
+
+







-
-
+
+
+



+
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+

+
+
+
+
+













+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



+



















-
+







            }
        }
    }
    Jim_SetResultInt(interp, exitCode);
    return JIM_OK;
}

#ifdef JIM_REFERENCES


static int Jim_RefCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
    if (argc != 3 && argc != 4) {
        Jim_WrongNumArgs(interp, 1, argv, "string tag ?finalizer?");
        return JIM_ERR;
    }
    if (argc == 3) {
        Jim_SetResult(interp, Jim_NewReference(interp, argv[1], argv[2], NULL));
    }
    else {
        Jim_SetResult(interp, Jim_NewReference(interp, argv[1], argv[2], argv[3]));
    }
    return JIM_OK;
}


static int Jim_GetrefCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
    Jim_Reference *refPtr;

    if (argc != 2) {
        Jim_WrongNumArgs(interp, 1, argv, "reference");
        return JIM_ERR;
    }
    if ((refPtr = Jim_GetReference(interp, argv[1])) == NULL)
        return JIM_ERR;
    Jim_SetResult(interp, refPtr->objPtr);
    return JIM_OK;
}


static int Jim_SetrefCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
    Jim_Reference *refPtr;

    if (argc != 3) {
        Jim_WrongNumArgs(interp, 1, argv, "reference newValue");
        return JIM_ERR;
    }
    if ((refPtr = Jim_GetReference(interp, argv[1])) == NULL)
        return JIM_ERR;
    Jim_IncrRefCount(argv[2]);
    Jim_DecrRefCount(interp, refPtr->objPtr);
    refPtr->objPtr = argv[2];
    Jim_SetResult(interp, argv[2]);
    return JIM_OK;
}


static int Jim_CollectCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
    if (argc != 1) {
        Jim_WrongNumArgs(interp, 1, argv, "");
        return JIM_ERR;
    }
    Jim_SetResultInt(interp, Jim_Collect(interp));

    
    while (interp->freeList) {
        Jim_Obj *nextObjPtr = interp->freeList->nextObjPtr;
        Jim_Free(interp->freeList);
        interp->freeList = nextObjPtr;
    }

    return JIM_OK;
}


static int Jim_FinalizeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
    if (argc != 2 && argc != 3) {
        Jim_WrongNumArgs(interp, 1, argv, "reference ?finalizerProc?");
        return JIM_ERR;
    }
    if (argc == 2) {
        Jim_Obj *cmdNamePtr;

        if (Jim_GetFinalizer(interp, argv[1], &cmdNamePtr) != JIM_OK)
            return JIM_ERR;
        if (cmdNamePtr != NULL) 
            Jim_SetResult(interp, cmdNamePtr);
    }
    else {
        if (Jim_SetFinalizer(interp, argv[1], argv[2]) != JIM_OK)
            return JIM_ERR;
        Jim_SetResult(interp, argv[2]);
    }
    return JIM_OK;
}


static int JimInfoReferences(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
    Jim_Obj *listObjPtr;
    Jim_HashTableIterator htiter;
    Jim_HashEntry *he;

    listObjPtr = Jim_NewListObj(interp, NULL, 0);

    JimInitHashTableIterator(&interp->references, &htiter);
    while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
        char buf[JIM_REFERENCE_SPACE + 1];
        Jim_Reference *refPtr = Jim_GetHashEntryVal(he);
        const unsigned long *refId = he->key;

        JimFormatReference(buf, refPtr, *refId);
        Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, buf, -1));
    }
    Jim_SetResult(interp, listObjPtr);
    return JIM_OK;
}
#endif


static int Jim_RenameCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
    if (argc != 3) {
        Jim_WrongNumArgs(interp, 1, argv, "oldName newName");
        return JIM_ERR;
    }

    if (JimValidName(interp, "new procedure", argv[2])) {
        return JIM_ERR;
    }

    return Jim_RenameCommand(interp, Jim_String(argv[1]), Jim_String(argv[2]));
}

#define JIM_DICTMATCH_VALUES 0x0001
#define JIM_DICTMATCH_KEYS 0x0001

typedef void JimDictMatchCallbackType(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_HashEntry *he, int type);

static void JimDictMatchKeys(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_HashEntry *he, int type)
{
    Jim_ListAppendElement(interp, listObjPtr, (Jim_Obj *)he->key);
    if (type & JIM_DICTMATCH_VALUES) {
#define JIM_DICTMATCH_VALUES 0x002
        Jim_ListAppendElement(interp, listObjPtr, Jim_GetHashEntryVal(he));
    }

}

int Jim_DictMatchTypes(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *patternObj, int match_type, int return_types)
static Jim_Obj *JimDictPatternMatch(Jim_Interp *interp, Jim_HashTable *ht, Jim_Obj *patternObjPtr,
    JimDictMatchCallbackType *callback, int type)
{
    Jim_HashEntry *he;
    Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
    Jim_Obj *listObjPtr;
    Jim_HashTableIterator htiter;

    if (SetDictFromAny(interp, objPtr) != JIM_OK) {
        return JIM_ERR;
    
    Jim_HashTableIterator htiter;
    }

    JimInitHashTableIterator(ht, &htiter);
    while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
    listObjPtr = Jim_NewListObj(interp, NULL, 0);
        if (patternObjPtr == NULL || JimGlobMatch(Jim_String(patternObjPtr), Jim_String((Jim_Obj *)he->key), 0)) {
            callback(interp, listObjPtr, he, type);
        }
    }


    JimInitHashTableIterator(objPtr->internalRep.ptr, &htiter);
    while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
        if (patternObj) {
            Jim_Obj *matchObj = (match_type == JIM_DICTMATCH_KEYS) ? (Jim_Obj *)he->key : Jim_GetHashEntryVal(he);
            if (!JimGlobMatch(Jim_String(patternObj), Jim_String(matchObj), 0)) {

                continue;
    return listObjPtr;
}


            }
        }
        if (return_types & JIM_DICTMATCH_KEYS) {
int Jim_DictKeys(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *patternObjPtr)
{
    if (SetDictFromAny(interp, objPtr) != JIM_OK) {
            Jim_ListAppendElement(interp, listObjPtr, (Jim_Obj *)he->key);
        return JIM_ERR;
    }
        }
    Jim_SetResult(interp, JimDictPatternMatch(interp, objPtr->internalRep.ptr, patternObjPtr, JimDictMatchKeys, 0));
    return JIM_OK;
}

        if (return_types & JIM_DICTMATCH_VALUES) {
            Jim_ListAppendElement(interp, listObjPtr, Jim_GetHashEntryVal(he));
        }
    }
int Jim_DictValues(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *patternObjPtr)
{

    if (SetDictFromAny(interp, objPtr) != JIM_OK) {
        return JIM_ERR;
    }
    Jim_SetResult(interp, JimDictPatternMatch(interp, objPtr->internalRep.ptr, patternObjPtr, JimDictMatchKeys, JIM_DICTMATCH_VALUES));
    Jim_SetResult(interp, listObjPtr);
    return JIM_OK;
}

int Jim_DictSize(Jim_Interp *interp, Jim_Obj *objPtr)
{
    if (SetDictFromAny(interp, objPtr) != JIM_OK) {
        return -1;
    }
    return ((Jim_HashTable *)objPtr->internalRep.ptr)->used;
}

Jim_Obj *Jim_DictMerge(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
{
    Jim_Obj *objPtr = Jim_NewDictObj(interp, NULL, 0);
    int i;

    JimPanic((objc == 0, "Jim_DictMerge called with objc=0"));



    for (i = 0; i < objc; i++) {
        Jim_HashTable *ht;
        Jim_HashTableIterator htiter;
        Jim_HashEntry *he;

        if (SetDictFromAny(interp, objv[i]) != JIM_OK) {
            Jim_FreeNewObj(interp, objPtr);
            return NULL;
        }
        ht = objv[i]->internalRep.ptr;
        JimInitHashTableIterator(ht, &htiter);
        while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
            Jim_ReplaceHashEntry(objPtr->internalRep.ptr, Jim_GetHashEntryKey(he), Jim_GetHashEntryVal(he));
        }
    }
    return objPtr;
}

int Jim_DictInfo(Jim_Interp *interp, Jim_Obj *objPtr)
{
    Jim_HashTable *ht;
    unsigned int i;
    char buffer[100];
    int sum = 0;
    int nonzero_count = 0;
    Jim_Obj *output;
    int bucket_counts[11] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

    if (SetDictFromAny(interp, objPtr) != JIM_OK) {
        return JIM_ERR;
    }

    ht = (Jim_HashTable *)objPtr->internalRep.ptr;

    
    printf("%d entries in table, %d buckets\n", ht->used, ht->size);

    snprintf(buffer, sizeof(buffer), "%d entries in table, %d buckets\n", ht->used, ht->size);
    output = Jim_NewStringObj(interp, buffer, -1);

    for (i = 0; i < ht->size; i++) {
        Jim_HashEntry *he = ht->table[i];
        int entries = 0;
        while (he) {
            entries++;
            he = he->next;

        if (he) {
            printf("%d: ", i);

            while (he) {
                printf(" %s", Jim_String(he->key));
                he = he->next;
            }
        }
        if (entries > 9) {
            bucket_counts[10]++;
        }
        else {
            bucket_counts[entries]++;
        }
        if (entries) {
            sum += entries;
            nonzero_count++;
        }
            printf("\n");
        }
    }
    for (i = 0; i < 10; i++) {
        snprintf(buffer, sizeof(buffer), "number of buckets with %d entries: %d\n", i, bucket_counts[i]);
        Jim_AppendString(interp, output, buffer, -1);
    }
    snprintf(buffer, sizeof(buffer), "number of buckets with 10 or more entries: %d\n", bucket_counts[10]);
    Jim_AppendString(interp, output, buffer, -1);
    snprintf(buffer, sizeof(buffer), "average search distance for entry: %.1f", nonzero_count ? (double)sum / nonzero_count : 0.0);
    Jim_AppendString(interp, output, buffer, -1);
    Jim_SetResult(interp, output);
    return JIM_OK;
}

static int Jim_EvalEnsemble(Jim_Interp *interp, const char *basecmd, const char *subcmd, int argc, Jim_Obj *const *argv)
{
    Jim_Obj *prefixObj = Jim_NewStringObj(interp, basecmd, -1);

    Jim_AppendString(interp, prefixObj, " ", 1);
    Jim_AppendString(interp, prefixObj, subcmd, -1);

    return Jim_EvalObjPrefix(interp, prefixObj, argc, argv);
}

static int JimDictWith(Jim_Interp *interp, Jim_Obj *dictVarName, Jim_Obj *const *keyv, int keyc, Jim_Obj *scriptObj)

{
    int i;
    Jim_Obj *objPtr;
    Jim_Obj *dictObj;
    Jim_Obj **dictValues;
    int len;
    int ret = JIM_OK;


    dictObj = Jim_GetVariable(interp, dictVarName, JIM_ERRMSG);
    if (dictObj == NULL || Jim_DictKeysVector(interp, dictObj, keyv, keyc, &objPtr, JIM_ERRMSG) != JIM_OK) {
        return JIM_ERR;
    }

    if (Jim_DictPairs(interp, objPtr, &dictValues, &len) == JIM_ERR) {
        return JIM_ERR;
    }
    for (i = 0; i < len; i += 2) {
        if (Jim_SetVariable(interp, dictValues[i], dictValues[i + 1]) == JIM_ERR) {
            Jim_Free(dictValues);
            return JIM_ERR;
        }
    }


    if (Jim_Length(scriptObj)) {
        ret = Jim_EvalObj(interp, scriptObj);


        if (ret == JIM_OK && Jim_GetVariable(interp, dictVarName, 0) != NULL) {

            Jim_Obj **newkeyv = Jim_Alloc(sizeof(*newkeyv) * (keyc + 1));
            for (i = 0; i < keyc; i++) {
                newkeyv[i] = keyv[i];
            }

            for (i = 0; i < len; i += 2) {

                objPtr = Jim_GetVariable(interp, dictValues[i], 0);
                newkeyv[keyc] = dictValues[i];
                Jim_SetDictKeysVector(interp, dictVarName, newkeyv, keyc + 1, objPtr, 0);
            }
            Jim_Free(newkeyv);
        }
    }

    Jim_Free(dictValues);

    return ret;
}


static int Jim_DictCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
    Jim_Obj *objPtr;
    int types = JIM_DICTMATCH_KEYS;
    int option;
    static const char * const options[] = {
        "create", "get", "set", "unset", "exists", "keys", "size", "info",
        "merge", "with", "append", "lappend", "incr", "remove", "values", "for",
        "replace", "update", NULL
    };
    enum
    {
        OPT_CREATE, OPT_GET, OPT_SET, OPT_UNSET, OPT_EXISTS, OPT_KEYS, OPT_SIZE, OPT_INFO,
        OPT_MERGE, OPT_WITH, OPT_APPEND, OPT_LAPPEND, OPT_INCR, OPT_REMOVE, OPT_VALUES, OPT_FOR,
        OPT_REPLACE, OPT_UPDATE,
    };

    if (argc < 2) {
        Jim_WrongNumArgs(interp, 1, argv, "subcommand ?arguments ...?");
        return JIM_ERR;
    }

    if (Jim_GetEnum(interp, argv[1], options, &option, "subcommand", JIM_ERRMSG) != JIM_OK) {
        return JIM_ERR;
        return Jim_CheckShowCommands(interp, argv[1], options);
    }

    switch (option) {
        case OPT_GET:
            if (argc < 3) {
                Jim_WrongNumArgs(interp, 2, argv, "dictionary ?key ...?");
                return JIM_ERR;
18641
18642
18643
18644
18645
18646
18647



18648
18649
18650
18651
18652
18653

18654
18655
18656
18657
18658
18659
18660
18661
18662
18663
18664
18665
18666
18667
18668
18669
18670


18671
18672
18673
18674


18675
18676
18677
18678

18679
18680
18681
18682
18683
18684
18685
18686
18687
18688
18689
18690
18691
18692
18693
18694
18695
18696
18697
18698
18699









18700
18701
18702
18703
18704
18705
18706
18472
18473
18474
18475
18476
18477
18478
18479
18480
18481
18482
18483
18484
18485
18486

18487
18488
18489
18490
18491
18492
18493
18494
18495
18496
18497
18498
18499
18500
18501
18502
18503

18504
18505
18506
18507


18508
18509
18510
18511
18512

18513
18514
18515
18516
18517
18518
18519
18520
18521
18522
18523
18524
18525
18526
18527
18528
18529
18530
18531
18532


18533
18534
18535
18536
18537
18538
18539
18540
18541
18542
18543
18544
18545
18546
18547
18548







+
+
+





-
+
















-
+
+


-
-
+
+



-
+



















-
-
+
+
+
+
+
+
+
+
+







                return JIM_ERR;
            }
            if (Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 3, NULL, 0) != JIM_OK) {
                return JIM_ERR;
            }
            return JIM_OK;

        case OPT_VALUES:
            types = JIM_DICTMATCH_VALUES;

        case OPT_KEYS:
            if (argc != 3 && argc != 4) {
                Jim_WrongNumArgs(interp, 2, argv, "dictionary ?pattern?");
                return JIM_ERR;
            }
            return Jim_DictKeys(interp, argv[2], argc == 4 ? argv[3] : NULL);
            return Jim_DictMatchTypes(interp, argv[2], argc == 4 ? argv[3] : NULL, types, types);

        case OPT_SIZE:
            if (argc != 3) {
                Jim_WrongNumArgs(interp, 2, argv, "dictionary");
                return JIM_ERR;
            }
            else if (Jim_DictSize(interp, argv[2]) < 0) {
                return JIM_ERR;
            }
            Jim_SetResultInt(interp, Jim_DictSize(interp, argv[2]));
            return JIM_OK;

        case OPT_MERGE:
            if (argc == 2) {
                return JIM_OK;
            }
            if (Jim_DictSize(interp, argv[2]) < 0) {
            objPtr = Jim_DictMerge(interp, argc - 2, argv + 2);
            if (objPtr == NULL) {
                return JIM_ERR;
            }
            
            break;
            Jim_SetResult(interp, objPtr);
            return JIM_OK;

        case OPT_UPDATE:
            if (argc < 6 || argc % 2) {
                

                argc = 2;
            }
            break;

        case OPT_CREATE:
            if (argc % 2) {
                Jim_WrongNumArgs(interp, 2, argv, "?key value ...?");
                return JIM_ERR;
            }
            objPtr = Jim_NewDictObj(interp, argv + 2, argc - 2);
            Jim_SetResult(interp, objPtr);
            return JIM_OK;

        case OPT_INFO:
            if (argc != 3) {
                Jim_WrongNumArgs(interp, 2, argv, "dictionary");
                return JIM_ERR;
            }
            return Jim_DictInfo(interp, argv[2]);
    }
    

        case OPT_WITH:
            if (argc < 4) {
                Jim_WrongNumArgs(interp, 2, argv, "dictVar ?key ...? script");
                return JIM_ERR;
            }
            return JimDictWith(interp, argv[2], argv + 3, argc - 4, argv[argc - 1]);
    }

    return Jim_EvalEnsemble(interp, "dict", options[option], argc - 2, argv + 2);
}


static int Jim_SubstCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
    static const char * const options[] = {
18762
18763
18764
18765
18766
18767
18768
18769

18770
18771
18772
18773
18774
18775
18776
18777
18778
18779
18780

18781
18782

18783
18784
18785

18786
18787
18788
18789
18790
18791
18792
18604
18605
18606
18607
18608
18609
18610

18611
18612
18613
18614
18615
18616
18617
18618
18619
18620
18621

18622


18623
18624
18625

18626
18627
18628
18629
18630
18631
18632
18633







-
+










-
+
-
-
+


-
+







        INFO_RETURNCODES, INFO_REFERENCES, INFO_ALIAS,
    };

#ifdef jim_ext_namespace
    int nons = 0;

    if (argc > 2 && Jim_CompareStringImmediate(interp, argv[1], "-nons")) {
        

        argc--;
        argv++;
        nons = 1;
    }
#endif

    if (argc < 2) {
        Jim_WrongNumArgs(interp, 1, argv, "subcommand ?args ...?");
        return JIM_ERR;
    }
    if (Jim_GetEnum(interp, argv[1], commands, &cmd, "subcommand", JIM_ERRMSG | JIM_ENUM_ABBREV)
    if (Jim_GetEnum(interp, argv[1], commands, &cmd, "subcommand", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
        != JIM_OK) {
        return JIM_ERR;
        return Jim_CheckShowCommands(interp, argv[1], commands);
    }

    

    switch (cmd) {
        case INFO_EXISTS:
            if (argc != 3) {
                Jim_WrongNumArgs(interp, 2, argv, "varName");
                return JIM_ERR;
            }
            Jim_SetResultBool(interp, Jim_GetVariable(interp, argv[2], 0) != NULL);
18807
18808
18809
18810
18811
18812
18813
18814

18815
18816
18817
18818
18819

18820
18821
18822


18823
18824

18825
18826
18827
18828
18829
18830
18831
18832
18833
18834
18835
18836
18837
18838
18839
18840
18841


18842
18843
18844


18845
18846

18847
18848
18849
18850
18851
18852
18853
18648
18649
18650
18651
18652
18653
18654

18655
18656
18657
18658
18659

18660
18661


18662
18663
18664

18665
18666
18667
18668
18669
18670
18671
18672
18673
18674
18675
18676
18677
18678
18679
18680


18681
18682
18683


18684
18685
18686

18687
18688
18689
18690
18691
18692
18693
18694







-
+




-
+

-
-
+
+

-
+















-
-
+
+

-
-
+
+

-
+







                return JIM_ERR;
            }
            Jim_SetResult(interp, (Jim_Obj *)cmdPtr->u.native.privData);
            return JIM_OK;
        }

        case INFO_CHANNELS:
            mode++;             
            mode++;
#ifndef jim_ext_aio
            Jim_SetResultString(interp, "aio not enabled", -1);
            return JIM_ERR;
#endif
            

        case INFO_PROCS:
            mode++;             
            
            mode++;

        case INFO_COMMANDS:
            

            if (argc != 2 && argc != 3) {
                Jim_WrongNumArgs(interp, 2, argv, "?pattern?");
                return JIM_ERR;
            }
#ifdef jim_ext_namespace
            if (!nons) {
                if (Jim_Length(interp->framePtr->nsObj) || (argc == 3 && JimGlobMatch("::*", Jim_String(argv[2]), 0))) {
                    return Jim_EvalPrefix(interp, "namespace info", argc - 1, argv + 1);
                }
            }
#endif
            Jim_SetResult(interp, JimCommandsList(interp, (argc == 3) ? argv[2] : NULL, mode));
            break;

        case INFO_VARS:
            mode++;             
            
            mode++;

        case INFO_LOCALS:
            mode++;             
            
            mode++;

        case INFO_GLOBALS:
            

            if (argc != 2 && argc != 3) {
                Jim_WrongNumArgs(interp, 2, argv, "?pattern?");
                return JIM_ERR;
            }
#ifdef jim_ext_namespace
            if (!nons) {
                if (Jim_Length(interp->framePtr->nsObj) || (argc == 3 && JimGlobMatch("::*", Jim_String(argv[2]), 0))) {
18949
18950
18951
18952
18953
18954
18955
18956
18957
18958

18959
18960
18961
18962
18963
18964
18965
18790
18791
18792
18793
18794
18795
18796

18797

18798
18799
18800
18801
18802
18803
18804
18805







-

-
+







                        Jim_SetResult(interp, cmdPtr->u.proc.bodyObjPtr);
                        break;
                    case INFO_ARGS:
                        Jim_SetResult(interp, cmdPtr->u.proc.argListObjPtr);
                        break;
                    case INFO_STATICS:
                        if (cmdPtr->u.proc.staticVars) {
                            int mode = JIM_VARLIST_LOCALS | JIM_VARLIST_VALUES;
                            Jim_SetResult(interp, JimHashtablePatternMatch(interp, cmdPtr->u.proc.staticVars,
                                NULL, JimVariablesMatch, mode));
                                NULL, JimVariablesMatch, JIM_VARLIST_LOCALS | JIM_VARLIST_VALUES));
                        }
                        break;
                }
                break;
            }

        case INFO_VERSION:
18983
18984
18985
18986
18987
18988
18989
18990

18991
18992
18993
18994

18995
18996
18997
18998
18999
19000
19001
18823
18824
18825
18826
18827
18828
18829

18830
18831
18832
18833

18834
18835
18836
18837
18838
18839
18840
18841







-
+



-
+







                if (missing != ' ' && argc == 4) {
                    Jim_SetVariable(interp, argv[3], Jim_NewStringObj(interp, &missing, 1));
                }
            }
            break;

        case INFO_HOSTNAME:
            

            return Jim_Eval(interp, "os.gethostname");

        case INFO_NAMEOFEXECUTABLE:
            

            return Jim_Eval(interp, "{info nameofexecutable}");

        case INFO_RETURNCODES:
            if (argc == 2) {
                int i;
                Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);

19068
19069
19070
19071
19072
19073
19074
19075

19076
19077
19078
19079
19080
19081
19082
18908
18909
18910
18911
18912
18913
18914

18915
18916
18917
18918
18919
18920
18921
18922







-
+







        return JIM_ERR;
    }

    if (option == OPT_VAR) {
        result = Jim_GetVariable(interp, objPtr, 0) != NULL;
    }
    else {
        

        Jim_Cmd *cmd = Jim_GetCommand(interp, objPtr, JIM_NONE);

        if (cmd) {
            switch (option) {
            case OPT_COMMAND:
                result = 1;
                break;
19111
19112
19113
19114
19115
19116
19117
19118

19119
19120
19121
19122
19123
19124
19125
19126
19127
19128
19129
19130
19131

19132
19133
19134
19135
19136
19137
19138
18951
18952
18953
18954
18955
18956
18957

18958
18959
18960
18961
18962
18963
18964
18965
18966
18967
18968
18969
18970

18971
18972
18973
18974
18975
18976
18977
18978







-
+












-
+








    str = Jim_GetString(argv[1], &len);
    if (len == 0) {
        return JIM_OK;
    }
    strLen = Jim_Utf8Length(interp, argv[1]);

    

    if (argc == 2) {
        splitChars = " \n\t\r";
        splitLen = 4;
    }
    else {
        splitChars = Jim_String(argv[2]);
        splitLen = Jim_Utf8Length(interp, argv[2]);
    }

    noMatchStart = str;
    resObjPtr = Jim_NewListObj(interp, NULL, 0);

    

    if (splitLen) {
        Jim_Obj *objPtr;
        while (strLen--) {
            const char *sc = splitChars;
            int scLen = splitLen;
            int sl = utf8_tounicode(str, &c);
            while (scLen--) {
19153
19154
19155
19156
19157
19158
19159
19160

19161
19162
19163
19164
19165
19166
19167
18993
18994
18995
18996
18997
18998
18999

19000
19001
19002
19003
19004
19005
19006
19007







-
+







    else {
        Jim_Obj **commonObj = NULL;
#define NUM_COMMON (128 - 9)
        while (strLen--) {
            int n = utf8_tounicode(str, &c);
#ifdef JIM_OPTIMIZATION
            if (c >= 9 && c < 128) {
                

                c -= 9;
                if (!commonObj) {
                    commonObj = Jim_Alloc(sizeof(*commonObj) * NUM_COMMON);
                    memset(commonObj, 0, sizeof(*commonObj) * NUM_COMMON);
                }
                if (!commonObj[c]) {
                    commonObj[c] = Jim_NewStringObj(interp, str, 1);
19187
19188
19189
19190
19191
19192
19193
19194

19195
19196
19197
19198
19199
19200
19201
19027
19028
19029
19030
19031
19032
19033

19034
19035
19036
19037
19038
19039
19040
19041







-
+







    const char *joinStr;
    int joinStrLen;

    if (argc != 2 && argc != 3) {
        Jim_WrongNumArgs(interp, 1, argv, "list ?joinString?");
        return JIM_ERR;
    }
    

    if (argc == 2) {
        joinStr = " ";
        joinStrLen = 1;
    }
    else {
        joinStr = Jim_GetString(argv[2], &joinStrLen);
    }
19466
19467
19468
19469
19470
19471
19472
19473

19474
19475

19476
19477
19478
19479
19480
19481
19482
19306
19307
19308
19309
19310
19311
19312

19313
19314

19315
19316
19317
19318
19319
19320
19321
19322







-
+

-
+







        return 0;
    else if (step > 0 && start > end)
        return -1;
    else if (step < 0 && end > start)
        return -1;
    len = end - start;
    if (len < 0)
        len = -len;             
        len = -len;
    if (step < 0)
        step = -step;           
        step = -step;
    len = 1 + ((len - 1) / step);
    if (len > INT_MAX)
        len = INT_MAX;
    return (int)((len < 0) ? -1 : len);
}


19642
19643
19644
19645
19646
19647
19648
19649
19650

19651
19652
19653
19654

19655
19656
19657
19658















19659
19660
19661
19662
19663
19664
19665
19666



19667
19668
19669
19670
19671
19672

19673
19674
19675
19676
19677
19678
























19679
19680
19681
19682
19683
19684
19685
19686









19687

19688
19689
19690
19691
19692
19693
19694
19695



19696
19697
19698
19699
19700
19701
19702
19703
19704
19705
19706
19707
19708
19709
19710
19711

19712








19713
19714
19715
19716
19717
19718
19719
19482
19483
19484
19485
19486
19487
19488


19489
19490
19491
19492

19493
19494
19495
19496
19497
19498
19499
19500
19501
19502
19503
19504
19505
19506
19507
19508
19509
19510
19511
19512
19513
19514
19515
19516
19517



19518
19519
19520


19521
19522
19523

19524
19525
19526
19527
19528
19529
19530
19531
19532
19533
19534
19535
19536
19537
19538
19539
19540
19541
19542
19543
19544
19545
19546
19547
19548
19549
19550
19551
19552
19553
19554
19555
19556
19557
19558
19559
19560
19561
19562
19563
19564
19565
19566
19567
19568
19569
19570
19571

19572
19573
19574
19575
19576
19577



19578
19579
19580
19581
19582
19583
19584
19585
19586
19587
19588
19589
19590
19591
19592
19593
19594
19595

19596
19597
19598
19599
19600
19601
19602
19603
19604
19605
19606
19607
19608
19609
19610
19611
19612







-
-
+



-
+




+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





-
-
-
+
+
+
-
-



-
+






+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+








+
+
+
+
+
+
+
+
+
-
+





-
-
-
+
+
+















-
+

+
+
+
+
+
+
+
+








    argv[0] = Jim_NewStringObj(interp, "errorInfo", -1);
    argv[1] = interp->result;

    Jim_EvalObjVector(interp, 2, argv);
}

static void JimSetFailedEnumResult(Jim_Interp *interp, const char *arg, const char *badtype,
    const char *prefix, const char *const *tablePtr, const char *name)
static char **JimSortStringTable(const char *const *tablePtr)
{
    int count;
    char **tablePtrSorted;
    int i;


    for (count = 0; tablePtr[count]; count++) {
    }


    tablePtrSorted = Jim_Alloc(sizeof(char *) * (count + 1));
    memcpy(tablePtrSorted, tablePtr, sizeof(char *) * count);
    qsort(tablePtrSorted, count, sizeof(char *), qsortCompareStringPointers);
    tablePtrSorted[count] = NULL;

    return tablePtrSorted;
}

static void JimSetFailedEnumResult(Jim_Interp *interp, const char *arg, const char *badtype,
    const char *prefix, const char *const *tablePtr, const char *name)
{
    char **tablePtrSorted;
    int i;

    if (name == NULL) {
        name = "option";
    }

    Jim_SetResultFormatted(interp, "%s%s \"%s\": must be ", badtype, name, arg);
    tablePtrSorted = Jim_Alloc(sizeof(char *) * count);
    memcpy(tablePtrSorted, tablePtr, sizeof(char *) * count);
    qsort(tablePtrSorted, count, sizeof(char *), qsortCompareStringPointers);
    tablePtrSorted = JimSortStringTable(tablePtr);
    for (i = 0; tablePtrSorted[i]; i++) {
        if (tablePtrSorted[i + 1] == NULL && i > 0) {
    for (i = 0; i < count; i++) {
        if (i + 1 == count && count > 1) {
            Jim_AppendString(interp, Jim_GetResult(interp), "or ", -1);
        }
        Jim_AppendStrings(interp, Jim_GetResult(interp), prefix, tablePtrSorted[i], NULL);
        if (i + 1 != count) {
        if (tablePtrSorted[i + 1]) {
            Jim_AppendString(interp, Jim_GetResult(interp), ", ", -1);
        }
    }
    Jim_Free(tablePtrSorted);
}


int Jim_CheckShowCommands(Jim_Interp *interp, Jim_Obj *objPtr, const char *const *tablePtr)
{
    if (Jim_CompareStringImmediate(interp, objPtr, "-commands")) {
        int i;
        char **tablePtrSorted = JimSortStringTable(tablePtr);
        Jim_SetResult(interp, Jim_NewListObj(interp, NULL, 0));
        for (i = 0; tablePtrSorted[i]; i++) {
            Jim_ListAppendElement(interp, Jim_GetResult(interp), Jim_NewStringObj(interp, tablePtrSorted[i], -1));
        }
        Jim_Free(tablePtrSorted);
        return JIM_OK;
    }
    return JIM_ERR;
}

static const Jim_ObjType getEnumObjType = {
    "get-enum",
    NULL,
    NULL,
    NULL,
    JIM_TYPE_REFERENCES
};

int Jim_GetEnum(Jim_Interp *interp, Jim_Obj *objPtr,
    const char *const *tablePtr, int *indexPtr, const char *name, int flags)
{
    const char *bad = "bad ";
    const char *const *entryPtr = NULL;
    int i;
    int match = -1;
    int arglen;
    const char *arg;

    if (objPtr->typePtr == &getEnumObjType) {
        if (objPtr->internalRep.ptrIntValue.ptr == tablePtr && objPtr->internalRep.ptrIntValue.int1 == flags) {
            *indexPtr = objPtr->internalRep.ptrIntValue.int2;
            return JIM_OK;
        }
    }

    const char *arg = Jim_GetString(objPtr, &arglen);
    arg = Jim_GetString(objPtr, &arglen);

    *indexPtr = -1;

    for (entryPtr = tablePtr, i = 0; *entryPtr != NULL; entryPtr++, i++) {
        if (Jim_CompareStringImmediate(interp, objPtr, *entryPtr)) {
            
            *indexPtr = i;
            return JIM_OK;

            match = i;
            goto found;
        }
        if (flags & JIM_ENUM_ABBREV) {
            if (strncmp(arg, *entryPtr, arglen) == 0) {
                if (*arg == '-' && arglen == 1) {
                    break;
                }
                if (match >= 0) {
                    bad = "ambiguous ";
                    goto ambiguous;
                }
                match = i;
            }
        }
    }

    

    if (match >= 0) {
  found:

        Jim_FreeIntRep(interp, objPtr);
        objPtr->typePtr = &getEnumObjType;
        objPtr->internalRep.ptrIntValue.ptr = (void *)tablePtr;
        objPtr->internalRep.ptrIntValue.int1 = flags;
        objPtr->internalRep.ptrIntValue.int2 = match;

        *indexPtr = match;
        return JIM_OK;
    }

  ambiguous:
    if (flags & JIM_ERRMSG) {
        JimSetFailedEnumResult(interp, arg, bad, "", tablePtr, name);
19741
19742
19743
19744
19745
19746
19747
19748

19749
19750
19751
19752


19753
19754
19755
19756
19757
19758
19759
19760
19761
19762
19763
19764
19765
19766
19767
19768
19769
19770


19771
19772
19773
19774
19775
19776
19777
19778
19779
19780
19781
19782
19783
19784
19785
19786
19787
19788




19789
19790
19791
19792
19793
19794
19795
19634
19635
19636
19637
19638
19639
19640

19641
19642
19643
19644
19645
19646
19647
19648
19649
19650
19651
19652
19653
19654
19655
19656
19657
19658
19659
19660
19661
19662
19663
19664
19665
19666
19667
19668
19669
19670
19671
19672
19673
19674
19675
19676
19677
19678
19679
19680
19681
19682
19683
19684
19685
19686
19687
19688
19689
19690
19691
19692
19693
19694
19695
19696







-
+




+
+


















+
+


















+
+
+
+







int Jim_IsList(Jim_Obj *objPtr)
{
    return objPtr->typePtr == &listObjType;
}

void Jim_SetResultFormatted(Jim_Interp *interp, const char *format, ...)
{
    

    int len = strlen(format);
    int extra = 0;
    int n = 0;
    const char *params[5];
    int nobjparam = 0;
    Jim_Obj *objparam[5];
    char *buf;
    va_list args;
    int i;

    va_start(args, format);

    for (i = 0; i < len && n < 5; i++) {
        int l;

        if (strncmp(format + i, "%s", 2) == 0) {
            params[n] = va_arg(args, char *);

            l = strlen(params[n]);
        }
        else if (strncmp(format + i, "%#s", 3) == 0) {
            Jim_Obj *objPtr = va_arg(args, Jim_Obj *);

            params[n] = Jim_GetString(objPtr, &l);
            objparam[nobjparam++] = objPtr;
            Jim_IncrRefCount(objPtr);
        }
        else {
            if (format[i] == '%') {
                i++;
            }
            continue;
        }
        n++;
        extra += l;
    }

    len += extra;
    buf = Jim_Alloc(len + 1);
    len = snprintf(buf, len + 1, format, params[0], params[1], params[2], params[3], params[4]);

    va_end(args);

    Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, len));

    for (i = 0; i < nobjparam; i++) {
        Jim_DecrRefCount(interp, objparam[i]);
    }
}


#ifndef jim_ext_package
int Jim_PackageProvide(Jim_Interp *interp, const char *name, const char *ver, int flags)
{
    return JIM_OK;
19806
19807
19808
19809
19810
19811
19812
19813

19814
19815
19816
19817
19818
19819
19820
19707
19708
19709
19710
19711
19712
19713

19714
19715
19716
19717
19718
19719
19720
19721







-
+








#include <stdio.h>
#include <string.h>


static int subcmd_null(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
    

    return JIM_OK;
}

static const jim_subcmd_type dummy_subcmd = {
    "dummy", NULL, subcmd_null, 0, 0, JIM_MODFLAG_HIDDEN
};

19829
19830
19831
19832
19833
19834
19835
19836
19837
19838

19839
19840
19841
19842
19843
19844
19845
19846
19847

19848
19849
19850
19851
19852
19853
19854
19855
19856
19857
19858
19859
19860
19861
19862
19863
19864
19865
19866
19867








19868
19869
19870
19871
19872
19873
19874
19875
19876
19877
19878
19879
19880
19881
19882
19883
19884

19885
19886

19887
19888
19889
19890
19891
19892









19893
19894
19895

19896
19897
19898
19899
19900
19901

19902
19903
19904
19905

19906
19907

19908
19909
19910
19911
19912
19913
19914
19915
19916
19917

19918
19919
19920
19921
19922

19923
19924

19925
19926
19927
19928
19929
19930
19931
19932
19933
19934
19935
19936

19937
19938
19939
19940
19941
19942

19943
19944

19945
19946
19947
19948
19949
19950
19951
19952
19953
19954

19955
19956
19957
19958
19959








19960
19961
19962

19963
19964
19965
19966
19967
19968
19969

19970
19971
19972
19973
19974
19975
19976
19730
19731
19732
19733
19734
19735
19736



19737
19738
19739
19740
19741
19742
19743



19744
19745
19746
19747
19748
19749
19750
19751
19752
19753
19754
19755
19756
19757
19758
19759
19760
19761
19762
19763
19764
19765
19766
19767
19768
19769
19770
19771
19772
19773
19774
19775
19776
19777
19778
19779
19780
19781

19782
19783


19784


19785


19786
19787
19788
19789
19790
19791

19792
19793
19794
19795
19796
19797
19798
19799
19800
19801
19802

19803
19804
19805
19806
19807
19808

19809
19810
19811
19812

19813
19814

19815
19816
19817
19818
19819
19820
19821
19822
19823
19824

19825
19826
19827
19828
19829

19830
19831

19832
19833
19834
19835
19836
19837
19838
19839
19840
19841
19842
19843

19844
19845
19846
19847
19848
19849

19850
19851

19852
19853
19854
19855
19856
19857
19858
19859
19860
19861

19862
19863
19864
19865
19866

19867
19868
19869
19870
19871
19872
19873
19874
19875
19876

19877
19878
19879
19880
19881
19882
19883

19884
19885
19886
19887
19888
19889
19890
19891







-
-
-
+






-
-
-
+




















+
+
+
+
+
+
+
+









-


-
-

-
-
+
-
-
+





-
+
+
+
+
+
+
+
+
+


-
+





-
+



-
+

-
+









-
+




-
+

-
+











-
+





-
+

-
+









-
+




-
+
+
+
+
+
+
+
+


-
+






-
+







        }
    }
}

static void bad_subcmd(Jim_Interp *interp, const jim_subcmd_type * command_table, const char *type,
    Jim_Obj *cmd, Jim_Obj *subcmd)
{
    Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
    Jim_AppendStrings(interp, Jim_GetResult(interp), Jim_String(cmd), ", ", type,
        " command \"", Jim_String(subcmd), "\": should be ", NULL);
    Jim_SetResultFormatted(interp, "%#s, %s command \"%#s\": should be ", cmd, type, subcmd);
    add_commands(interp, command_table, ", ");
}

static void show_cmd_usage(Jim_Interp *interp, const jim_subcmd_type * command_table, int argc,
    Jim_Obj *const *argv)
{
    Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
    Jim_AppendStrings(interp, Jim_GetResult(interp), "Usage: \"", Jim_String(argv[0]),
        " command ... \", where command is one of: ", NULL);
    Jim_SetResultFormatted(interp, "Usage: \"%#s command ... \", where command is one of: ", argv[0]);
    add_commands(interp, command_table, ", ");
}

static void add_cmd_usage(Jim_Interp *interp, const jim_subcmd_type * ct, Jim_Obj *cmd)
{
    if (cmd) {
        Jim_AppendStrings(interp, Jim_GetResult(interp), Jim_String(cmd), " ", NULL);
    }
    Jim_AppendStrings(interp, Jim_GetResult(interp), ct->cmd, NULL);
    if (ct->args && *ct->args) {
        Jim_AppendStrings(interp, Jim_GetResult(interp), " ", ct->args, NULL);
    }
}

static void set_wrong_args(Jim_Interp *interp, const jim_subcmd_type * command_table, Jim_Obj *subcmd)
{
    Jim_SetResultString(interp, "wrong # args: should be \"", -1);
    add_cmd_usage(interp, command_table, subcmd);
    Jim_AppendStrings(interp, Jim_GetResult(interp), "\"", NULL);
}

static const Jim_ObjType subcmdLookupObjType = {
    "subcmd-lookup",
    NULL,
    NULL,
    NULL,
    JIM_TYPE_REFERENCES
};

const jim_subcmd_type *Jim_ParseSubCmd(Jim_Interp *interp, const jim_subcmd_type * command_table,
    int argc, Jim_Obj *const *argv)
{
    const jim_subcmd_type *ct;
    const jim_subcmd_type *partial = 0;
    int cmdlen;
    Jim_Obj *cmd;
    const char *cmdstr;
    const char *cmdname;
    int help = 0;

    cmdname = Jim_String(argv[0]);

    if (argc < 2) {
        Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
        Jim_AppendStrings(interp, Jim_GetResult(interp), "wrong # args: should be \"", cmdname,
        Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s command ...\"\n"
            " command ...\"\n", NULL);
        Jim_AppendStrings(interp, Jim_GetResult(interp), "Use \"", cmdname, " -help ?command?\" for help", NULL);
            "Use \"%#s -help ?command?\" for help", argv[0], argv[0]);
        return 0;
    }

    cmd = argv[1];

    

    if (cmd->typePtr == &subcmdLookupObjType) {
        if (cmd->internalRep.ptrIntValue.ptr == command_table) {
            ct = command_table + cmd->internalRep.ptrIntValue.int1;
            goto found;
        }
    }


    if (Jim_CompareStringImmediate(interp, cmd, "-help")) {
        if (argc == 2) {
            

            show_cmd_usage(interp, command_table, argc, argv);
            return &dummy_subcmd;
        }
        help = 1;

        

        cmd = argv[2];
    }

    

    if (Jim_CompareStringImmediate(interp, cmd, "-commands")) {
        

        Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
        add_commands(interp, command_table, " ");
        return &dummy_subcmd;
    }

    cmdstr = Jim_GetString(cmd, &cmdlen);

    for (ct = command_table; ct->cmd; ct++) {
        if (Jim_CompareStringImmediate(interp, cmd, ct->cmd)) {
            

            break;
        }
        if (strncmp(cmdstr, ct->cmd, cmdlen) == 0) {
            if (partial) {
                

                if (help) {
                    

                    show_cmd_usage(interp, command_table, argc, argv);
                    return &dummy_subcmd;
                }
                bad_subcmd(interp, command_table, "ambiguous", argv[0], argv[1 + help]);
                return 0;
            }
            partial = ct;
        }
        continue;
    }

    

    if (partial && !ct->cmd) {
        ct = partial;
    }

    if (!ct->cmd) {
        

        if (help) {
            

            show_cmd_usage(interp, command_table, argc, argv);
            return &dummy_subcmd;
        }
        bad_subcmd(interp, command_table, "unknown", argv[0], argv[1 + help]);
        return 0;
    }

    if (help) {
        Jim_SetResultString(interp, "Usage: ", -1);
        

        add_cmd_usage(interp, ct, argv[0]);
        return &dummy_subcmd;
    }

    

    Jim_FreeIntRep(interp, cmd);
    cmd->typePtr = &subcmdLookupObjType;
    cmd->internalRep.ptrIntValue.ptr = (void *)command_table;
    cmd->internalRep.ptrIntValue.int1 = ct - command_table;

found:

    if (argc - 2 < ct->minargs || (ct->maxargs >= 0 && argc - 2 > ct->maxargs)) {
        Jim_SetResultString(interp, "wrong # args: should be \"", -1);
        

        add_cmd_usage(interp, ct, argv[0]);
        Jim_AppendStrings(interp, Jim_GetResult(interp), "\"", NULL);

        return 0;
    }

    

    return ct;
}

int Jim_CallSubCmd(Jim_Interp *interp, const jim_subcmd_type * ct, int argc, Jim_Obj *const *argv)
{
    int ret = JIM_ERR;

20017
20018
20019
20020
20021
20022
20023
20024

20025
20026
20027
20028
20029
20030
20031
19932
19933
19934
19935
19936
19937
19938

19939
19940
19941
19942
19943
19944
19945
19946







-
+







    }
    else if (uc <= 0xffff) {
        *p++ = 0xe0 | ((uc & 0xf000) >> 12);
        *p++ = 0x80 | ((uc & 0xfc0) >> 6);
        *p = 0x80 | (uc & 0x3f);
        return 3;
    }
    

    else {
        *p++ = 0xf0 | ((uc & 0x1c0000) >> 18);
        *p++ = 0x80 | ((uc & 0x3f000) >> 12);
        *p++ = 0x80 | ((uc & 0xfc0) >> 6);
        *p = 0x80 | (uc & 0x3f);
        return 4;
    }
20144
20145
20146
20147
20148
20149
20150

20151

20152
20153
20154
20155
20156
20157
20158
20059
20060
20061
20062
20063
20064
20065
20066

20067
20068
20069
20070
20071
20072
20073
20074







+
-
+







            default:
                sawFlag = 0;
                continue;
            }
            *p++ = ch;
            format += step;
            step = utf8_tounicode(format, &ch);

        } while (sawFlag);
        } while (sawFlag && (p - spec <= 5));


        width = 0;
        if (isdigit(ch)) {
            width = strtoul(format, &end, 10);
            format = end;
            step = utf8_tounicode(format, &ch);
20208
20209
20210
20211
20212
20213
20214
20215

20216
20217
20218
20219
20220
20221
20222
20124
20125
20126
20127
20128
20129
20130

20131
20132
20133
20134
20135
20136
20137
20138







-
+








        useShort = 0;
        if (ch == 'h') {
            useShort = 1;
            format += step;
            step = utf8_tounicode(format, &ch);
        } else if (ch == 'l') {
            

            format += step;
            step = utf8_tounicode(format, &ch);
            if (ch == 'l') {
                format += step;
                step = utf8_tounicode(format, &ch);
            }
        }
20235
20236
20237
20238
20239
20240
20241
20242

20243
20244
20245
20246
20247
20248
20249
20250
20251
20252
20253
20254

20255
20256
20257
20258
20259
20260
20261
20262
20263
20264
20265
20266
20267
20268
20269
20270
20271
20272

20273
20274
20275
20276
20277
20278
20279
20151
20152
20153
20154
20155
20156
20157

20158
20159
20160
20161
20162
20163
20164
20165
20166
20167
20168
20169

20170
20171
20172
20173
20174
20175
20176
20177
20178
20179
20180
20181
20182
20183
20184
20185
20186
20187

20188
20189
20190
20191
20192
20193
20194
20195







-
+











-
+

















-
+







        case '\0':
            msg = "format string ended in middle of field specifier";
            goto errorMsg;
        case 's': {
            formatted_buf = Jim_GetString(objv[objIndex], &formatted_bytes);
            formatted_chars = Jim_Utf8Length(interp, objv[objIndex]);
            if (gotPrecision && (precision < formatted_chars)) {
                

                formatted_chars = precision;
                formatted_bytes = utf8_index(formatted_buf, precision);
            }
            break;
        }
        case 'c': {
            jim_wide code;

            if (Jim_GetWide(interp, objv[objIndex], &code) != JIM_OK) {
                goto error;
            }
            

            formatted_bytes = utf8_getchars(spec, code);
            formatted_buf = spec;
            formatted_chars = 1;
            break;
        }
        case 'b': {
                unsigned jim_wide w;
                int length;
                int i;
                int j;

                if (Jim_GetWide(interp, objv[objIndex], (jim_wide *)&w) != JIM_OK) {
                    goto error;
                }
                length = sizeof(w) * 8;


                

                if (num_buffer_size < length + 1) {
                    num_buffer_size = length + 1;
                    num_buffer = Jim_Realloc(num_buffer, num_buffer_size);
                }

                j = 0;
                for (i = length; i > 0; ) {
20293
20294
20295
20296
20297
20298
20299
20300

20301
20302
20303
20304
20305
20306
20307
20308
20309
20310

20311
20312
20313
20314
20315
20316
20317
20318

20319
20320
20321
20322
20323
20324
20325
20209
20210
20211
20212
20213
20214
20215

20216
20217
20218
20219
20220
20221
20222
20223
20224
20225

20226
20227
20228
20229
20230
20231
20232
20233

20234
20235
20236
20237
20238
20239
20240
20241







-
+









-
+







-
+








        case 'e':
        case 'E':
        case 'f':
        case 'g':
        case 'G':
            doubleType = 1;
            

        case 'd':
        case 'u':
        case 'o':
        case 'x':
        case 'X': {
            jim_wide w;
            double d;
            int length;

            

            if (width) {
                p += sprintf(p, "%ld", width);
            }
            if (gotPrecision) {
                p += sprintf(p, ".%ld", precision);
            }

            

            if (doubleType) {
                if (Jim_GetDouble(interp, objv[objIndex], &d) != JIM_OK) {
                    goto error;
                }
                length = MAX_FLOAT_WIDTH;
            }
            else {
20342
20343
20344
20345
20346
20347
20348
20349








20350
20351
20352
20353
20354
20355
20356
20357

20358
20359
20360
20361
20362
20363
20364
20365
20366
20367
20368
20369
20370
20371
20372
20373
20374
20375

20376
20377
20378
20379
20380
20381
20382
20258
20259
20260
20261
20262
20263
20264

20265
20266
20267
20268
20269
20270
20271
20272
20273
20274
20275
20276
20277
20278
20279

20280
20281
20282
20283
20284
20285
20286
20287
20288
20289
20290
20291
20292
20293
20294
20295
20296
20297

20298
20299
20300
20301
20302
20303
20304
20305







-
+
+
+
+
+
+
+
+







-
+

















-
+







                }
#endif
            }

            *p++ = (char) ch;
            *p = '\0';

            

            if (width > 10000 || length > 10000 || precision > 10000) {
                Jim_SetResultString(interp, "format too long", -1);
                goto error;
            }



            if (width > length) {
                length = width;
            }
            if (gotPrecision) {
                length += precision;
            }

            

            if (num_buffer_size < length + 1) {
                num_buffer_size = length + 1;
                num_buffer = Jim_Realloc(num_buffer, num_buffer_size);
            }

            if (doubleType) {
                snprintf(num_buffer, length + 1, spec, d);
            }
            else {
                formatted_bytes = snprintf(num_buffer, length + 1, spec, w);
            }
            formatted_chars = formatted_bytes = strlen(num_buffer);
            formatted_buf = num_buffer;
            break;
        }

        default: {
            

            spec[0] = ch;
            spec[1] = '\0';
            Jim_SetResultFormatted(interp, "bad field specifier \"%s\"", spec);
            goto error;
        }
        }

20420
20421
20422
20423
20424
20425
20426
20427
20428
20429
20430
20431
20432
20433
20434
20435
20436
20437
20438
20439
20440
20441
20442
20443
20444


















20445
20446
20447
20448



20449
20450
20451
20452
20453


20454
20455
20456
20457
20458
20459
20460
20461
20462
20463
20464
20465
20466
20467
20468
20469
20470
20471
20472
20473




20474
20475
20476
20477

20478
20479
20480
20481
20482
20483
20484
20343
20344
20345
20346
20347
20348
20349


















20350
20351
20352
20353
20354
20355
20356
20357
20358
20359
20360
20361
20362
20363
20364
20365
20366
20367
20368



20369
20370
20371
20372
20373
20374


20375
20376
20377
20378
20379
20380
20381
20382
20383
20384
20385
20386
20387
20388
20389
20390
20391
20392




20393
20394
20395
20396
20397
20398
20399

20400
20401
20402
20403
20404
20405
20406
20407







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
+
+
+



-
-
+
+
















-
-
-
-
+
+
+
+



-
+










#define REG_MAX_PAREN 100



#define	END	0	
#define	BOL	1	
#define	EOL	2	
#define	ANY	3	
#define	ANYOF	4	
#define	ANYBUT	5	
#define	BRANCH	6	
#define	BACK	7	
#define	EXACTLY	8	
#define	NOTHING	9	
#define	REP	10	
#define	REPMIN	11	
#define	REPX	12	
#define	REPXMIN	13	
#define	BOLX	14	
#define	EOLX	15	
#define	WORDA	16	
#define	WORDZ	17	
#define	END	0
#define	BOL	1
#define	EOL	2
#define	ANY	3
#define	ANYOF	4
#define	ANYBUT	5
#define	BRANCH	6
#define	BACK	7
#define	EXACTLY	8
#define	NOTHING	9
#define	REP	10
#define	REPMIN	11
#define	REPX	12
#define	REPXMIN	13
#define	BOLX	14
#define	EOLX	15
#define	WORDA	16
#define	WORDZ	17

#define	OPENNC 	1000	
#define	OPEN   	1001	
			
#define	OPENNC 	1000
#define	OPEN   	1001




#define	CLOSENC	2000 	
#define	CLOSE	2001 	
#define	CLOSENC	2000
#define	CLOSE	2001
#define	CLOSE_END	(CLOSE+REG_MAX_PAREN)

#define	REG_MAGIC	0xFADED00D


#define	OP(preg, p)	(preg->program[p])
#define	NEXT(preg, p)	(preg->program[p + 1])
#define	OPERAND(p)	((p) + 2)




#define	FAIL(R,M)	{ (R)->err = (M); return (M); }
#define	ISMULT(c)	((c) == '*' || (c) == '+' || (c) == '?' || (c) == '{')
#define	META		"^$.[()|?{+*"

#define	HASWIDTH	1	
#define	SIMPLE		2	
#define	SPSTART		4	
#define	WORST		0	
#define	HASWIDTH	1
#define	SIMPLE		2
#define	SPSTART		4
#define	WORST		0

#define MAX_REP_COUNT 1000000

static int reg(regex_t *preg, int paren , int *flagp );
static int reg(regex_t *preg, int paren, int *flagp );
static int regpiece(regex_t *preg, int *flagp );
static int regbranch(regex_t *preg, int *flagp );
static int regatom(regex_t *preg, int *flagp );
static int regnode(regex_t *preg, int op );
static int regnext(regex_t *preg, int p );
static void regc(regex_t *preg, int b );
static int reginsert(regex_t *preg, int op, int size, int opnd );
20518
20519
20520
20521
20522
20523
20524
20525

20526
20527
20528
20529

20530
20531
20532
20533
20534
20535
20536
20537
20538
20539
20540
20541


20542
20543
20544
20545


20546
20547
20548
20549
20550


20551
20552
20553

20554
20555
20556
20557
20558
20559
20560
20441
20442
20443
20444
20445
20446
20447

20448
20449
20450
20451

20452
20453
20454
20455
20456
20457
20458
20459
20460
20461
20462


20463
20464
20465
20466


20467
20468
20469
20470
20471


20472
20473
20474
20475

20476
20477
20478
20479
20480
20481
20482
20483







-
+



-
+










-
-
+
+


-
-
+
+



-
-
+
+


-
+







	fprintf(stderr, "Compiling: '%s'\n", exp);
#endif
	memset(preg, 0, sizeof(*preg));

	if (exp == NULL)
		FAIL(preg, REG_ERR_NULL_ARGUMENT);

	

	preg->cflags = cflags;
	preg->regparse = exp;

	

	preg->proglen = (strlen(exp) + 1) * 5;
	preg->program = malloc(preg->proglen * sizeof(int));
	if (preg->program == NULL)
		FAIL(preg, REG_ERR_NOMEM);

	regc(preg, REG_MAGIC);
	if (reg(preg, 0, &flags) == 0) {
		return preg->err;
	}

	
	if (preg->re_nsub >= REG_MAX_PAREN)		

	if (preg->re_nsub >= REG_MAX_PAREN)
		FAIL(preg,REG_ERR_TOO_BIG);

	
	preg->regstart = 0;	

	preg->regstart = 0;
	preg->reganch = 0;
	preg->regmust = 0;
	preg->regmlen = 0;
	scan = 1;			
	if (OP(preg, regnext(preg, scan)) == END) {		
	scan = 1;
	if (OP(preg, regnext(preg, scan)) == END) {
		scan = OPERAND(scan);

		

		if (OP(preg, scan) == EXACTLY) {
			preg->regstart = preg->program[OPERAND(scan)];
		}
		else if (OP(preg, scan) == BOL)
			preg->reganch++;

		if (flags&SPSTART) {
20577
20578
20579
20580
20581
20582
20583
20584

20585
20586
20587
20588
20589
20590
20591
20592

20593
20594

20595
20596
20597

20598
20599
20600
20601
20602
20603
20604
20605
20606
20607
20608

20609
20610
20611
20612
20613

20614
20615
20616
20617
20618
20619
20620
20621
20622
20623
20624

20625
20626
20627
20628
20629
20630

20631
20632
20633
20634

20635
20636
20637
20638

20639
20640
20641
20642
20643
20644
20645
20500
20501
20502
20503
20504
20505
20506

20507
20508
20509
20510
20511
20512
20513
20514

20515
20516

20517
20518
20519

20520
20521
20522
20523
20524
20525
20526
20527
20528
20529
20530

20531
20532
20533
20534
20535

20536
20537
20538
20539
20540
20541
20542
20543
20544
20545
20546

20547
20548
20549
20550
20551
20552

20553
20554
20555
20556

20557
20558
20559
20560

20561
20562
20563
20564
20565
20566
20567
20568







-
+







-
+

-
+


-
+










-
+




-
+










-
+





-
+



-
+



-
+







#ifdef DEBUG
	regdump(preg);
#endif

	return 0;
}

static int reg(regex_t *preg, int paren , int *flagp )
static int reg(regex_t *preg, int paren, int *flagp )
{
	int ret;
	int br;
	int ender;
	int parno = 0;
	int flags;

	*flagp = HASWIDTH;	
	*flagp = HASWIDTH;

	

	if (paren) {
		if (preg->regparse[0] == '?' && preg->regparse[1] == ':') {
			

			preg->regparse += 2;
			parno = -1;
		}
		else {
			parno = ++preg->re_nsub;
		}
		ret = regnode(preg, OPEN+parno);
	} else
		ret = 0;

	

	br = regbranch(preg, &flags);
	if (br == 0)
		return 0;
	if (ret != 0)
		regtail(preg, ret, br);	
		regtail(preg, ret, br);
	else
		ret = br;
	if (!(flags&HASWIDTH))
		*flagp &= ~HASWIDTH;
	*flagp |= flags&SPSTART;
	while (*preg->regparse == '|') {
		preg->regparse++;
		br = regbranch(preg, &flags);
		if (br == 0)
			return 0;
		regtail(preg, ret, br);	
		regtail(preg, ret, br);
		if (!(flags&HASWIDTH))
			*flagp &= ~HASWIDTH;
		*flagp |= flags&SPSTART;
	}

	

	ender = regnode(preg, (paren) ? CLOSE+parno : END);
	regtail(preg, ret, ender);

	

	for (br = ret; br != 0; br = regnext(preg, br))
		regoptail(preg, br, ender);

	

	if (paren && *preg->regparse++ != ')') {
		preg->err = REG_ERR_UNMATCHED_PAREN;
		return 0;
	} else if (!paren && *preg->regparse != '\0') {
		if (*preg->regparse == ')') {
			preg->err = REG_ERR_UNMATCHED_PAREN;
			return 0;
20655
20656
20657
20658
20659
20660
20661
20662

20663
20664
20665
20666
20667
20668
20669
20670
20671
20672
20673
20674
20675
20676
20677
20678
20679
20680

20681
20682
20683
20684
20685
20686
20687
20578
20579
20580
20581
20582
20583
20584

20585
20586
20587
20588
20589
20590
20591
20592
20593
20594
20595
20596
20597
20598
20599
20600
20601
20602

20603
20604
20605
20606
20607
20608
20609
20610







-
+

















-
+







static int regbranch(regex_t *preg, int *flagp )
{
	int ret;
	int chain;
	int latest;
	int flags;

	*flagp = WORST;		
	*flagp = WORST;

	ret = regnode(preg, BRANCH);
	chain = 0;
	while (*preg->regparse != '\0' && *preg->regparse != ')' &&
	       *preg->regparse != '|') {
		latest = regpiece(preg, &flags);
		if (latest == 0)
			return 0;
		*flagp |= flags&HASWIDTH;
		if (chain == 0) {
			*flagp |= flags&SPSTART;
		}
		else {
			regtail(preg, chain, latest);
		}
		chain = latest;
	}
	if (chain == 0)	
	if (chain == 0)
		(void) regnode(preg, NOTHING);

	return(ret);
}

static int regpiece(regex_t *preg, int *flagp)
{
20703
20704
20705
20706
20707
20708
20709
20710

20711
20712
20713
20714
20715
20716
20717
20718
20719
20720




20721
20722
20723
20724
20725
20726
20727
20626
20627
20628
20629
20630
20631
20632

20633
20634
20635
20636
20637
20638
20639
20640
20641
20642
20643
20644
20645
20646
20647
20648
20649
20650
20651
20652
20653
20654







-
+










+
+
+
+







	}

	if (!(flags&HASWIDTH) && op != '?') {
		preg->err = REG_ERR_OPERAND_COULD_BE_EMPTY;
		return 0;
	}

	

	if (op == '{') {
		char *end;

		min = strtoul(preg->regparse + 1, &end, 10);
		if (end == preg->regparse + 1) {
			preg->err = REG_ERR_BAD_COUNT;
			return 0;
		}
		if (*end == '}') {
			max = min;
		}
		else if (*end == '\0') {
			preg->err = REG_ERR_UNMATCHED_BRACES;
			return 0;
		}
		else {
			preg->regparse = end;
			max = strtoul(preg->regparse + 1, &end, 10);
			if (*end != '}') {
				preg->err = REG_ERR_UNMATCHED_BRACES;
				return 0;
20775
20776
20777
20778
20779
20780
20781
20782

20783
20784
20785
20786
20787
20788
20789
20702
20703
20704
20705
20706
20707
20708

20709
20710
20711
20712
20713
20714
20715
20716







-
+







}

static void reg_addrange(regex_t *preg, int lower, int upper)
{
	if (lower > upper) {
		reg_addrange(preg, upper, lower);
	}
	

	regc(preg, upper - lower + 1);
	regc(preg, lower);
}

static void reg_addrange_str(regex_t *preg, const char *str)
{
	while (*str) {
20843
20844
20845
20846
20847
20848
20849
20850

20851
20852
20853
20854
20855
20856

20857
20858
20859
20860
20861
20862
20863
20770
20771
20772
20773
20774
20775
20776

20777
20778
20779
20780
20781
20782

20783
20784
20785
20786
20787
20788
20789
20790







-
+





-
+







		case 'f': *ch = '\f'; break;
		case 'n': *ch = '\n'; break;
		case 'r': *ch = '\r'; break;
		case 't': *ch = '\t'; break;
		case 'v': *ch = '\v'; break;
		case 'u':
			if (*s == '{') {
				

				n = parse_hex(s + 1, 6, ch);
				if (n > 0 && s[n + 1] == '}' && *ch >= 0 && *ch <= 0x1fffff) {
					s += n + 2;
				}
				else {
					

					*ch = 'u';
				}
			}
			else if ((n = parse_hex(s, 4, ch)) > 0) {
				s += n;
			}
			break;
20884
20885
20886
20887
20888
20889
20890
20891

20892
20893
20894
20895

20896
20897
20898
20899
20900
20901
20902
20903
20904
20905
20906
20907
20908
20909

20910
20911
20912
20913
20914
20915

20916
20917
20918
20919
20920
20921
20922

20923
20924
20925







20926
20927
















20928
20929
20930
20931
20932
20933
20934
20935

20936
20937
20938
20939
20940
20941
20942
20943
20944
20945
20946
20947
20948
20949
20950
20951
20952
20953
20954
20955
20956
20957
20958
20959
20960
20961
20962
20963
20964




20965
20966
20967
20968
20969
20970



20971
20972
20973

20974
20975
20976
20977
20978
20979
20980
20811
20812
20813
20814
20815
20816
20817

20818
20819
20820
20821

20822
20823
20824
20825
20826
20827
20828
20829
20830
20831
20832
20833
20834
20835

20836
20837
20838
20839
20840
20841

20842
20843
20844
20845
20846
20847
20848

20849
20850
20851
20852
20853
20854
20855
20856
20857
20858
20859
20860
20861
20862
20863
20864
20865
20866
20867
20868
20869
20870
20871
20872
20873
20874
20875
20876
20877
20878
20879
20880
20881
20882
20883
20884

20885
20886
20887
20888
20889
20890
20891
20892
20893
20894
20895
20896
20897
20898
20899
20900
20901
20902
20903






20904




20905
20906
20907
20908
20909
20910
20911
20912


20913
20914
20915
20916
20917

20918
20919
20920
20921
20922
20923
20924
20925







-
+



-
+













-
+





-
+






-
+



+
+
+
+
+
+
+


+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







-
+


















-
-
-
-
-
-

-
-
-
-
+
+
+
+




-
-
+
+
+


-
+







	int ret;
	int flags;
	int nocase = (preg->cflags & REG_ICASE);

	int ch;
	int n = reg_utf8_tounicode_case(preg->regparse, &ch, nocase);

	*flagp = WORST;		
	*flagp = WORST;

	preg->regparse += n;
	switch (ch) {
	

	case '^':
		ret = regnode(preg, BOL);
		break;
	case '$':
		ret = regnode(preg, EOL);
		break;
	case '.':
		ret = regnode(preg, ANY);
		*flagp |= HASWIDTH|SIMPLE;
		break;
	case '[': {
			const char *pattern = preg->regparse;

			if (*pattern == '^') {	
			if (*pattern == '^') {
				ret = regnode(preg, ANYBUT);
				pattern++;
			} else
				ret = regnode(preg, ANYOF);

			

			if (*pattern == ']' || *pattern == '-') {
				reg_addrange(preg, *pattern, *pattern);
				pattern++;
			}

			while (*pattern && *pattern != ']') {
				

				int start;
				int end;

				enum {
					CC_ALPHA, CC_ALNUM, CC_SPACE, CC_BLANK, CC_UPPER, CC_LOWER,
					CC_DIGIT, CC_XDIGIT, CC_CNTRL, CC_GRAPH, CC_PRINT, CC_PUNCT,
					CC_NUM
				};
				int cc;

				pattern += reg_utf8_tounicode_case(pattern, &start, nocase);
				if (start == '\\') {

					switch (*pattern) {
						case 's':
							pattern++;
							cc = CC_SPACE;
							goto cc_switch;
						case 'd':
							pattern++;
							cc = CC_DIGIT;
							goto cc_switch;
						case 'w':
							pattern++;
							reg_addrange(preg, '_', '_');
							cc = CC_ALNUM;
							goto cc_switch;
					}
					pattern += reg_decode_escape(pattern, &start);
					if (start == 0) {
						preg->err = REG_ERR_NULL_CHAR;
						return 0;
					}
				}
				if (pattern[0] == '-' && pattern[1] && pattern[1] != ']') {
					

					pattern += utf8_tounicode(pattern, &end);
					pattern += reg_utf8_tounicode_case(pattern, &end, nocase);
					if (end == '\\') {
						pattern += reg_decode_escape(pattern, &end);
						if (end == 0) {
							preg->err = REG_ERR_NULL_CHAR;
							return 0;
						}
					}

					reg_addrange(preg, start, end);
					continue;
				}
				if (start == '[' && pattern[0] == ':') {
					static const char *character_class[] = {
						":alpha:", ":alnum:", ":space:", ":blank:", ":upper:", ":lower:",
						":digit:", ":xdigit:", ":cntrl:", ":graph:", ":print:", ":punct:",
					};
					enum {
						CC_ALPHA, CC_ALNUM, CC_SPACE, CC_BLANK, CC_UPPER, CC_LOWER,
						CC_DIGIT, CC_XDIGIT, CC_CNTRL, CC_GRAPH, CC_PRINT, CC_PUNCT,
						CC_NUM
					};
					int i;

					for (i = 0; i < CC_NUM; i++) {
						int n = strlen(character_class[i]);
						if (strncmp(pattern, character_class[i], n) == 0) {
							
					for (cc = 0; cc < CC_NUM; cc++) {
						n = strlen(character_class[cc]);
						if (strncmp(pattern, character_class[cc], n) == 0) {

							pattern += n + 1;
							break;
						}
					}
					if (i != CC_NUM) {
						switch (i) {
					if (cc != CC_NUM) {
cc_switch:
						switch (cc) {
							case CC_ALNUM:
								reg_addrange(preg, '0', '9');
								

							case CC_ALPHA:
								if ((preg->cflags & REG_ICASE) == 0) {
									reg_addrange(preg, 'a', 'z');
								}
								reg_addrange(preg, 'A', 'Z');
								break;
							case CC_SPACE:
20988
20989
20990
20991
20992
20993
20994
20995

20996
20997
20998
20999
21000
21001
21002
20933
20934
20935
20936
20937
20938
20939

20940
20941
20942
20943
20944
20945
20946
20947







-
+







								break;
							case CC_LOWER:
								reg_addrange(preg, 'a', 'z');
								break;
							case CC_XDIGIT:
								reg_addrange(preg, 'a', 'f');
								reg_addrange(preg, 'A', 'F');
								

							case CC_DIGIT:
								reg_addrange(preg, '0', '9');
								break;
							case CC_CNTRL:
								reg_addrange(preg, 0, 31);
								reg_addrange(preg, 127, 127);
								break;
21012
21013
21014
21015
21016
21017
21018
21019

21020
21021
21022
21023
21024
21025
21026
20957
20958
20959
20960
20961
20962
20963

20964
20965
20966
20967
20968
20969
20970
20971







-
+







								reg_addrange(preg, '[', '`');
								reg_addrange(preg, '{', '~');
								break;
						}
						continue;
					}
				}
				

				reg_addrange(preg, start, start);
			}
			regc(preg, '\0');

			if (*pattern) {
				pattern++;
			}
21035
21036
21037
21038
21039
21040
21041
21042

21043
21044
21045
21046
21047
21048
21049
20980
20981
20982
20983
20984
20985
20986

20987
20988
20989
20990
20991
20992
20993
20994







-
+







			return 0;
		*flagp |= flags&(HASWIDTH|SPSTART);
		break;
	case '\0':
	case '|':
	case ')':
		preg->err = REG_ERR_INTERNAL;
		return 0;	
		return 0;
	case '?':
	case '+':
	case '*':
	case '{':
		preg->err = REG_ERR_COUNT_FOLLOWS_NOTHING;
		return 0;
	case '\\':
21088
21089
21090
21091
21092
21093
21094
21095

21096
21097
21098


21099
21100
21101
21102
21103
21104
21105
21106
21107

21108
21109
21110
21111
21112
21113

21114
21115
21116
21117
21118

21119
21120
21121
21122
21123
21124
21125
21126
21127
21128
21129
21130

21131
21132

21133
21134
21135

21136
21137
21138
21139
21140
21141
21142

21143
21144
21145
21146
21147
21148
21149
21033
21034
21035
21036
21037
21038
21039

21040
21041


21042
21043
21044
21045
21046
21047
21048
21049
21050
21051

21052
21053
21054
21055
21056
21057

21058
21059
21060
21061
21062

21063
21064
21065
21066
21067
21068
21069
21070
21071
21072
21073
21074

21075
21076

21077
21078
21079

21080
21081
21082
21083
21084
21085
21086

21087
21088
21089
21090
21091
21092
21093
21094







-
+

-
-
+
+








-
+





-
+




-
+











-
+

-
+


-
+






-
+







		case 's':
		case 'S':
			ret = regnode(preg, ch == 's' ? ANYOF : ANYBUT);
			reg_addrange_str(preg," \t\r\n\f\v");
			regc(preg, '\0');
			*flagp |= HASWIDTH|SIMPLE;
			break;
		

		default:
			
			


			preg->regparse--;
			goto de_fault;
		}
		break;
	de_fault:
	default: {
			int added = 0;

			

			preg->regparse -= n;

			ret = regnode(preg, EXACTLY);


			

			while (*preg->regparse && strchr(META, *preg->regparse) == NULL) {
				n = reg_utf8_tounicode_case(preg->regparse, &ch, (preg->cflags & REG_ICASE));
				if (ch == '\\' && preg->regparse[n]) {
					if (strchr("<>mMwWdDsSAZ", preg->regparse[n])) {
						

						break;
					}
					n += reg_decode_escape(preg->regparse + n, &ch);
					if (ch == 0) {
						preg->err = REG_ERR_NULL_CHAR;
						return 0;
					}
				}


				if (ISMULT(preg->regparse[n])) {
					

					if (added) {
						

						break;
					}
					

					regc(preg, ch);
					added++;
					preg->regparse += n;
					break;
				}

				

				regc(preg, ch);
				added++;
				preg->regparse += n;
			}
			regc(preg, '\0');

			*flagp |= HASWIDTH;
21166
21167
21168
21169
21170
21171
21172
21173

21174
21175
21176
21177

21178
21179
21180
21181
21182
21183
21184
21185
21186
21187
21188
21189
21190
21191

21192
21193

21194
21195
21196
21197
21198
21199
21200
21201
21202
21203
21204
21205
21206
21207
21208
21209

21210
21211
21212
21213
21214
21215
21216
21217
21218
21219
21220
21221
21222
21223
21224
21225
21226
21227
21228
21229

21230
21231
21232
21233
21234
21235
21236
21237
21238
21239
21240
21241
21242
21243
21244
21245

21246
21247
21248
21249
21250

21251
21252
21253
21254
21255
21256
21257
21258
21259
21260
21261
21262
21263

21264
21265

21266
21267
21268
21269
21270
21271
21272
21273
21274

21275
21276
21277
21278
21279
21280
21281
21282
21283

21284
21285
21286
21287

21288
21289
21290

21291
21292
21293

21294
21295
21296
21297
21298
21299
21300
21301
21302
21303

21304
21305
21306
21307
21308
21309
21310
21311
21312
21313
21314
21315

21316
21317
21318

21319
21320
21321
21322
21323
21324
21325
21326

21327
21328
21329
21330
21331
21332
21333
21334
21335
21336
21337
21338
21339

21340
21341
21342
21343

21344
21345
21346
21347
21348
21349
21350
21111
21112
21113
21114
21115
21116
21117

21118
21119
21120
21121

21122
21123
21124
21125
21126
21127
21128
21129
21130
21131
21132
21133
21134
21135

21136
21137

21138
21139
21140
21141
21142
21143
21144
21145
21146
21147
21148
21149
21150
21151
21152
21153

21154
21155
21156
21157
21158
21159
21160
21161
21162
21163
21164
21165
21166
21167
21168
21169
21170
21171
21172
21173

21174
21175
21176
21177
21178
21179
21180
21181
21182
21183
21184
21185
21186
21187
21188
21189

21190
21191
21192
21193
21194

21195
21196
21197
21198
21199
21200
21201
21202
21203
21204
21205
21206
21207

21208
21209

21210
21211
21212
21213
21214
21215
21216
21217
21218

21219
21220
21221
21222
21223
21224
21225
21226
21227

21228
21229
21230
21231

21232
21233
21234

21235
21236
21237

21238
21239
21240
21241
21242
21243
21244
21245
21246
21247

21248
21249
21250
21251
21252
21253
21254
21255
21256
21257
21258
21259

21260
21261
21262

21263
21264
21265
21266
21267
21268
21269
21270

21271
21272
21273
21274
21275
21276
21277
21278
21279
21280
21281
21282
21283

21284
21285
21286
21287

21288
21289
21290
21291
21292
21293
21294
21295







-
+



-
+













-
+

-
+















-
+



















-
+















-
+




-
+












-
+

-
+








-
+








-
+



-
+


-
+


-
+









-
+











-
+


-
+







-
+












-
+



-
+







}


static int regnode(regex_t *preg, int op)
{
	reg_grow(preg, 2);

	

	preg->program[preg->p++] = op;
	preg->program[preg->p++] = 0;

	

	return preg->p - 2;
}

static void regc(regex_t *preg, int b )
{
	reg_grow(preg, 1);
	preg->program[preg->p++] = b;
}

static int reginsert(regex_t *preg, int op, int size, int opnd )
{
	reg_grow(preg, size);

	

	memmove(preg->program + opnd + size, preg->program + opnd, sizeof(int) * (preg->p - opnd));
	

	memset(preg->program + opnd, 0, sizeof(int) * size);

	preg->program[opnd] = op;

	preg->p += size;

	return opnd + size;
}

static void regtail(regex_t *preg, int p, int val)
{
	int scan;
	int temp;
	int offset;

	

	scan = p;
	for (;;) {
		temp = regnext(preg, scan);
		if (temp == 0)
			break;
		scan = temp;
	}

	if (OP(preg, scan) == BACK)
		offset = scan - val;
	else
		offset = val - scan;

	preg->program[scan + 1] = offset;
}


static void regoptail(regex_t *preg, int p, int val )
{
	

	if (p != 0 && OP(preg, p) == BRANCH) {
		regtail(preg, OPERAND(p), val);
	}
}


static int regtry(regex_t *preg, const char *string );
static int regmatch(regex_t *preg, int prog);
static int regrepeat(regex_t *preg, int p, int max);

int regexec(regex_t  *preg,  const  char *string, size_t nmatch, regmatch_t pmatch[], int eflags)
{
	const char *s;
	int scan;

	

	if (preg == NULL || preg->program == NULL || string == NULL) {
		return REG_ERR_NULL_ARGUMENT;
	}

	

	if (*preg->program != REG_MAGIC) {
		return REG_ERR_CORRUPTED;
	}

#ifdef DEBUG
	fprintf(stderr, "regexec: %s\n", string);
	regdump(preg);
#endif

	preg->eflags = eflags;
	preg->pmatch = pmatch;
	preg->nmatch = nmatch;
	preg->start = string;	
	preg->start = string;

	

	for (scan = OPERAND(1); scan != 0; scan += regopsize(preg, scan)) {
		int op = OP(preg, scan);
		if (op == END)
			break;
		if (op == REPX || op == REPXMIN)
			preg->program[scan + 4] = 0;
	}

	

	if (preg->regmust != 0) {
		s = string;
		while ((s = str_find(s, preg->program[preg->regmust], preg->cflags & REG_ICASE)) != NULL) {
			if (prefix_cmp(preg->program + preg->regmust, preg->regmlen, s, preg->cflags & REG_ICASE) >= 0) {
				break;
			}
			s++;
		}
		if (s == NULL)	
		if (s == NULL)
			return REG_NOMATCH;
	}

	

	preg->regbol = string;

	

	if (preg->reganch) {
		if (eflags & REG_NOTBOL) {
			

			goto nextline;
		}
		while (1) {
			if (regtry(preg, string)) {
				return REG_NOERROR;
			}
			if (*string) {
nextline:
				if (preg->cflags & REG_NEWLINE) {
					

					string = strchr(string, '\n');
					if (string) {
						preg->regbol = ++string;
						continue;
					}
				}
			}
			return REG_NOMATCH;
		}
	}

	

	s = string;
	if (preg->regstart != '\0') {
		

		while ((s = str_find(s, preg->regstart, preg->cflags & REG_ICASE)) != NULL) {
			if (regtry(preg, s))
				return REG_NOERROR;
			s++;
		}
	}
	else
		

		while (1) {
			if (regtry(preg, s))
				return REG_NOERROR;
			if (*s == '\0') {
				break;
			}
			else {
				int c;
				s += utf8_tounicode(s, &c);
			}
		}

	

	return REG_NOMATCH;
}

			

static int regtry( regex_t *preg, const char *string )
{
	int i;

	preg->reginput = string;

	for (i = 0; i < preg->nmatch; i++) {
21377
21378
21379
21380
21381
21382
21383
21384

21385
21386
21387
21388
21389
21390
21391
21392
21393
21394
21395
21396

21397
21398
21399
21400
21401
21402
21403
21322
21323
21324
21325
21326
21327
21328

21329
21330
21331
21332
21333
21334
21335
21336
21337
21338
21339
21340

21341
21342
21343
21344
21345
21346
21347
21348







-
+











-
+







	}
	return -1;
}

static int reg_range_find(const int *range, int c)
{
	while (*range) {
		

		if (c >= range[1] && c <= (range[0] + range[1] - 1)) {
			return 1;
		}
		range += 2;
	}
	return 0;
}

static const char *str_find(const char *string, int c, int nocase)
{
	if (nocase) {
		

		c = utf8_upper(c);
	}
	while (*string) {
		int ch;
		int n = reg_utf8_tounicode_case(string, &ch, nocase);
		if (c == ch) {
			return string;
21433
21434
21435
21436
21437
21438
21439
21440

21441
21442
21443
21444

21445
21446
21447
21448
21449
21450
21451
21452
21453
21454
21455
21456
21457
21458

21459
21460
21461
21462
21463
21464
21465

21466
21467
21468
21469

21470
21471
21472
21473
21474
21475
21476
21477
21478
21479
21480
21481
21482
21483

21484
21485

21486
21487
21488
21489
21490
21491
21492
21493
21494
21495
21496
21497
21498

21499
21500
21501
21502

21503
21504
21505
21506
21507
21508
21509
21510

21511
21512
21513
21514
21515
21516
21517
21518

21519
21520
21521
21522
21523
21524
21525
21526


21527
21528
21529
21530
21531
21532
21533
21534
21535
21536
21537
21538
21539
21540

21541
21542
21543
21544
21545
21546
21547
21548
21549
21550
21551

21552
21553
21554
21555
21556
21557
21558
21559

21560
21561
21562
21563
21564
21565
21566
21567
21568
21569

21570
21571
21572

21573
21574
21575
21576
21577
21578

21579
21580

21581
21582
21583

21584
21585
21586
21587
21588
21589

21590
21591
21592
21593
21594
21595
21596
21378
21379
21380
21381
21382
21383
21384

21385
21386
21387
21388

21389
21390
21391
21392
21393
21394
21395
21396
21397
21398
21399
21400
21401
21402

21403
21404
21405
21406
21407
21408
21409

21410
21411
21412
21413

21414
21415
21416
21417
21418
21419
21420
21421
21422
21423
21424
21425
21426
21427

21428
21429

21430
21431
21432
21433
21434
21435
21436
21437
21438
21439
21440
21441
21442

21443
21444
21445
21446

21447
21448
21449
21450
21451
21452
21453
21454

21455
21456
21457
21458
21459
21460
21461
21462

21463
21464
21465
21466
21467
21468
21469


21470
21471
21472
21473
21474
21475
21476
21477
21478
21479
21480
21481
21482
21483
21484

21485
21486
21487
21488
21489
21490
21491
21492
21493
21494
21495

21496
21497
21498
21499
21500
21501
21502
21503

21504
21505
21506
21507
21508
21509
21510
21511
21512
21513

21514
21515
21516

21517
21518
21519
21520
21521
21522

21523
21524

21525
21526
21527

21528
21529
21530
21531
21532
21533

21534
21535
21536
21537
21538
21539
21540
21541







-
+



-
+













-
+






-
+



-
+













-
+

-
+












-
+



-
+







-
+







-
+






-
-
+
+













-
+










-
+







-
+









-
+


-
+





-
+

-
+


-
+





-
+







	}
	save = preg->reginput;
	no = regrepeat(preg, scan + 5, max);
	if (no < min) {
		return 0;
	}
	if (matchmin) {
		

		max = no;
		no = min;
	}
	

	while (1) {
		if (matchmin) {
			if (no > max) {
				break;
			}
		}
		else {
			if (no < min) {
				break;
			}
		}
		preg->reginput = save + utf8_index(save, no);
		reg_utf8_tounicode_case(preg->reginput, &c, (preg->cflags & REG_ICASE));
		

		if (reg_iseol(preg, nextch) || c == nextch) {
			if (regmatch(preg, next)) {
				return(1);
			}
		}
		if (matchmin) {
			

			no++;
		}
		else {
			

			no--;
		}
	}
	return(0);
}

static int regmatchrepeat(regex_t *preg, int scan, int matchmin)
{
	int *scanpt = preg->program + scan;

	int max = scanpt[2];
	int min = scanpt[3];

	

	if (scanpt[4] < min) {
		

		scanpt[4]++;
		if (regmatch(preg, scan + 5)) {
			return 1;
		}
		scanpt[4]--;
		return 0;
	}
	if (scanpt[4] > max) {
		return 0;
	}

	if (matchmin) {
		

		if (regmatch(preg, regnext(preg, scan))) {
			return 1;
		}
		

		scanpt[4]++;
		if (regmatch(preg, scan + 5)) {
			return 1;
		}
		scanpt[4]--;
		return 0;
	}
	

	if (scanpt[4] < max) {
		scanpt[4]++;
		if (regmatch(preg, scan + 5)) {
			return 1;
		}
		scanpt[4]--;
	}
	

	return regmatch(preg, regnext(preg, scan));
}


static int regmatch(regex_t *preg, int prog)
{
	int scan;	
	int next;		
	int scan;
	int next;
	const char *save;

	scan = prog;

#ifdef DEBUG
	if (scan != 0 && regnarrate)
		fprintf(stderr, "%s(\n", regprop(scan));
#endif
	while (scan != 0) {
		int n;
		int c;
#ifdef DEBUG
		if (regnarrate) {
			fprintf(stderr, "%3d: %s...\n", scan, regprop(OP(preg, scan)));	
			fprintf(stderr, "%3d: %s...\n", scan, regprop(OP(preg, scan)));
		}
#endif
		next = regnext(preg, scan);
		n = reg_utf8_tounicode_case(preg->reginput, &c, (preg->cflags & REG_ICASE));

		switch (OP(preg, scan)) {
		case BOLX:
			if ((preg->eflags & REG_NOTBOL)) {
				return(0);
			}
			

		case BOL:
			if (preg->reginput != preg->regbol) {
				return(0);
			}
			break;
		case EOLX:
			if (c != 0) {
				

				return 0;
			}
			break;
		case EOL:
			if (!reg_iseol(preg, c)) {
				return(0);
			}
			break;
		case WORDA:
			

			if ((!isalnum(UCHAR(c))) && c != '_')
				return(0);
			

			if (preg->reginput > preg->regbol &&
				(isalnum(UCHAR(preg->reginput[-1])) || preg->reginput[-1] == '_'))
				return(0);
			break;
		case WORDZ:
			

			if (preg->reginput > preg->regbol) {
				

				if (reg_iseol(preg, c) || !isalnum(UCHAR(c)) || c != '_') {
					c = preg->reginput[-1];
					

					if (isalnum(UCHAR(c)) || c == '_') {
						break;
					}
				}
			}
			

			return(0);

		case ANY:
			if (reg_iseol(preg, c))
				return 0;
			preg->reginput += n;
			break;
21622
21623
21624
21625
21626
21627
21628
21629
21630


21631
21632
21633
21634
21635
21636
21637
21638
21639
21640
21641

21642
21643
21644
21645
21646
21647
21648
21649
21650
21651
21652
21653

21654
21655
21656
21657
21658
21659
21660
21567
21568
21569
21570
21571
21572
21573


21574
21575
21576
21577
21578
21579
21580
21581
21582
21583
21584
21585

21586
21587
21588
21589
21590
21591
21592
21593
21594
21595
21596
21597

21598
21599
21600
21601
21602
21603
21604
21605







-
-
+
+










-
+











-
+







			preg->reginput += n;
			break;
		case NOTHING:
			break;
		case BACK:
			break;
		case BRANCH:
			if (OP(preg, next) != BRANCH)		
				next = OPERAND(scan);	
			if (OP(preg, next) != BRANCH)
				next = OPERAND(scan);
			else {
				do {
					save = preg->reginput;
					if (regmatch(preg, OPERAND(scan))) {
						return(1);
					}
					preg->reginput = save;
					scan = regnext(preg, scan);
				} while (scan != 0 && OP(preg, scan) == BRANCH);
				return(0);
				

			}
			break;
		case REP:
		case REPMIN:
			return regmatchsimplerepeat(preg, scan, OP(preg, scan) == REPMIN);

		case REPX:
		case REPXMIN:
			return regmatchrepeat(preg, scan, OP(preg, scan) == REPXMIN);

		case END:
			return 1;	
			return 1;

		case OPENNC:
		case CLOSENC:
			return regmatch(preg, next);

		default:
			if (OP(preg, scan) >= OPEN+1 && OP(preg, scan) < CLOSE_END) {
21693
21694
21695
21696
21697
21698
21699
21700

21701
21702
21703
21704
21705
21706
21707
21638
21639
21640
21641
21642
21643
21644

21645
21646
21647
21648
21649
21650
21651
21652







-
+







	int ch;
	int n;

	scan = preg->reginput;
	opnd = OPERAND(p);
	switch (OP(preg, p)) {
	case ANY:
		

		while (!reg_iseol(preg, *scan) && count < max) {
			count++;
			scan++;
		}
		break;
	case EXACTLY:
		while (count < max) {
21729
21730
21731
21732
21733
21734
21735
21736

21737
21738

21739
21740
21741
21742
21743
21744
21745
21674
21675
21676
21677
21678
21679
21680

21681
21682

21683
21684
21685
21686
21687
21688
21689
21690







-
+

-
+







			if (reg_iseol(preg, ch) || reg_range_find(preg->program + opnd, ch) != 0) {
				break;
			}
			count++;
			scan += n;
		}
		break;
	default:		
	default:
		preg->err = REG_ERR_INTERNAL;
		count = 0;	
		count = 0;
		break;
	}
	preg->reginput = scan;

	return(count);
}

21756
21757
21758
21759
21760
21761
21762
21763

21764
21765
21766
21767
21768
21769
21770
21701
21702
21703
21704
21705
21706
21707

21708
21709
21710
21711
21712
21713
21714
21715







-
+







		return(p-offset);
	else
		return(p+offset);
}

static int regopsize(regex_t *preg, int p )
{
	

	switch (OP(preg, p)) {
		case REP:
		case REPMIN:
		case REPX:
		case REPXMIN:
			return 5;

21816
21817
21818
21819
21820
21821
21822





















































































































































































































21823
21824
21825
21826
21827
21828
21829
21761
21762
21763
21764
21765
21766
21767
21768
21769
21770
21771
21772
21773
21774
21775
21776
21777
21778
21779
21780
21781
21782
21783
21784
21785
21786
21787
21788
21789
21790
21791
21792
21793
21794
21795
21796
21797
21798
21799
21800
21801
21802
21803
21804
21805
21806
21807
21808
21809
21810
21811
21812
21813
21814
21815
21816
21817
21818
21819
21820
21821
21822
21823
21824
21825
21826
21827
21828
21829
21830
21831
21832
21833
21834
21835
21836
21837
21838
21839
21840
21841
21842
21843
21844
21845
21846
21847
21848
21849
21850
21851
21852
21853
21854
21855
21856
21857
21858
21859
21860
21861
21862
21863
21864
21865
21866
21867
21868
21869
21870
21871
21872
21873
21874
21875
21876
21877
21878
21879
21880
21881
21882
21883
21884
21885
21886
21887
21888
21889
21890
21891
21892
21893
21894
21895
21896
21897
21898
21899
21900
21901
21902
21903
21904
21905
21906
21907
21908
21909
21910
21911
21912
21913
21914
21915
21916
21917
21918
21919
21920
21921
21922
21923
21924
21925
21926
21927
21928
21929
21930
21931
21932
21933
21934
21935
21936
21937
21938
21939
21940
21941
21942
21943
21944
21945
21946
21947
21948
21949
21950
21951
21952
21953
21954
21955
21956
21957
21958
21959
21960
21961
21962
21963
21964
21965
21966
21967
21968
21969
21970
21971
21972
21973
21974
21975
21976
21977
21978
21979
21980
21981
21982
21983
21984
21985
21986
21987







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







	return snprintf(errbuf, errbuf_size, "%s", err);
}

void regfree(regex_t *preg)
{
	free(preg->program);
}

#endif
#include <string.h>

void Jim_SetResultErrno(Jim_Interp *interp, const char *msg)
{
    Jim_SetResultFormatted(interp, "%s: %s", msg, strerror(Jim_Errno()));
}

#if defined(__MINGW32__)
#include <sys/stat.h>

int Jim_Errno(void)
{
    switch (GetLastError()) {
    case ERROR_FILE_NOT_FOUND: return ENOENT;
    case ERROR_PATH_NOT_FOUND: return ENOENT;
    case ERROR_TOO_MANY_OPEN_FILES: return EMFILE;
    case ERROR_ACCESS_DENIED: return EACCES;
    case ERROR_INVALID_HANDLE: return EBADF;
    case ERROR_BAD_ENVIRONMENT: return E2BIG;
    case ERROR_BAD_FORMAT: return ENOEXEC;
    case ERROR_INVALID_ACCESS: return EACCES;
    case ERROR_INVALID_DRIVE: return ENOENT;
    case ERROR_CURRENT_DIRECTORY: return EACCES;
    case ERROR_NOT_SAME_DEVICE: return EXDEV;
    case ERROR_NO_MORE_FILES: return ENOENT;
    case ERROR_WRITE_PROTECT: return EROFS;
    case ERROR_BAD_UNIT: return ENXIO;
    case ERROR_NOT_READY: return EBUSY;
    case ERROR_BAD_COMMAND: return EIO;
    case ERROR_CRC: return EIO;
    case ERROR_BAD_LENGTH: return EIO;
    case ERROR_SEEK: return EIO;
    case ERROR_WRITE_FAULT: return EIO;
    case ERROR_READ_FAULT: return EIO;
    case ERROR_GEN_FAILURE: return EIO;
    case ERROR_SHARING_VIOLATION: return EACCES;
    case ERROR_LOCK_VIOLATION: return EACCES;
    case ERROR_SHARING_BUFFER_EXCEEDED: return ENFILE;
    case ERROR_HANDLE_DISK_FULL: return ENOSPC;
    case ERROR_NOT_SUPPORTED: return ENODEV;
    case ERROR_REM_NOT_LIST: return EBUSY;
    case ERROR_DUP_NAME: return EEXIST;
    case ERROR_BAD_NETPATH: return ENOENT;
    case ERROR_NETWORK_BUSY: return EBUSY;
    case ERROR_DEV_NOT_EXIST: return ENODEV;
    case ERROR_TOO_MANY_CMDS: return EAGAIN;
    case ERROR_ADAP_HDW_ERR: return EIO;
    case ERROR_BAD_NET_RESP: return EIO;
    case ERROR_UNEXP_NET_ERR: return EIO;
    case ERROR_NETNAME_DELETED: return ENOENT;
    case ERROR_NETWORK_ACCESS_DENIED: return EACCES;
    case ERROR_BAD_DEV_TYPE: return ENODEV;
    case ERROR_BAD_NET_NAME: return ENOENT;
    case ERROR_TOO_MANY_NAMES: return ENFILE;
    case ERROR_TOO_MANY_SESS: return EIO;
    case ERROR_SHARING_PAUSED: return EAGAIN;
    case ERROR_REDIR_PAUSED: return EAGAIN;
    case ERROR_FILE_EXISTS: return EEXIST;
    case ERROR_CANNOT_MAKE: return ENOSPC;
    case ERROR_OUT_OF_STRUCTURES: return ENFILE;
    case ERROR_ALREADY_ASSIGNED: return EEXIST;
    case ERROR_INVALID_PASSWORD: return EPERM;
    case ERROR_NET_WRITE_FAULT: return EIO;
    case ERROR_NO_PROC_SLOTS: return EAGAIN;
    case ERROR_DISK_CHANGE: return EXDEV;
    case ERROR_BROKEN_PIPE: return EPIPE;
    case ERROR_OPEN_FAILED: return ENOENT;
    case ERROR_DISK_FULL: return ENOSPC;
    case ERROR_NO_MORE_SEARCH_HANDLES: return EMFILE;
    case ERROR_INVALID_TARGET_HANDLE: return EBADF;
    case ERROR_INVALID_NAME: return ENOENT;
    case ERROR_PROC_NOT_FOUND: return ESRCH;
    case ERROR_WAIT_NO_CHILDREN: return ECHILD;
    case ERROR_CHILD_NOT_COMPLETE: return ECHILD;
    case ERROR_DIRECT_ACCESS_HANDLE: return EBADF;
    case ERROR_SEEK_ON_DEVICE: return ESPIPE;
    case ERROR_BUSY_DRIVE: return EAGAIN;
    case ERROR_DIR_NOT_EMPTY: return EEXIST;
    case ERROR_NOT_LOCKED: return EACCES;
    case ERROR_BAD_PATHNAME: return ENOENT;
    case ERROR_LOCK_FAILED: return EACCES;
    case ERROR_ALREADY_EXISTS: return EEXIST;
    case ERROR_FILENAME_EXCED_RANGE: return ENAMETOOLONG;
    case ERROR_BAD_PIPE: return EPIPE;
    case ERROR_PIPE_BUSY: return EAGAIN;
    case ERROR_PIPE_NOT_CONNECTED: return EPIPE;
    case ERROR_DIRECTORY: return ENOTDIR;
    }
    return EINVAL;
}

pidtype waitpid(pidtype pid, int *status, int nohang)
{
    DWORD ret = WaitForSingleObject(pid, nohang ? 0 : INFINITE);
    if (ret == WAIT_TIMEOUT || ret == WAIT_FAILED) {

        return JIM_BAD_PID;
    }
    GetExitCodeProcess(pid, &ret);
    *status = ret;
    CloseHandle(pid);
    return pid;
}

int Jim_MakeTempFile(Jim_Interp *interp, const char *filename_template, int unlink_file)
{
    char name[MAX_PATH];
    HANDLE handle;

    if (!GetTempPath(MAX_PATH, name) || !GetTempFileName(name, filename_template ? filename_template : "JIM", 0, name)) {
        return -1;
    }

    handle = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, NULL,
            CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY | (unlink_file ? FILE_FLAG_DELETE_ON_CLOSE : 0),
            NULL);

    if (handle == INVALID_HANDLE_VALUE) {
        goto error;
    }

    Jim_SetResultString(interp, name, -1);
    return _open_osfhandle((int)handle, _O_RDWR | _O_TEXT);

  error:
    Jim_SetResultErrno(interp, name);
    DeleteFile(name);
    return -1;
}

int Jim_OpenForWrite(const char *filename, int append)
{
    if (strcmp(filename, "/dev/null") == 0) {
        filename = "nul:";
    }
    int fd = _open(filename, _O_WRONLY | _O_CREAT | _O_TEXT | (append ? _O_APPEND : _O_TRUNC), _S_IREAD | _S_IWRITE);
    if (fd >= 0 && append) {

        _lseek(fd, 0L, SEEK_END);
    }
    return fd;
}

int Jim_OpenForRead(const char *filename)
{
    if (strcmp(filename, "/dev/null") == 0) {
        filename = "nul:";
    }
    return _open(filename, _O_RDONLY | _O_TEXT, 0);
}

#elif defined(HAVE_UNISTD_H)



int Jim_MakeTempFile(Jim_Interp *interp, const char *filename_template, int unlink_file)
{
    int fd;
    mode_t mask;
    Jim_Obj *filenameObj;

    if (filename_template == NULL) {
        const char *tmpdir = getenv("TMPDIR");
        if (tmpdir == NULL || *tmpdir == '\0' || access(tmpdir, W_OK) != 0) {
            tmpdir = "/tmp/";
        }
        filenameObj = Jim_NewStringObj(interp, tmpdir, -1);
        if (tmpdir[0] && tmpdir[strlen(tmpdir) - 1] != '/') {
            Jim_AppendString(interp, filenameObj, "/", 1);
        }
        Jim_AppendString(interp, filenameObj, "tcl.tmp.XXXXXX", -1);
    }
    else {
        filenameObj = Jim_NewStringObj(interp, filename_template, -1);
    }


    mask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
#ifdef HAVE_MKSTEMP
    fd = mkstemp(filenameObj->bytes);
#else
    if (mktemp(filenameObj->bytes) == NULL) {
        fd = -1;
    }
    else {
        fd = open(filenameObj->bytes, O_RDWR | O_CREAT | O_TRUNC);
    }
#endif
    umask(mask);
    if (fd < 0) {
        Jim_SetResultErrno(interp, Jim_String(filenameObj));
        Jim_FreeNewObj(interp, filenameObj);
        return -1;
    }
    if (unlink_file) {
        remove(Jim_String(filenameObj));
    }

    Jim_SetResult(interp, filenameObj);
    return fd;
}

int Jim_OpenForWrite(const char *filename, int append)
{
    return open(filename, O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC), 0666);
}

int Jim_OpenForRead(const char *filename)
{
    return open(filename, O_RDONLY, 0);
}

#endif

#if defined(_WIN32) || defined(WIN32)
#ifndef STRICT
#define STRICT
#endif
21877
21878
21879
21880
21881
21882
21883
21884

21885
21886
21887
21888
21889
21890
21891
21892
21893

21894
21895
21896
21897
21898
21899

21900
21901
21902
21903
21904
21905
21906
22035
22036
22037
22038
22039
22040
22041

22042
22043
22044
22045
22046
22047
22048
22049
22050

22051
22052
22053
22054
22055
22056

22057
22058
22059
22060
22061
22062
22063
22064







-
+








-
+





-
+








DIR *opendir(const char *name)
{
    DIR *dir = 0;

    if (name && name[0]) {
        size_t base_length = strlen(name);
        const char *all =       
        const char *all =
            strchr("/\\", name[base_length - 1]) ? "*" : "/*";

        if ((dir = (DIR *) Jim_Alloc(sizeof *dir)) != 0 &&
            (dir->name = (char *)Jim_Alloc(base_length + strlen(all) + 1)) != 0) {
            strcat(strcpy(dir->name, name), all);

            if ((dir->handle = (long)_findfirst(dir->name, &dir->info)) != -1)
                dir->result.d_name = 0;
            else {              
            else {
                Jim_Free(dir->name);
                Jim_Free(dir);
                dir = 0;
            }
        }
        else {                  
        else {
            Jim_Free(dir);
            dir = 0;
            errno = ENOMEM;
        }
    }
    else {
        errno = EINVAL;
21914
21915
21916
21917
21918
21919
21920
21921

21922
21923
21924
21925
21926
21927
21928
22072
22073
22074
22075
22076
22077
22078

22079
22080
22081
22082
22083
22084
22085
22086







-
+








    if (dir) {
        if (dir->handle != -1)
            result = _findclose(dir->handle);
        Jim_Free(dir->name);
        Jim_Free(dir);
    }
    if (result == -1)           
    if (result == -1)
        errno = EBADF;
    return result;
}

struct dirent *readdir(DIR * dir)
{
    struct dirent *result = 0;
21936
21937
21938
21939
21940
21941
21942


























21943
21944
21945
21946
21947
21948
21949
21950
21951



21952
21953
21954
21955
21956





21957

21958
21959












21960




21961
21962
21963
21964
21965
21966
21967
22094
22095
22096
22097
22098
22099
22100
22101
22102
22103
22104
22105
22106
22107
22108
22109
22110
22111
22112
22113
22114
22115
22116
22117
22118
22119
22120
22121
22122
22123
22124
22125
22126
22127
22128
22129
22130
22131
22132
22133
22134
22135
22136
22137
22138
22139
22140
22141
22142
22143
22144
22145
22146
22147
22148

22149
22150
22151
22152
22153
22154
22155
22156
22157
22158
22159
22160
22161
22162
22163

22164
22165
22166
22167
22168
22169
22170
22171
22172
22173
22174







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+









+
+
+





+
+
+
+
+
-
+


+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+







    else {
        errno = EBADF;
    }
    return result;
}
#endif
#endif
#include <stdio.h>
#include <signal.h>






#ifndef SIGPIPE
#define SIGPIPE 13
#endif
#ifndef SIGINT
#define SIGINT 2
#endif

const char *Jim_SignalId(int sig)
{
	static char buf[10];
	switch (sig) {
		case SIGINT: return "SIGINT";
		case SIGPIPE: return "SIGPIPE";

	}
	snprintf(buf, sizeof(buf), "%d", sig);
	return buf;
}
#ifndef JIM_BOOTSTRAP_LIB_ONLY
#include <errno.h>
#include <string.h>


#ifdef USE_LINENOISE
#ifdef HAVE_UNISTD_H
    #include <unistd.h>
#endif
#ifdef HAVE_SYS_STAT_H
    #include <sys/stat.h>
#endif
#include "linenoise.h"
#else
#define MAX_LINE_LEN 512
#endif

#ifdef USE_LINENOISE
static void JimCompletionCallback(const char *prefix, linenoiseCompletions *comp, void *userdata);
static const char completion_callback_assoc_key[] = "interactive-completion";
#endif

char *Jim_HistoryGetline(const char *prompt)
char *Jim_HistoryGetline(Jim_Interp *interp, const char *prompt)
{
#ifdef USE_LINENOISE
    struct JimCompletionInfo *compinfo = Jim_GetAssocData(interp, completion_callback_assoc_key);
    char *result;
    Jim_Obj *objPtr;
    long mlmode = 0;
    if (compinfo) {
        linenoiseSetCompletionCallback(JimCompletionCallback, compinfo);
    }
    objPtr = Jim_GetVariableStr(interp, "history::multiline", JIM_NONE);
    if (objPtr && Jim_GetLong(interp, objPtr, &mlmode) == JIM_NONE) {
        linenoiseSetMultiLine(mlmode);
    }

    return linenoise(prompt);
    result = linenoise(prompt);

    linenoiseSetCompletionCallback(NULL, NULL);
    return result;
#else
    int len;
    char *line = malloc(MAX_LINE_LEN);

    fputs(prompt, stdout);
    fflush(stdout);

21990
21991
21992
21993
21994
21995
21996





21997



21998
21999
22000
22001
22002
22003
22004

22005
22006
22007
22008
22009
22010
22011
22012


























































22013
22014
22015
22016
22017
22018
22019
22020
22021
22022
22023
22024
22025
22026
22027


22028
22029
22030
22031
22032
22033
22034
22197
22198
22199
22200
22201
22202
22203
22204
22205
22206
22207
22208
22209
22210
22211
22212
22213
22214
22215
22216
22217
22218

22219
22220
22221
22222
22223
22224
22225
22226
22227
22228
22229
22230
22231
22232
22233
22234
22235
22236
22237
22238
22239
22240
22241
22242
22243
22244
22245
22246
22247
22248
22249
22250
22251
22252
22253
22254
22255
22256
22257
22258
22259
22260
22261
22262
22263
22264
22265
22266
22267
22268
22269
22270
22271
22272
22273
22274
22275
22276
22277
22278
22279
22280
22281
22282
22283
22284
22285
22286
22287
22288
22289
22290
22291
22292
22293
22294
22295
22296
22297
22298
22299
22300
22301
22302
22303
22304
22305
22306
22307
22308
22309







+
+
+
+
+

+
+
+






-
+








+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+















+
+







    linenoiseHistoryAdd(line);
#endif
}

void Jim_HistorySave(const char *filename)
{
#ifdef USE_LINENOISE
#ifdef HAVE_UMASK
    mode_t mask;

    mask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
#endif
    linenoiseHistorySave(filename);
#ifdef HAVE_UMASK
    umask(mask);
#endif
#endif
}

void Jim_HistoryShow(void)
{
#ifdef USE_LINENOISE
    

    int i;
    int len;
    char **history = linenoiseHistory(&len);
    for (i = 0; i < len; i++) {
        printf("%4d %s\n", i + 1, history[i]);
    }
#endif
}

#ifdef USE_LINENOISE
struct JimCompletionInfo {
    Jim_Interp *interp;
    Jim_Obj *command;
};

static void JimCompletionCallback(const char *prefix, linenoiseCompletions *comp, void *userdata)
{
    struct JimCompletionInfo *info = (struct JimCompletionInfo *)userdata;
    Jim_Obj *objv[2];
    int ret;

    objv[0] = info->command;
    objv[1] = Jim_NewStringObj(info->interp, prefix, -1);

    ret = Jim_EvalObjVector(info->interp, 2, objv);


    if (ret == JIM_OK) {
        int i;
        Jim_Obj *listObj = Jim_GetResult(info->interp);
        int len = Jim_ListLength(info->interp, listObj);
        for (i = 0; i < len; i++) {
            linenoiseAddCompletion(comp, Jim_String(Jim_ListGetIndex(info->interp, listObj, i)));
        }
    }
}

static void JimHistoryFreeCompletion(Jim_Interp *interp, void *data)
{
    struct JimCompletionInfo *compinfo = data;

    Jim_DecrRefCount(interp, compinfo->command);

    Jim_Free(compinfo);
}
#endif

void Jim_HistorySetCompletion(Jim_Interp *interp, Jim_Obj *commandObj)
{
#ifdef USE_LINENOISE
    if (commandObj) {

        Jim_IncrRefCount(commandObj);
    }

    Jim_DeleteAssocData(interp, completion_callback_assoc_key);

    if (commandObj) {
        struct JimCompletionInfo *compinfo = Jim_Alloc(sizeof(*compinfo));
        compinfo->interp = interp;
        compinfo->command = commandObj;

        Jim_SetAssocData(interp, completion_callback_assoc_key, JimHistoryFreeCompletion, compinfo);
    }
#endif
}

int Jim_InteractivePrompt(Jim_Interp *interp)
{
    int retcode = JIM_OK;
    char *history_file = NULL;
#ifdef USE_LINENOISE
    const char *home;

    home = getenv("HOME");
    if (home && isatty(STDIN_FILENO)) {
        int history_len = strlen(home) + sizeof("/.jim_history");
        history_file = Jim_Alloc(history_len);
        snprintf(history_file, history_len, "%s/.jim_history", home);
        Jim_HistoryLoad(history_file);
    }

    Jim_HistorySetCompletion(interp, Jim_NewStringObj(interp, "tcl::autocomplete", -1));
#endif

    printf("Welcome to Jim version %d.%d\n",
        JIM_VERSION / 100, JIM_VERSION % 100);
    Jim_SetVariableStrWithStr(interp, JIM_INTERACTIVE, "1");

    while (1) {
22053
22054
22055
22056
22057
22058
22059
22060

22061
22062
22063
22064
22065
22066
22067
22068
22069
22070

22071
22072
22073
22074
22075
22076
22077
22078
22079
22080
22081
22082

22083
22084
22085
22086
22087
22088
22089
22328
22329
22330
22331
22332
22333
22334

22335
22336
22337
22338
22339
22340
22341
22342
22343
22344

22345
22346
22347
22348
22349
22350
22351
22352
22353
22354
22355
22356

22357
22358
22359
22360
22361
22362
22363
22364







-
+









-
+











-
+








        scriptObjPtr = Jim_NewStringObj(interp, "", 0);
        Jim_IncrRefCount(scriptObjPtr);
        while (1) {
            char state;
            char *line;

            line = Jim_HistoryGetline(prompt);
            line = Jim_HistoryGetline(interp, prompt);
            if (line == NULL) {
                if (errno == EINTR) {
                    continue;
                }
                Jim_DecrRefCount(interp, scriptObjPtr);
                retcode = JIM_OK;
                goto out;
            }
            if (Jim_Length(scriptObjPtr) != 0) {
                

                Jim_AppendString(interp, scriptObjPtr, "\n", 1);
            }
            Jim_AppendString(interp, scriptObjPtr, line, -1);
            free(line);
            if (Jim_ScriptIsComplete(interp, scriptObjPtr, &state))
                break;

            snprintf(prompt, sizeof(prompt), "%c> ", state);
        }
#ifdef USE_LINENOISE
        if (strcmp(Jim_String(scriptObjPtr), "h") == 0) {
            

            Jim_HistoryShow();
            Jim_DecrRefCount(interp, scriptObjPtr);
            continue;
        }

        Jim_HistoryAdd(Jim_String(scriptObjPtr));
        if (history_file) {
22102
22103
22104
22105
22106
22107
22108

22109
22110
22111
22112
22113
22114
22115
22116
22117
22118
22119
22120
22121
22122
22123
22124
22125

22126
22127
22128
22129
22130
22131
22132
22377
22378
22379
22380
22381
22382
22383
22384
22385
22386
22387
22388
22389
22390
22391
22392
22393
22394
22395
22396
22397
22398
22399
22400

22401
22402
22403
22404
22405
22406
22407
22408







+
















-
+







        result = Jim_GetString(Jim_GetResult(interp), &reslen);
        if (reslen) {
            printf("%s\n", result);
        }
    }
  out:
    Jim_Free(history_file);

    return retcode;
}

#include <stdio.h>
#include <stdlib.h>
#include <string.h>



extern int Jim_initjimshInit(Jim_Interp *interp);

static void JimSetArgv(Jim_Interp *interp, int argc, char *const argv[])
{
    int n;
    Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);

    

    for (n = 0; n < argc; n++) {
        Jim_Obj *obj = Jim_NewStringObj(interp, argv[n], -1);

        Jim_ListAppendElement(interp, listObj, obj);
    }

    Jim_SetVariableStr(interp, "argv", listObj);
22144
22145
22146
22147
22148
22149
22150
22151
22152
22153
22154
22155
22156






22157
22158
22159
22160
22161
22162
22163
22164
22165

22166
22167
22168
22169
22170
22171
22172
22173
22174
22175

22176
22177
22178
22179

22180
22181
22182
22183
22184
22185
22186
22187
22188
22189

22190
22191
22192
22193
22194
22195
22196
22197
22198
22199

22200
22201

22202
22203
22204
22205
22206
22207
22208
22209
22210



22211


22212
22213
22214
22215
22216
22217
22218
22420
22421
22422
22423
22424
22425
22426






22427
22428
22429
22430
22431
22432
22433
22434
22435
22436
22437
22438
22439
22440

22441
22442
22443
22444
22445
22446
22447
22448
22449
22450

22451
22452
22453
22454

22455
22456
22457
22458
22459
22460
22461
22462
22463
22464

22465
22466
22467
22468
22469
22470
22471
22472
22473
22474

22475
22476

22477
22478
22479
22480
22481
22482
22483
22484
22485
22486
22487
22488
22489

22490
22491
22492
22493
22494
22495
22496
22497
22498







-
-
-
-
-
-
+
+
+
+
+
+








-
+









-
+



-
+









-
+









-
+

-
+









+
+
+
-
+
+







    printf("jimsh version %d.%d\n", JIM_VERSION / 100, JIM_VERSION % 100);
    printf("Usage: %s\n", executable_name);
    printf("or   : %s [options] [filename]\n", executable_name);
    printf("\n");
    printf("Without options: Interactive mode\n");
    printf("\n");
    printf("Options:\n");
    printf("         --version  : prints the version string\n");
    printf("         --help     : prints this text\n");
    printf("         -e CMD     : executes command CMD\n");
    printf("                      NOTE: all subsequent options will be passed as arguments to the command\n");
    printf("         [filename] : executes the script contained in the named file\n");
    printf("                      NOTE: all subsequent options will be passed to the script\n\n");
    printf("      --version  : prints the version string\n");
    printf("      --help     : prints this text\n");
    printf("      -e CMD     : executes command CMD\n");
    printf("                   NOTE: all subsequent options will be passed as arguments to the command\n");
    printf("    [filename|-] : executes the script contained in the named file, or from stdin if \"-\"\n");
    printf("                   NOTE: all subsequent options will be passed to the script\n\n");
}

int main(int argc, char *const argv[])
{
    int retcode;
    Jim_Interp *interp;
    char *const orig_argv0 = argv[0];

    

    if (argc > 1 && strcmp(argv[1], "--version") == 0) {
        printf("%d.%d\n", JIM_VERSION / 100, JIM_VERSION % 100);
        return 0;
    }
    else if (argc > 1 && strcmp(argv[1], "--help") == 0) {
        usage(argv[0]);
        return 0;
    }

    

    interp = Jim_CreateInterp();
    Jim_RegisterCoreCommands(interp);

    

    if (Jim_InitStaticExtensions(interp) != JIM_OK) {
        JimPrintErrorMessage(interp);
    }

    Jim_SetVariableStrWithStr(interp, "jim::argv0", orig_argv0);
    Jim_SetVariableStrWithStr(interp, JIM_INTERACTIVE, argc == 1 ? "1" : "0");
    retcode = Jim_initjimshInit(interp);

    if (argc == 1) {
        

        if (retcode == JIM_ERR) {
            JimPrintErrorMessage(interp);
        }
        if (retcode != JIM_EXIT) {
            JimSetArgv(interp, 0, NULL);
            retcode = Jim_InteractivePrompt(interp);
        }
    }
    else {
        

        if (argc > 2 && strcmp(argv[1], "-e") == 0) {
            

            JimSetArgv(interp, argc - 3, argv + 3);
            retcode = Jim_Eval(interp, argv[2]);
            if (retcode != JIM_ERR) {
                printf("%s\n", Jim_String(Jim_GetResult(interp)));
            }
        }
        else {
            Jim_SetVariableStr(interp, "argv0", Jim_NewStringObj(interp, argv[1], -1));
            JimSetArgv(interp, argc - 2, argv + 2);
            if (strcmp(argv[1], "-") == 0) {
                retcode = Jim_Eval(interp, "eval [info source [stdin read] stdin 1]");
            } else {
            retcode = Jim_EvalFile(interp, argv[1]);
                retcode = Jim_EvalFile(interp, argv[1]);
            }
        }
        if (retcode == JIM_ERR) {
            JimPrintErrorMessage(interp);
        }
    }
    if (retcode == JIM_EXIT) {
        retcode = Jim_GetExitCode(interp);

Changes to 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
1
2
3
4
5

6
7
8

9
10


11
12
13

14
15
16
17
18
19
20
21
22
23


24
25
26
27

28
29

30
31
32
33
34
35
36
37





-
+


-
+

-
-
+
+

-
+









-
-
+
+
+

-
+

-
+







# 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
# 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.
# 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.
# 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
# '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 the pkg-config
# 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.
# 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=0)
# 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

44
45
46
47
48
49
50
51
52


53
54
55
56
57
58
59
45
46
47
48
49
50
51


52
53
54
55
56
57
58
59
60







-
-
+
+







		}
	} else {
		msg-result $version
		define PKG_CONFIG_VERSION $version

		set found 1

		if {[opt-val sysroot] ne ""} {
			define SYSROOT [file-normalize [opt-val sysroot]]
		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 {
79
80
81
82
83
84
85
86

87
88
89
90
91

92
93
94

95
96

97
98
99
100
101
102
103
80
81
82
83
84
85
86

87
88
89
90
91

92
93
94

95
96

97
98
99
100
101
102
103
104







-
+




-
+


-
+

-
+







	}
	define HAVE_PKG_CONFIG $found
	return $found
}

# @pkg-config module ?requirements?
#
# Use pkg-config to find the given module meeting the given 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:
# 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_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)
## 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..."
120
121
122
123
124
125
126
127

128
129
130


131
132
133
134
121
122
123
124
125
126
127

128
129


130
131
132
133
134
135







-
+

-
-
+
+




	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
# 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.
# 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} ""
}

Changes to 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
1
2
3
4
5
6

7
8


9
10
11

12
13
14
15
16
17
18
19
20



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80



81
82
83
84
85
86
87
88
89
90
91



92
93
94
95
96
97
98
99
100
101
102

103
104
105
106
107
108
109
110


111
112
113

114
115
116
117
118
119
120
121


122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233


234
235
236
237
238




239
240
241
242
243

244
245
246
247

248
249
250
251
252
253

254
255
256
257
258
259


260
261
262

263
264
265

266
267
268
269
270
271
272
273






-
+

-
-
+
+

-
+








-
-
-
+
+
+
+
+
+
+
+







-
+














+


+


+
+
+
-
+

+
+
+
+
+














-
-
-
+
+
+
+







-
-
-
+
+
+
+







-
+







-
-
+
+

-
+





+
+
-
-
+
+
+











+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


-
-
+
+
+


-
-
-
-
+
+
+
+

-
+



-
+





-
+
+



+
-
-
+
+

-
+
+

-
+







# 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
# 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 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:
# It defines the following variables, based on '--prefix' unless overridden by the user:
#
## datadir
## sysconfdir
## sharedstatedir
## localstatedir
## infodir
## mandir
## includedir

# Do "define defaultprefix myvalue" to set the default prefix *before* the first "use"
set defaultprefix [get-define defaultprefix /usr/local]
#
# 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 =>			{the target directory for the build (defaults to '$defaultprefix')}
	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',
# Returns 1 if exists, or 0 if  not
# 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 the feature if defined, or $default if not.
# See 'feature-define-name' for how the feature name
# is translated into the define name.
# 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 the given value.
# See 'feature-define-name' for how the feature name
# is translated into the define name.
# 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 the feature has been checked, whether true or not
# 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 name to the corresponding define,
# e.g. sys/stat.h becomes HAVE_SYS_STAT_H.
# 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.
# 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 $file doesn't exist, or it's contents are different than $buf,
# the file is written and $script is executed.
# 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.
# If $outfile is blank/omitted, $template should end with ".in" which
# 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
# 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
# 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 == value
## @if NAME eq "value"
## lines
## @else
## lines
## @endif
#
# Where 'name' is a defined variable name and @else is optional.
# 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
## @if name != value
## @if !NAME  (opposite of the form above)
## @if <general-tcl-expression>
#
# Where the first form is true if the variable is defined, but not empty or 0
# In the general Tcl expression, any words beginning with an uppercase letter
# are translated into [get-define NAME]
#
# Currently these expressions can't be nested.
# 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"
	}
167
168
169
170
171
172
173



174
175
176
177




178
179
180
181
182
183
184
185


186
187

188
189

190
191
192


193
194
195



196
197

198
199
200
201
202
203
204
205
206
207
208
209
210
211
212



213
214
215
216

217
218
219
220
221
222
223
224

225
226
227
228
229
230
231
232
233
234










235


236
237
238
239
240
241
242
243



244
245
246
247

248
249
250
251
252
253
254

255
256
257
258
259
260
261
262
263
264
265

266




267






268





269
270
271
272
273
274
275
287
288
289
290
291
292
293
294
295
296




297
298
299
300








301
302


303


304



305
306



307
308
309


310















311
312
313
314
315
316

317
318
319
320
321
322
323
324

325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345

346
347
348
349
350
351
352
353


354
355
356
357
358


359
360
361
362
363
364
365

366
367
368
369

370

371
372
373
374

375
376
377
378
379
380

381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399







+
+
+
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
+
+
-
-
+
-
-
+
-
-
-
+
+
-
-
-
+
+
+
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+



-
+







-
+










+
+
+
+
+
+
+
+
+
+
-
+
+






-
-
+
+
+


-
-
+






-
+



-

-




-
+

+
+
+
+
-
+
+
+
+
+
+

+
+
+
+
+







	# 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
	}
		set mapping {}
		foreach {n v} [array get ::define] {
			lappend mapping @$n@ $v
		}
	set result {}
	foreach line [split [readfile $infile] \n] {
		if {[info exists cond]} {
			set l [string trimright $line]
			if {$l eq "@endif"} {
				unset cond
				continue
			}
		return $mapping
	}
			if {$l eq "@else"} {
				set cond [expr {!$cond}]
	set mapping [build-define-mapping]
				continue
			}

			if {$cond} {
				lappend result $line
			}
	set result [include-file $infile $mapping]

			continue
		}
		if {[regexp {^@if\s+(\w+)(.*)} $line -> name expression]} {
	# 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.
			lassign $expression equal value
			set varval [get-define $name ""]
	set mapping [build-define-mapping]
			if {$equal eq ""} {
				set cond [expr {$varval ni {"" 0}}]
			} else {
				set cond [expr {$varval eq $value}]
				if {$equal ne "=="} {
					set cond [expr {!$cond}]
				}
			}
			continue
		}
		lappend result $line
	}
    write-if-changed $out [string map $mapping [join $result \n]]\n {
        msg-result "Created [relative-path $out] from [relative-path $template]"
    }
	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
set build [opt-val build]
opt-str build build ""
define build_alias $build
if {$build eq ""} {
	define build [config_guess]
} else {
	define build [config_sub $build]
}

set host [opt-val host]
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
}
set prefix [opt-val prefix $defaultprefix]

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)
# Allow this to come from the environment
define top_srcdir [get-env top_srcdir [get-define 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
set exec_prefix [opt-val exec-prefix $prefix]
define exec_prefix $exec_prefix
define exec_prefix [opt-str exec-prefix exec_prefix $prefix]
foreach {name defpath} {
	bindir /bin
	sbindir /sbin
	libexecdir /libexec
	libdir /lib
} {
	define $name [opt-val $name $exec_prefix$defpath]
	define $name [opt-str $name o $exec_prefix$defpath]
}
foreach {name defpath} {
	datadir /share
	sysconfdir /etc
	sharedstatedir /com
	localstatedir /var
	infodir /share/info
	mandir /share/man
	includedir /include
} {
	define $name [opt-val $name $prefix$defpath]
	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
	}

Deleted 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

Changes to autosetup/tmake.auto.

19
20
21
22
23
24
25
26

27
28
29
30


31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

46
47
48
49
50
51
52
53
54

55
56




57
58


59
60
61
62
63
64
65
66
19
20
21
22
23
24
25

26
27
28
29
30
31
32
33
34
35
36
37
38









39
40








41
42
43
44
45
46
47


48
49


50
51
52
53
54
55







-
+




+
+






-
-
-
-
-
-
-
-
-
+

-
-
-
-
-
-
-
-
+


+
+
+
+
-
-
+
+
-
-






}

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]*}
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

# We use [set AUTOREMAKE] here to avoid rebuilding settings.conf
# if the AUTOREMAKE command changes
Depends {settings.conf include/autoconf.h} auto.def -msg {note Configuring...} -do {
	run [set AUTOREMAKE] >$build/config.out
} -onerror {puts [readfile $build/config.out]} -fatal
Clean config.out
DistClean --source config.log
DistClean settings.conf include/autoconf.h
Autosetup include/autoconf.h

# If not configured, configure with default options
# Note that it is expected that configure will normally be run
# separately. This is just a convenience for a host build
define? AUTOREMAKE configure TOPBUILDDIR=$TOPBUILDDIR --conf=auto.def

Load settings.conf

# e.g. for up 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 CONFIGURED

	ifconfig false
}
# Hmmm, but should we turn off AutoSubDirs?
#AutoSubDirs off
}

	if {![file exists build.spec]} {
		puts "Note: I don't see build.spec. Try running: tmake --genie"
	}
}

Changes to autosetup/tmake.tcl.

13
14
15
16
17
18
19
20

21
22

23
24
25
26
27
28
29
30

31
32
33
34
35
36
37
13
14
15
16
17
18
19

20
21

22
23
24
25
26
27
28
29

30
31
32
33
34
35
36
37







-
+

-
+







-
+








module-options {}

define CONFIGURED

# @make-tmake-settings outfile patterns ...
#
# Examines all defined variables which match the given patterns (defaults to "*")
# 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:
# 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]*}
## 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 *
	}

Changes to compat/zlib/CMakeLists.txt.

1

2
3
4
5
6

7
8

9
10
11
12
13
14
15
16

1
2
3
4
5

6
7

8

9
10
11
12
13
14
15
-
+




-
+

-
+
-







cmake_minimum_required(VERSION 2.4.4)
cmake_minimum_required(VERSION 2.4.4...3.15.0)
set(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS ON)

project(zlib C)

set(VERSION "1.2.11")
set(VERSION "1.3.1")

option(ASM686 "Enable building i686 assembly implementation")
option(ZLIB_BUILD_EXAMPLES "Enable Zlib Examples" ON)
option(AMD64 "Enable building amd64 assembly implementation")

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")

125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
124
125
126
127
128
129
130

































131
132
133
134
135
136
137







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-








if(NOT MINGW)
    set(ZLIB_DLL_SRCS
        win32/zlib1.rc # If present will override custom build rule below.
    )
endif()

if(CMAKE_COMPILER_IS_GNUCC)
    if(ASM686)
        set(ZLIB_ASMS contrib/asm686/match.S)
    elseif (AMD64)
        set(ZLIB_ASMS contrib/amd64/amd64-match.S)
    endif ()

	if(ZLIB_ASMS)
		add_definitions(-DASMV)
		set_source_files_properties(${ZLIB_ASMS} PROPERTIES LANGUAGE C COMPILE_FLAGS -DNO_UNDERLINE)
	endif()
endif()

if(MSVC)
    if(ASM686)
		ENABLE_LANGUAGE(ASM_MASM)
        set(ZLIB_ASMS
			contrib/masmx86/inffas32.asm
			contrib/masmx86/match686.asm
		)
    elseif (AMD64)
		ENABLE_LANGUAGE(ASM_MASM)
        set(ZLIB_ASMS
			contrib/masmx64/gvmat64.asm
			contrib/masmx64/inffasx64.asm
		)
    endif()

	if(ZLIB_ASMS)
		add_definitions(-DASMV -DASMINF)
	endif()
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.
179
180
181
182
183
184
185
186
187




188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205

206
207
208
209
210
211
212
145
146
147
148
149
150
151


152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172

173
174
175
176
177
178
179
180







-
-
+
+
+
+

















-
+







                            -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_ASMS} ${ZLIB_DLL_SRCS} ${ZLIB_PUBLIC_HDRS} ${ZLIB_PRIVATE_HDRS})
add_library(zlibstatic STATIC ${ZLIB_SRCS} ${ZLIB_ASMS} ${ZLIB_PUBLIC_HDRS} ${ZLIB_PRIVATE_HDRS})
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)
   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()

225
226
227
228
229
230
231
232
233
234
235




236
237
238


239
240
241
242
243
244





245
246
247
248
249





193
194
195
196
197
198
199




200
201
202
203
204


205
206
207





208
209
210
211
212
213




214
215
216
217
218







-
-
-
-
+
+
+
+

-
-
+
+

-
-
-
-
-
+
+
+
+
+

-
-
-
-
+
+
+
+
+
if(NOT SKIP_INSTALL_FILES AND NOT SKIP_INSTALL_ALL )
    install(FILES ${ZLIB_PC} DESTINATION "${INSTALL_PKGCONFIG_DIR}")
endif()

#============================================================================
# Example binaries
#============================================================================

add_executable(example test/example.c)
target_link_libraries(example zlib)
add_test(example example)
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)
    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)
    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()
        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()

Changes to compat/zlib/ChangeLog.

1
2
3







































































































4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113



+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+








                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
92
93
94
95
96
97
98
99

100
101
102
103
104
105
106
195
196
197
198
199
200
201

202
203
204
205
206
207
208
209







-
+







- 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
- Suport i686 and amd64 assembler builds in CMakeLists.txt
- 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
292
293
294
295
296
297
298
299

300
301
302
303
304
305
306

307
308
309
310
311
312
313
395
396
397
398
399
400
401

402
403
404
405
406
407
408

409
410
411
412
413
414
415
416







-
+






-
+







- 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 indeterminancy
- 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 assmebler code [Londer]
- 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
507
508
509
510
511
512
513
514

515
516
517
518
519
520
521
610
611
612
613
614
615
616

617
618
619
620
621
622
623
624







-
+







- 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 .
- 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
681
682
683
684
685
686
687
688

689
690
691
692
693
694
695
784
785
786
787
788
789
790

791
792
793
794
795
796
797
798







-
+







- 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.
  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]
717
718
719
720
721
722
723
724

725
726
727
728
729
730
731
820
821
822
823
824
825
826

827
828
829
830
831
832
833
834







-
+







- 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.
  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
788
789
790
791
792
793
794
795

796
797
798
799
800
801
802
891
892
893
894
895
896
897

898
899
900
901
902
903
904
905







-
+








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.
- 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]
966
967
968
969
970
971
972
973

974
975
976
977
978
979
980
1069
1070
1071
1072
1073
1074
1075

1076
1077
1078
1079
1080
1081
1082
1083







-
+







- 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
- Enchance comments in zlib.h on what happens if gzprintf() tries to
- 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]
1032
1033
1034
1035
1036
1037
1038
1039

1040
1041
1042
1043
1044

1045
1046

1047
1048
1049
1050
1051
1052
1053
1135
1136
1137
1138
1139
1140
1141

1142
1143
1144
1145
1146

1147
1148

1149
1150
1151
1152
1153
1154
1155
1156







-
+




-
+

-
+







    - 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.
- 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.
  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).
  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)
1113
1114
1115
1116
1117
1118
1119
1120

1121
1122
1123
1124
1125
1126
1127
1216
1217
1218
1219
1220
1221
1222

1223
1224
1225
1226
1227
1228
1229
1230







-
+







- 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.
  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)
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
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







-
+













-
+







 . 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)
  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".
  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
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
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







-
-
+
+



-
+











-
+







-
+








-
-
+
+












-
+









-
+




















-
+







- 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.
  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.
- 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().
        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.
        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.
- 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).
- 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.
- 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.
  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
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
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







-
+




















-
+







- 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.
- 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.
  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
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
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







-
+


-
+





-
+


-
+


-
+






-
+

-
+

-
+








-
+




-
+


















-
+

















-
+








-
+









-
+


-
+


-
+


-
+




-
+

-
+





-
+

-
+

-
-
-
+
+
+


-
+

-
+


-
+

-
+

-
+



-
+


- 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 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.
- 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).
- 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.
- 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?).
     (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).
  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).
  (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).
  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.
- 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.
- 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.
- 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.
- 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.
  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.
  TurboC large model

Changes in 0.7 (14 April 95)
- Added full inflate support.
- 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.
  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.
- 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.
- 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).
- 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.
  not yet Z_FULL_FLUSH
- support decompression but only in a single step (forced Z_FINISH)
- added opaque object for zalloc and zfree.
- 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.
- 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.
- avoid "zip" everywhere, use zlib instead of ziplib
- suppress Z_BLOCK_FLUSH, interpret Z_PARTIAL_FLUSH as block flush
  if compression method == 8.
  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 the method parameter for deflateInit2
- added inflateInit2
- simplied considerably deflateInit and inflateInit by not supporting
- simplified considerably deflateInit and inflateInit by not supporting
  user-provided history buffer. This is supported only in deflateInit2
  and inflateInit2.
  and inflateInit2

Changes in 0.3:
- prefix all macro names with Z_
- use Z_FINISH instead of deflateEnd to finish compression.
- use Z_FINISH instead of deflateEnd to finish compression
- added Z_HUFFMAN_ONLY
- added gzerror()

Changes to 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
1
2
3
4
5
6

7
8
9
10
11
12
13
14
15
16

17

18
19
20
21
22
23
24






-
+









-
+
-








                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 lastest zlib FAQ is at http://zlib.net/zlib_faq.html
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.  Pointers to the
    file win32/DLL_FAQ.txt in the zlib distribution.
    precompiled DLL are found in the zlib web site at http://zlib.net/ .

 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

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

Changes to 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
1

2
3
4
5
6
7
8
9




10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

25
26
27
28
29
30

31
32
33
34
35
36
37
38

-
+







-
-
-
-















-
+





-
+







# Makefile for zlib
# Copyright (C) 1995-2017 Jean-loup Gailly, Mark Adler
# 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 use the asm code, type:
#    cp contrib/asm?86/match.S ./match.S
#    make LOC=-DASMV OBJA=match.o

# 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_LDFLAGS=-L. libz.a
TEST_LIBS=-L. libz.a
LDSHARED=$(CC)
CPP=$(CC) -E

STATICLIB=libz.a
SHAREDLIB=libz.so
SHAREDLIBV=libz.so.1.2.11
SHAREDLIBV=libz.so.1.3.1
SHAREDLIBM=libz.so.1
LIBS=$(STATICLIB) $(SHAREDLIBV)

AR=ar
ARFLAGS=rc
RANLIB=ranlib
LDCONFIG=ldconfig
83
84
85
86
87
88
89
90

91
92
93
94
95


96
97
98
99
100
101
102
103

104
105
106
107
108


109
110
111
112

113
114
115
116
117


118
119
120
121
122
123
124
125
126
127

128
129
130
131
132
133
134
79
80
81
82
83
84
85

86
87
88
89


90
91
92
93
94
95
96
97
98

99
100
101
102


103
104
105
106
107

108
109
110
111


112
113
114
115
116
117
118
119
120
121
122

123
124
125
126
127
128
129
130







-
+



-
-
+
+







-
+



-
-
+
+



-
+



-
-
+
+









-
+








check: test

test: all teststatic testshared

teststatic: static
	@TMPST=tmpst_$$; \
	if echo hello world | ./minigzip | ./minigzip -d && ./example $$TMPST ; then \
	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
	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 | ./minigzipsh | ./minigzipsh -d && ./examplesh $$TMPSH; then \
	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
	fi
	@rm -f tmpsh_$$

test64: all64
	@TMP64=tmp64_$$; \
	if echo hello world | ./minigzip64 | ./minigzip64 -d && ./example64 $$TMP64; then \
	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
	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
	./infcover
	${QEMU_RUN} ./infcover
	gcov inf*.c

libz.a: $(OBJS)
	$(AR) $(ARFLAGS) $@ $(OBJS)
	-@ ($(RANLIB) $@ || true) >/dev/null 2>&1

match.o: match.S
200
201
202
203
204
205
206
207

208
209
210
211
212

213
214
215
216
217

218
219
220
221
222

223
224
225
226
227

228
229
230
231
232

233
234
235
236
237

238
239
240
241
242

243
244
245
246
247

248
249
250
251
252

253
254
255
256
257

258
259
260
261
262

263
264
265
266
267

268
269
270
271
272

273
274
275
276
277

278
279
280
281
282
283
284
285
286
287
288
289

290
291
292

293
294
295

296
297
298

299
300
301

302
303
304

305
306
307
308
309
310
311
196
197
198
199
200
201
202

203
204
205
206
207

208
209
210
211
212

213
214
215
216
217

218
219
220
221
222

223
224
225
226
227

228
229
230
231
232

233
234
235
236
237

238
239
240
241
242

243
244
245
246
247

248
249
250
251
252

253
254
255
256
257

258
259
260
261
262

263
264
265
266
267

268
269
270
271
272

273
274
275
276
277
278
279
280
281
282
283
284

285
286
287

288
289
290

291
292
293

294
295
296

297
298
299

300
301
302
303
304
305
306
307







-
+




-
+




-
+




-
+




-
+




-
+




-
+




-
+




-
+




-
+




-
+




-
+




-
+




-
+




-
+











-
+


-
+


-
+


-
+


-
+


-
+








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) -DPIC -c -o objs/adler32.o $(SRCDIR)adler32.c
	$(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) -DPIC -c -o objs/crc32.o $(SRCDIR)crc32.c
	$(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) -DPIC -c -o objs/deflate.o $(SRCDIR)deflate.c
	$(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) -DPIC -c -o objs/infback.o $(SRCDIR)infback.c
	$(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) -DPIC -c -o objs/inffast.o $(SRCDIR)inffast.c
	$(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) -DPIC -c -o objs/inflate.o $(SRCDIR)inflate.c
	$(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) -DPIC -c -o objs/inftrees.o $(SRCDIR)inftrees.c
	$(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) -DPIC -c -o objs/trees.o $(SRCDIR)trees.c
	$(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) -DPIC -c -o objs/zutil.o $(SRCDIR)zutil.c
	$(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) -DPIC -c -o objs/compress.o $(SRCDIR)compress.c
	$(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) -DPIC -c -o objs/uncompr.o $(SRCDIR)uncompr.c
	$(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) -DPIC -c -o objs/gzclose.o $(SRCDIR)gzclose.c
	$(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) -DPIC -c -o objs/gzlib.o $(SRCDIR)gzlib.c
	$(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) -DPIC -c -o objs/gzread.o $(SRCDIR)gzread.c
	$(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) -DPIC -c -o objs/gzwrite.o $(SRCDIR)gzwrite.c
	$(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) -o $@ example.o $(TEST_LDFLAGS)
	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ example.o $(TEST_LIBS)

minigzip$(EXE): minigzip.o $(STATICLIB)
	$(CC) $(CFLAGS) -o $@ minigzip.o $(TEST_LDFLAGS)
	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ minigzip.o $(TEST_LIBS)

examplesh$(EXE): example.o $(SHAREDLIBV)
	$(CC) $(CFLAGS) -o $@ example.o -L. $(SHAREDLIBV)
	$(CC) $(CFLAGS) -o $@ example.o $(LDFLAGS) -L. $(SHAREDLIBV)

minigzipsh$(EXE): minigzip.o $(SHAREDLIBV)
	$(CC) $(CFLAGS) -o $@ minigzip.o -L. $(SHAREDLIBV)
	$(CC) $(CFLAGS) -o $@ minigzip.o $(LDFLAGS) -L. $(SHAREDLIBV)

example64$(EXE): example64.o $(STATICLIB)
	$(CC) $(CFLAGS) -o $@ example64.o $(TEST_LDFLAGS)
	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ example64.o $(TEST_LIBS)

minigzip64$(EXE): minigzip64.o $(STATICLIB)
	$(CC) $(CFLAGS) -o $@ minigzip64.o $(TEST_LDFLAGS)
	$(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
359
360
361
362
363
364
365






366
367

368
369
370
371
372
373
374
375
376
377
378
379

380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
355
356
357
358
359
360
361
362
363
364
365
366
367
368

369
370
371
372
373
374
375
376
377
378
379
380

381
382
383
384
385
386
387


388
389
390
391
392
393
394







+
+
+
+
+
+

-
+











-
+






-
-







	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:
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 docs
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
	@if [ ! -f zconf.h.in ]; then rm -f zconf.h zconf.h.cmakein ; fi
	@if [ ! -f zlib.3 ]; then rm -f zlib.3.pdf ; 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

Changes to compat/zlib/README.

1
2
3

4
5
6
7
8
9
10
1
2

3
4
5
6
7
8
9
10


-
+







ZLIB DATA COMPRESSION LIBRARY

zlib 1.2.11 is a general purpose data compression library.  All the code is
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
25
26
27
28
29
30
31
32

33
34

35
36
37
38
39


40
41

42
43

44
45
46
47
48
49
50
25
26
27
28
29
30
31

32
33

34
35
36
37


38
39
40

41


42
43
44
45
46
47
48
49







-
+

-
+



-
-
+
+

-
+
-
-
+







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
http://marknelson.us/1997/01/01/zlib-engine/ .
https://marknelson.us/posts/1997/01/01/zlib-engine.html .

The changes made in version 1.2.11 are documented in the file ChangeLog.
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, documented at
http://java.sun.com/developer/technicalArticles/Programming/compression/ .
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 written by Paul Marquess <pmqs@cpan.org> is available
A Perl interface to zlib and bzip2 written by Paul Marquess <pmqs@cpan.org>
at CPAN (Comprehensive Perl Archive Network) sites, including
http://search.cpan.org/~pmqs/IO-Compress-Zlib/ .
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 .

60
61
62
63
64
65
66
67

68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87

88
89
90
91
92
93
94
59
60
61
62
63
64
65

66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85

86
87
88
89
90
91
92
93







-
+



















-
+







- 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 (formely OSF/1) on AlphaServer, the cc option -std1 is
- 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-2017 Jean-loup Gailly and Mark Adler
 (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
104
105
106
107
108
109
110
111




112
113
114
115
103
104
105
106
107
108
109

110
111
112
113
114
115
116
117







-
+
+
+
+





  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.
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.

Changes to compat/zlib/adler32.c.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1
2
3
4
5
6
7
8
9


10
11
12
13
14
15
16









-
-







/* 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"

local uLong adler32_combine_ OF((uLong adler1, uLong adler2, z_off64_t len2));

#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);
56
57
58
59
60
61
62
63

64
65
66
67
68
69
70
71
72
73
74
54
55
56
57
58
59
60

61




62
63
64
65
66
67
68







-
+
-
-
-
-







#else
#  define MOD(a) a %= BASE
#  define MOD28(a) a %= BASE
#  define MOD63(a) a %= BASE
#endif

/* ========================================================================= */
uLong ZEXPORT adler32_z(adler, buf, len)
uLong ZEXPORT adler32_z(uLong adler, const Bytef *buf, z_size_t len) {
    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;

127
128
129
130
131
132
133
134

135
136
137
138
139
140
141
142
143

144
145
146
147
148
149
150
151
152
153
154
121
122
123
124
125
126
127

128




129
130
131
132

133




134
135
136
137
138
139
140







-
+
-
-
-
-




-
+
-
-
-
-







    }

    /* return recombined sums */
    return adler | (sum2 << 16);
}

/* ========================================================================= */
uLong ZEXPORT adler32(adler, buf, len)
uLong ZEXPORT adler32(uLong adler, const Bytef *buf, uInt len) {
    uLong adler;
    const Bytef *buf;
    uInt len;
{
    return adler32_z(adler, buf, len);
}

/* ========================================================================= */
local uLong adler32_combine_(adler1, adler2, len2)
local uLong adler32_combine_(uLong adler1, uLong adler2, z_off64_t len2) {
    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;
165
166
167
168
169
170
171
172

173
174
175
176
177
178
179
180

181
182
183
184
185
186
151
152
153
154
155
156
157

158




159
160
161

162




163
164







-
+
-
-
-
-



-
+
-
-
-
-


    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(adler1, adler2, len2)
uLong ZEXPORT adler32_combine(uLong adler1, uLong adler2, z_off_t len2) {
    uLong adler1;
    uLong adler2;
    z_off_t len2;
{
    return adler32_combine_(adler1, adler2, len2);
}

uLong ZEXPORT adler32_combine64(adler1, adler2, len2)
uLong ZEXPORT adler32_combine64(uLong adler1, uLong adler2, z_off64_t len2) {
    uLong adler1;
    uLong adler2;
    z_off64_t len2;
{
    return adler32_combine_(adler1, adler2, len2);
}

Changes to compat/zlib/compress.c.

15
16
17
18
19
20
21
22
23
24
25
26


27
28
29
30
31
32
33
34
35
15
16
17
18
19
20
21





22
23


24
25
26
27
28
29
30







-
-
-
-
-
+
+
-
-







   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 (dest, destLen, source, sourceLen, level)
    Bytef *dest;
    uLongf *destLen;
    const Bytef *source;
    uLong sourceLen;
int ZEXPORT compress2(Bytef *dest, uLongf *destLen, const Bytef *source,
                      uLong sourceLen, int level) {
    int level;
{
    z_stream stream;
    int err;
    const uInt max = (uInt)-1;
    uLong left;

    left = *destLen;
    *destLen = 0;
61
62
63
64
65
66
67
68
69
70
71
72


73
74
75
76
77
78
79
80
81

82
83
84
85
86
56
57
58
59
60
61
62





63
64

65
66
67
68
69
70
71

72


73
74
75







-
-
-
-
-
+
+
-







-
+
-
-



    *destLen = stream.total_out;
    deflateEnd(&stream);
    return err == Z_STREAM_END ? Z_OK : err;
}

/* ===========================================================================
 */
int ZEXPORT compress (dest, destLen, source, sourceLen)
    Bytef *dest;
    uLongf *destLen;
    const Bytef *source;
    uLong sourceLen;
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 (sourceLen)
uLong ZEXPORT compressBound(uLong sourceLen) {
    uLong sourceLen;
{
    return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) +
           (sourceLen >> 25) + 13;
}

Changes to compat/zlib/configure.

21
22
23
24
25
26
27
28

29
30
31
32
33
34
35


36


37
38
39
40
41
42
43
44

45
46

47
48
49
50
51
52
53
21
22
23
24
25
26
27

28
29
30
31
32
33
34

35
36
37
38
39
40
41
42
43
44
45
46

47


48
49
50
51
52
53
54
55







-
+






-
+
+

+
+







-
+
-
-
+







# get source directory
SRCDIR=`dirname $0`
if test $SRCDIR = "."; then
    ZINC=""
    ZINCOUT="-I."
    SRCDIR=""
else
    ZINC='-include zconf.h'
    ZINC='-I. -include zconf.h'
    ZINCOUT='-I. -I$(SRCDIR)'
    SRCDIR="$SRCDIR/"
fi

# set command prefix for cross-compilation
if [ -n "${CHOST}" ]; then
    uname="`echo "${CHOST}" | sed -e 's/^[^-]*-\([^-]*\)$/\1/' -e 's/^[^-]*-[^-]*-\([^-]*\)$/\1/' -e 's/^[^-]*-[^-]*-\([^-]*\)-.*$/\1/'`"
    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=`sed -n -e '/VERSION "/s/.*"\([0-9]*\\.[0-9]*\\.[0-9]*\).*/\1/p' < ${SRCDIR}zlib.h`
VER3=`echo ${VER}|sed -n -e 's/\([0-9]\{1,\}\(\\.[0-9]\{1,\}\)\{1,2\}\).*/\1/p'`
VER2=`sed -n -e '/VERSION "/s/.*"\([0-9]*\\.[0-9]*\)\\..*/\1/p' < ${SRCDIR}zlib.h`
VER1=`sed -n -e '/VERSION "/s/.*"\([0-9]*\)\\..*/\1/p' < ${SRCDIR}zlib.h`
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"}
83
84
85
86
87
88
89


90
91
92
93
94
95
96
97
98
99
100
101

102
103
104
105
106
107
108
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104

105
106
107
108
109
110
111
112







+
+











-
+







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 -f $test.[co] $test $test$shared_ext $test.gcno ./--version
  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
133
134
135
136
137
138
139



140
141
142
143
144
145
146
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153







+
+
+







    -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

161
162
163
164
165
166
167

168
169
170
171










172
173
174
175
176
177
178
168
169
170
171
172
173
174
175




176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192







+
-
-
-
-
+
+
+
+
+
+
+
+
+
+








# 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
test -z "$CC" && echo Checking for ${CROSS_PREFIX}gcc... | tee -a configure.log
cc=${CC-${CROSS_PREFIX}gcc}
cflags=${CFLAGS-"-O3"}
# to force the asm version use: CFLAGS="-O3 -DASMV" ./configure
  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 ;;
190
191
192
193
194
195
196
197

198
199

200
201






202
203
204
205
206
207
208
209
210





211
212
213
214
215

216
217

218
219
220

221
222
223
224
225
226
227



228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250



























251
252
253
254
255
256
257
204
205
206
207
208
209
210

211
212

213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229

230
231
232
233
234
235
236
237
238

239
240

241

242

243

244
245
246



247
248
249
250






















251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284







-
+

-
+


+
+
+
+
+
+








-
+
+
+
+
+




-
+

-
+
-

-
+
-



-
-
-
+
+
+

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







  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 -pedantic -DZLIB_CONST"
      CFLAGS="${CFLAGS} -Wall -Wextra -Wcast-qual -DZLIB_CONST"
    else
      CFLAGS="${CFLAGS} -Wall -Wextra -pedantic"
      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* | GNU | GNU/* | solaris*)
  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* | OS/2*)
  CYGWIN* | Cygwin* | cygwin* | *-cygwin* | OS/2*)
        EXE='.exe' ;;
  MINGW* | mingw*)
  MINGW* | mingw* | *-mingw*)
# temporary bypass
        rm -f $test.[co] $test $test$shared_ext
        echo "Please use win32/Makefile.gcc instead." | tee -a configure.log
        echo "If this doesn't work for you, try win32/Makefile.gcc." | tee -a configure.log
        leave 1
        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"} ;;
  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 ;;
  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 libtool -V 2>&1 | grep Apple > /dev/null; then
                 AR="libtool"
             else
                 AR="/usr/bin/libtool"
             fi
             ARFLAGS="-o" ;;
  *)             LDSHARED=${LDSHARED-"$cc -shared"} ;;
        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
363
364
365
366
367
368
369
370

371



372
373
374
375
376
377
378
379
380
381
382
383

384


385

386
387
388
389
390
391
392
390
391
392
393
394
395
396

397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414

415
416
417
418
419
420
421
422
423
424
425







-
+

+
+
+












+
-
+
+

+







    test "`( $* ) 2>&1 | tee -a configure.log`" = ""
  }
  echo - using any output from compiler to indicate an error >> configure.log
else
  try()
  {
    show $*
    ( $* ) >> configure.log 2>&1
    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 "$got" >> configure.log
    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; }
405
406
407
408
409
410
411
412

413
414
415
416
417
418
419
438
439
440
441
442
443
444

445
446
447
448
449
450
451
452







-
+







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 -w -c $SFLAGS $test.c &&
  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
429
430
431
432
433
434
435
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
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







-
-
-
-
-
-
-
-
-
-
-
-
-
-










-


-
-
-
+
-
-
+
-
-
-
+







  SHAREDLIBM=""
  echo Building static library $STATICLIB version $VER with $CC. | tee -a configure.log
else
  ALL="static shared"
  TEST="all teststatic testshared"
fi

# check for underscores in external names for use by assembler code
CPP=${CPP-"$CC -E"}
case $CFLAGS in
  *ASMV*)
    echo >> configure.log
    show "$NM $test.o | grep _hello"
    if test "`$NM $test.o | grep _hello | tee -a configure.log`" = ""; then
      CPP="$CPP -DNO_UNDERLINE"
      echo Checking for underline in external names... No. | tee -a configure.log
    else
      echo Checking for underline in external names... Yes. | tee -a configure.log
    fi ;;
esac

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
  need_sizet=0
else
  echo "Checking for size_t... No." | tee -a configure.log
  need_sizet=1
fi

  # find a size_t integer type
echo >> configure.log

  # check for long long
# find the size_t integer type, if needed
if test $need_sizet -eq 1; then
  cat > $test.c <<EOF
  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) {
491
492
493
494
495
496
497


498
499

500
501
502
503
504
505
506
507
508
509
510
511
512
513
504
505
506
507
508
509
510
511
512
513

514

515





516
517
518
519
520
521
522







+
+

-
+
-

-
-
-
-
-







    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 "Failed to find a pointer-size integer type." | tee -a configure.log
    echo "Checking for a pointer-size integer type... not found." | tee -a configure.log
    leave 1
  fi
fi

if test $need_sizet -eq 1; then
  CFLAGS="${CFLAGS} -DNO_SIZE_T=${sizet}"
  SFLAGS="${SFLAGS} -DNO_SIZE_T=${sizet}"
fi

echo >> configure.log

# check for large file support, and if none, check for fseeko()
cat > $test.c <<EOF
#include <sys/types.h>
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
854
855
856
857
858
859
860

861
862
863
864
865
866
867
868
869

870
871
872
873
874
875
876
877







-









-
+







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 Z_U4 = $Z_U4 >> 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

# udpate Makefile with the configure results
# 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#

Changes to 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
58
59
60
61
62
63
64

1
2
3
4
5
6
7
8
9
10








11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26



27
28
29
30
31
32
33
34
35
36










37
38
39
40
41
42
43
-
+









-
-
-
-
-
-
-
-
















-
-
-










-
-
-
-
-
-
-
-
-
-







All files under this contrib directory are UNSUPPORTED. There were
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/

amd64/      by Mikhail Teterin <mi@ALDAN.algebra.com>
        asm code for AMD64
        See patch at http://www.freebsd.org/cgi/query-pr.cgi?pr=bin/96393

asm686/     by Brian Raiter <breadbox@muppetlabs.com>
        asm code for Pentium and PPro/PII, using the AT&T (GNU as) syntax
        See http://www.muppetlabs.com/~breadbox/software/assembly.html

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

inflate86/  by Chris Anderson <christop@charm.net>
        Tuned x86 gcc asm code to replace inflate_fast()

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

masmx64/    by Gilles Vollant <info@winimage.com>
        x86 64-bit (AMD64 and Intel EM64t) code for x64 assembler to
        replace longest_match() and inflate_fast(),  also masm x86
        64-bits translation of Chris Anderson inflate_fast()

masmx86/    by Gilles Vollant <info@winimage.com>
        x86 asm code to replace longest_match() and inflate_fast(),
        for Visual C++ and MASM (32 bits).
        Based on Brian Raiter (asm686) and Chris Anderson (inflate86)

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

Deleted compat/zlib/contrib/ada/buffer_demo.adb.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106










































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
----------------------------------------------------------------
--  ZLib for Ada thick binding.                               --
--                                                            --
--  Copyright (C) 2002-2004 Dmitriy Anisimkov                 --
--                                                            --
--  Open source license information is in the zlib.ads file.  --
----------------------------------------------------------------
--
--  $Id: buffer_demo.adb,v 1.3 2004/09/06 06:55:35 vagul Exp $

--  This demo program provided by Dr Steve Sangwine <sjs@essex.ac.uk>
--
--  Demonstration of a problem with Zlib-Ada (already fixed) when a buffer
--  of exactly the correct size is used for decompressed data, and the last
--  few bytes passed in to Zlib are checksum bytes.

--  This program compresses a string of text, and then decompresses the
--  compressed text into a buffer of the same size as the original text.

with Ada.Streams; use Ada.Streams;
with Ada.Text_IO;

with ZLib; use ZLib;

procedure Buffer_Demo is
   EOL  : Character renames ASCII.LF;
   Text : constant String
     := "Four score and seven years ago our fathers brought forth," & EOL &
        "upon this continent, a new nation, conceived in liberty," & EOL &
        "and dedicated to the proposition that `all men are created equal'.";

   Source : Stream_Element_Array (1 .. Text'Length);
   for Source'Address use Text'Address;

begin
   Ada.Text_IO.Put (Text);
   Ada.Text_IO.New_Line;
   Ada.Text_IO.Put_Line
     ("Uncompressed size : " & Positive'Image (Text'Length) & " bytes");

   declare
      Compressed_Data : Stream_Element_Array (1 .. Text'Length);
      L               : Stream_Element_Offset;
   begin
      Compress : declare
         Compressor : Filter_Type;
         I : Stream_Element_Offset;
      begin
         Deflate_Init (Compressor);

         --  Compress the whole of T at once.

         Translate (Compressor, Source, I, Compressed_Data, L, Finish);
         pragma Assert (I = Source'Last);

         Close (Compressor);

         Ada.Text_IO.Put_Line
           ("Compressed size :   "
            & Stream_Element_Offset'Image (L) & " bytes");
      end Compress;

      --  Now we decompress the data, passing short blocks of data to Zlib
      --  (because this demonstrates the problem - the last block passed will
      --  contain checksum information and there will be no output, only a
      --  check inside Zlib that the checksum is correct).

      Decompress : declare
         Decompressor : Filter_Type;

         Uncompressed_Data : Stream_Element_Array (1 .. Text'Length);

         Block_Size : constant := 4;
         --  This makes sure that the last block contains
         --  only Adler checksum data.

         P : Stream_Element_Offset := Compressed_Data'First - 1;
         O : Stream_Element_Offset;
      begin
         Inflate_Init (Decompressor);

         loop
            Translate
              (Decompressor,
               Compressed_Data
                 (P + 1 .. Stream_Element_Offset'Min (P + Block_Size, L)),
               P,
               Uncompressed_Data
                 (Total_Out (Decompressor) + 1 .. Uncompressed_Data'Last),
               O,
               No_Flush);

               Ada.Text_IO.Put_Line
                 ("Total in : " & Count'Image (Total_In (Decompressor)) &
                  ", out : " & Count'Image (Total_Out (Decompressor)));

               exit when P = L;
         end loop;

         Ada.Text_IO.New_Line;
         Ada.Text_IO.Put_Line
           ("Decompressed text matches original text : "
             & Boolean'Image (Uncompressed_Data = Source));
      end Decompress;
   end;
end Buffer_Demo;

Deleted compat/zlib/contrib/ada/mtest.adb.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156




























































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
----------------------------------------------------------------
--  ZLib for Ada thick binding.                               --
--                                                            --
--  Copyright (C) 2002-2003 Dmitriy Anisimkov                 --
--                                                            --
--  Open source license information is in the zlib.ads file.  --
----------------------------------------------------------------
--  Continuous test for ZLib multithreading. If the test would fail
--  we should provide thread safe allocation routines for the Z_Stream.
--
--  $Id: mtest.adb,v 1.4 2004/07/23 07:49:54 vagul Exp $

with ZLib;
with Ada.Streams;
with Ada.Numerics.Discrete_Random;
with Ada.Text_IO;
with Ada.Exceptions;
with Ada.Task_Identification;

procedure MTest is
   use Ada.Streams;
   use ZLib;

   Stop : Boolean := False;

   pragma Atomic (Stop);

   subtype Visible_Symbols is Stream_Element range 16#20# .. 16#7E#;

   package Random_Elements is
      new Ada.Numerics.Discrete_Random (Visible_Symbols);

   task type Test_Task;

   task body Test_Task is
      Buffer : Stream_Element_Array (1 .. 100_000);
      Gen : Random_Elements.Generator;

      Buffer_First  : Stream_Element_Offset;
      Compare_First : Stream_Element_Offset;

      Deflate : Filter_Type;
      Inflate : Filter_Type;

      procedure Further (Item : in Stream_Element_Array);

      procedure Read_Buffer
        (Item : out Ada.Streams.Stream_Element_Array;
         Last : out Ada.Streams.Stream_Element_Offset);

      -------------
      -- Further --
      -------------

      procedure Further (Item : in Stream_Element_Array) is

         procedure Compare (Item : in Stream_Element_Array);

         -------------
         -- Compare --
         -------------

         procedure Compare (Item : in Stream_Element_Array) is
            Next_First : Stream_Element_Offset := Compare_First + Item'Length;
         begin
            if Buffer (Compare_First .. Next_First - 1) /= Item then
               raise Program_Error;
            end if;

            Compare_First := Next_First;
         end Compare;

         procedure Compare_Write is new ZLib.Write (Write => Compare);
      begin
         Compare_Write (Inflate, Item, No_Flush);
      end Further;

      -----------------
      -- Read_Buffer --
      -----------------

      procedure Read_Buffer
        (Item : out Ada.Streams.Stream_Element_Array;
         Last : out Ada.Streams.Stream_Element_Offset)
      is
         Buff_Diff   : Stream_Element_Offset := Buffer'Last - Buffer_First;
         Next_First : Stream_Element_Offset;
      begin
         if Item'Length <= Buff_Diff then
            Last := Item'Last;

            Next_First := Buffer_First + Item'Length;

            Item := Buffer (Buffer_First .. Next_First - 1);

            Buffer_First := Next_First;
         else
            Last := Item'First + Buff_Diff;
            Item (Item'First .. Last) := Buffer (Buffer_First .. Buffer'Last);
            Buffer_First := Buffer'Last + 1;
         end if;
      end Read_Buffer;

      procedure Translate is new Generic_Translate
                                   (Data_In  => Read_Buffer,
                                    Data_Out => Further);

   begin
      Random_Elements.Reset (Gen);

      Buffer := (others => 20);

      Main : loop
         for J in Buffer'Range loop
            Buffer (J) := Random_Elements.Random (Gen);

            Deflate_Init (Deflate);
            Inflate_Init (Inflate);

            Buffer_First  := Buffer'First;
            Compare_First := Buffer'First;

            Translate (Deflate);

            if Compare_First /= Buffer'Last + 1 then
               raise Program_Error;
            end if;

            Ada.Text_IO.Put_Line
              (Ada.Task_Identification.Image
                 (Ada.Task_Identification.Current_Task)
               & Stream_Element_Offset'Image (J)
               & ZLib.Count'Image (Total_Out (Deflate)));

            Close (Deflate);
            Close (Inflate);

            exit Main when Stop;
         end loop;
      end loop Main;
   exception
      when E : others =>
         Ada.Text_IO.Put_Line (Ada.Exceptions.Exception_Information (E));
         Stop := True;
   end Test_Task;

   Test : array (1 .. 4) of Test_Task;

   pragma Unreferenced (Test);

   Dummy : Character;

begin
   Ada.Text_IO.Get_Immediate (Dummy);
   Stop := True;
end MTest;

Deleted compat/zlib/contrib/ada/read.adb.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156




























































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
----------------------------------------------------------------
--  ZLib for Ada thick binding.                               --
--                                                            --
--  Copyright (C) 2002-2003 Dmitriy Anisimkov                 --
--                                                            --
--  Open source license information is in the zlib.ads file.  --
----------------------------------------------------------------

--  $Id: read.adb,v 1.8 2004/05/31 10:53:40 vagul Exp $

--  Test/demo program for the generic read interface.

with Ada.Numerics.Discrete_Random;
with Ada.Streams;
with Ada.Text_IO;

with ZLib;

procedure Read is

   use Ada.Streams;

   ------------------------------------
   --  Test configuration parameters --
   ------------------------------------

   File_Size   : Stream_Element_Offset := 100_000;

   Continuous  : constant Boolean          := False;
   --  If this constant is True, the test would be repeated again and again,
   --  with increment File_Size for every iteration.

   Header      : constant ZLib.Header_Type := ZLib.Default;
   --  Do not use Header other than Default in ZLib versions 1.1.4 and older.

   Init_Random : constant := 8;
   --  We are using the same random sequence, in case of we catch bug,
   --  so we would be able to reproduce it.

   -- End --

   Pack_Size : Stream_Element_Offset;
   Offset    : Stream_Element_Offset;

   Filter     : ZLib.Filter_Type;

   subtype Visible_Symbols
      is Stream_Element range 16#20# .. 16#7E#;

   package Random_Elements is new
      Ada.Numerics.Discrete_Random (Visible_Symbols);

   Gen : Random_Elements.Generator;
   Period  : constant Stream_Element_Offset := 200;
   --  Period constant variable for random generator not to be very random.
   --  Bigger period, harder random.

   Read_Buffer : Stream_Element_Array (1 .. 2048);
   Read_First  : Stream_Element_Offset;
   Read_Last   : Stream_Element_Offset;

   procedure Reset;

   procedure Read
     (Item : out Stream_Element_Array;
      Last : out Stream_Element_Offset);
   --  this procedure is for generic instantiation of
   --  ZLib.Read
   --  reading data from the File_In.

   procedure Read is new ZLib.Read
                           (Read,
                            Read_Buffer,
                            Rest_First => Read_First,
                            Rest_Last  => Read_Last);

   ----------
   -- Read --
   ----------

   procedure Read
     (Item : out Stream_Element_Array;
      Last : out Stream_Element_Offset) is
   begin
      Last := Stream_Element_Offset'Min
               (Item'Last,
                Item'First + File_Size - Offset);

      for J in Item'First .. Last loop
         if J < Item'First + Period then
            Item (J) := Random_Elements.Random (Gen);
         else
            Item (J) := Item (J - Period);
         end if;

         Offset   := Offset + 1;
      end loop;
   end Read;

   -----------
   -- Reset --
   -----------

   procedure Reset is
   begin
      Random_Elements.Reset (Gen, Init_Random);
      Pack_Size := 0;
      Offset := 1;
      Read_First := Read_Buffer'Last + 1;
      Read_Last  := Read_Buffer'Last;
   end Reset;

begin
   Ada.Text_IO.Put_Line ("ZLib " & ZLib.Version);

   loop
      for Level in ZLib.Compression_Level'Range loop

         Ada.Text_IO.Put ("Level ="
            & ZLib.Compression_Level'Image (Level));

         --  Deflate using generic instantiation.

         ZLib.Deflate_Init
               (Filter,
                Level,
                Header => Header);

         Reset;

         Ada.Text_IO.Put
           (Stream_Element_Offset'Image (File_Size) & " ->");

         loop
            declare
               Buffer : Stream_Element_Array (1 .. 1024);
               Last   : Stream_Element_Offset;
            begin
               Read (Filter, Buffer, Last);

               Pack_Size := Pack_Size + Last - Buffer'First + 1;

               exit when Last < Buffer'Last;
            end;
         end loop;

         Ada.Text_IO.Put_Line (Stream_Element_Offset'Image (Pack_Size));

         ZLib.Close (Filter);
      end loop;

      exit when not Continuous;

      File_Size := File_Size + 1;
   end loop;
end Read;

Deleted compat/zlib/contrib/ada/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

































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
                        ZLib for Ada thick binding (ZLib.Ada)
                        Release 1.3

ZLib.Ada is a thick binding interface to the popular ZLib data
compression library, available at http://www.gzip.org/zlib/.
It provides Ada-style access to the ZLib C library.


        Here are the main changes since ZLib.Ada 1.2:

- Attension: ZLib.Read generic routine have a initialization requirement
  for Read_Last parameter now. It is a bit incompartible with previous version,
  but extends functionality, we could use new parameters Allow_Read_Some and
  Flush now.

- Added Is_Open routines to ZLib and ZLib.Streams packages.

- Add pragma Assert to check Stream_Element is 8 bit.

- Fix extraction to buffer with exact known decompressed size. Error reported by
  Steve Sangwine.

- Fix definition of ULong (changed to unsigned_long), fix regression on 64 bits
  computers. Patch provided by Pascal Obry.

- Add Status_Error exception definition.

- Add pragma Assertion that Ada.Streams.Stream_Element size is 8 bit.


        How to build ZLib.Ada under GNAT

You should have the ZLib library already build on your computer, before
building ZLib.Ada. Make the directory of ZLib.Ada sources current and
issue the command:

  gnatmake test -largs -L<directory where libz.a is> -lz

Or use the GNAT project file build for GNAT 3.15 or later:

  gnatmake -Pzlib.gpr -L<directory where libz.a is>


        How to build ZLib.Ada under Aonix ObjectAda for Win32 7.2.2

1. Make a project with all *.ads and *.adb files from the distribution.
2. Build the libz.a library from the ZLib C sources.
3. Rename libz.a to z.lib.
4. Add the library z.lib to the project.
5. Add the libc.lib library from the ObjectAda distribution to the project.
6. Build the executable using test.adb as a main procedure.


        How to use ZLib.Ada

The source files test.adb and read.adb are small demo programs that show
the main functionality of ZLib.Ada.

The routines from the package specifications are commented.


Homepage: http://zlib-ada.sourceforge.net/
Author: Dmitriy Anisimkov <anisimkov@yahoo.com>

Contributors: Pascal Obry <pascal@obry.org>, Steve Sangwine <sjs@essex.ac.uk>

Deleted compat/zlib/contrib/ada/test.adb.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
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















































































































































































































































































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
----------------------------------------------------------------
--  ZLib for Ada thick binding.                               --
--                                                            --
--  Copyright (C) 2002-2003 Dmitriy Anisimkov                 --
--                                                            --
--  Open source license information is in the zlib.ads file.  --
----------------------------------------------------------------

--  $Id: test.adb,v 1.17 2003/08/12 12:13:30 vagul Exp $

--  The program has a few aims.
--  1. Test ZLib.Ada95 thick binding functionality.
--  2. Show the example of use main functionality of the ZLib.Ada95 binding.
--  3. Build this program automatically compile all ZLib.Ada95 packages under
--     GNAT Ada95 compiler.

with ZLib.Streams;
with Ada.Streams.Stream_IO;
with Ada.Numerics.Discrete_Random;

with Ada.Text_IO;

with Ada.Calendar;

procedure Test is

   use Ada.Streams;
   use Stream_IO;

   ------------------------------------
   --  Test configuration parameters --
   ------------------------------------

   File_Size   : Count   := 100_000;
   Continuous  : constant Boolean := False;

   Header      : constant ZLib.Header_Type := ZLib.Default;
                                              --  ZLib.None;
                                              --  ZLib.Auto;
                                              --  ZLib.GZip;
   --  Do not use Header other then Default in ZLib versions 1.1.4
   --  and older.

   Strategy    : constant ZLib.Strategy_Type := ZLib.Default_Strategy;
   Init_Random : constant := 10;

   -- End --

   In_File_Name  : constant String := "testzlib.in";
   --  Name of the input file

   Z_File_Name   : constant String := "testzlib.zlb";
   --  Name of the compressed file.

   Out_File_Name : constant String := "testzlib.out";
   --  Name of the decompressed file.

   File_In   : File_Type;
   File_Out  : File_Type;
   File_Back : File_Type;
   File_Z    : ZLib.Streams.Stream_Type;

   Filter : ZLib.Filter_Type;

   Time_Stamp : Ada.Calendar.Time;

   procedure Generate_File;
   --  Generate file of spetsified size with some random data.
   --  The random data is repeatable, for the good compression.

   procedure Compare_Streams
     (Left, Right : in out Root_Stream_Type'Class);
   --  The procedure compearing data in 2 streams.
   --  It is for compare data before and after compression/decompression.

   procedure Compare_Files (Left, Right : String);
   --  Compare files. Based on the Compare_Streams.

   procedure Copy_Streams
     (Source, Target : in out Root_Stream_Type'Class;
      Buffer_Size    : in     Stream_Element_Offset := 1024);
   --  Copying data from one stream to another. It is for test stream
   --  interface of the library.

   procedure Data_In
     (Item : out Stream_Element_Array;
      Last : out Stream_Element_Offset);
   --  this procedure is for generic instantiation of
   --  ZLib.Generic_Translate.
   --  reading data from the File_In.

   procedure Data_Out (Item : in Stream_Element_Array);
   --  this procedure is for generic instantiation of
   --  ZLib.Generic_Translate.
   --  writing data to the File_Out.

   procedure Stamp;
   --  Store the timestamp to the local variable.

   procedure Print_Statistic (Msg : String; Data_Size : ZLib.Count);
   --  Print the time statistic with the message.

   procedure Translate is new ZLib.Generic_Translate
                                (Data_In  => Data_In,
                                 Data_Out => Data_Out);
   --  This procedure is moving data from File_In to File_Out
   --  with compression or decompression, depend on initialization of
   --  Filter parameter.

   -------------------
   -- Compare_Files --
   -------------------

   procedure Compare_Files (Left, Right : String) is
      Left_File, Right_File : File_Type;
   begin
      Open (Left_File, In_File, Left);
      Open (Right_File, In_File, Right);
      Compare_Streams (Stream (Left_File).all, Stream (Right_File).all);
      Close (Left_File);
      Close (Right_File);
   end Compare_Files;

   ---------------------
   -- Compare_Streams --
   ---------------------

   procedure Compare_Streams
     (Left, Right : in out Ada.Streams.Root_Stream_Type'Class)
   is
      Left_Buffer, Right_Buffer : Stream_Element_Array (0 .. 16#FFF#);
      Left_Last, Right_Last : Stream_Element_Offset;
   begin
      loop
         Read (Left, Left_Buffer, Left_Last);
         Read (Right, Right_Buffer, Right_Last);

         if Left_Last /= Right_Last then
            Ada.Text_IO.Put_Line ("Compare error :"
              & Stream_Element_Offset'Image (Left_Last)
              & " /= "
              & Stream_Element_Offset'Image (Right_Last));

            raise Constraint_Error;

         elsif Left_Buffer (0 .. Left_Last)
               /= Right_Buffer (0 .. Right_Last)
         then
            Ada.Text_IO.Put_Line ("ERROR: IN and OUT files is not equal.");
            raise Constraint_Error;

         end if;

         exit when Left_Last < Left_Buffer'Last;
      end loop;
   end Compare_Streams;

   ------------------
   -- Copy_Streams --
   ------------------

   procedure Copy_Streams
     (Source, Target : in out Ada.Streams.Root_Stream_Type'Class;
      Buffer_Size    : in     Stream_Element_Offset := 1024)
   is
      Buffer : Stream_Element_Array (1 .. Buffer_Size);
      Last   : Stream_Element_Offset;
   begin
      loop
         Read  (Source, Buffer, Last);
         Write (Target, Buffer (1 .. Last));

         exit when Last < Buffer'Last;
      end loop;
   end Copy_Streams;

   -------------
   -- Data_In --
   -------------

   procedure Data_In
     (Item : out Stream_Element_Array;
      Last : out Stream_Element_Offset) is
   begin
      Read (File_In, Item, Last);
   end Data_In;

   --------------
   -- Data_Out --
   --------------

   procedure Data_Out (Item : in Stream_Element_Array) is
   begin
      Write (File_Out, Item);
   end Data_Out;

   -------------------
   -- Generate_File --
   -------------------

   procedure Generate_File is
      subtype Visible_Symbols is Stream_Element range 16#20# .. 16#7E#;

      package Random_Elements is
         new Ada.Numerics.Discrete_Random (Visible_Symbols);

      Gen    : Random_Elements.Generator;
      Buffer : Stream_Element_Array := (1 .. 77 => 16#20#) & 10;

      Buffer_Count : constant Count := File_Size / Buffer'Length;
      --  Number of same buffers in the packet.

      Density : constant Count := 30; --  from 0 to Buffer'Length - 2;

      procedure Fill_Buffer (J, D : in Count);
      --  Change the part of the buffer.

      -----------------
      -- Fill_Buffer --
      -----------------

      procedure Fill_Buffer (J, D : in Count) is
      begin
         for K in 0 .. D loop
            Buffer
              (Stream_Element_Offset ((J + K) mod (Buffer'Length - 1) + 1))
             := Random_Elements.Random (Gen);

         end loop;
      end Fill_Buffer;

   begin
      Random_Elements.Reset (Gen, Init_Random);

      Create (File_In, Out_File, In_File_Name);

      Fill_Buffer (1, Buffer'Length - 2);

      for J in 1 .. Buffer_Count loop
         Write (File_In, Buffer);

         Fill_Buffer (J, Density);
      end loop;

      --  fill remain size.

      Write
        (File_In,
         Buffer
           (1 .. Stream_Element_Offset
                   (File_Size - Buffer'Length * Buffer_Count)));

      Flush (File_In);
      Close (File_In);
   end Generate_File;

   ---------------------
   -- Print_Statistic --
   ---------------------

   procedure Print_Statistic (Msg : String; Data_Size : ZLib.Count) is
      use Ada.Calendar;
      use Ada.Text_IO;

      package Count_IO is new Integer_IO (ZLib.Count);

      Curr_Dur : Duration := Clock - Time_Stamp;
   begin
      Put (Msg);

      Set_Col (20);
      Ada.Text_IO.Put ("size =");

      Count_IO.Put
        (Data_Size,
         Width => Stream_IO.Count'Image (File_Size)'Length);

      Put_Line (" duration =" & Duration'Image (Curr_Dur));
   end Print_Statistic;

   -----------
   -- Stamp --
   -----------

   procedure Stamp is
   begin
      Time_Stamp := Ada.Calendar.Clock;
   end Stamp;

begin
   Ada.Text_IO.Put_Line ("ZLib " & ZLib.Version);

   loop
      Generate_File;

      for Level in ZLib.Compression_Level'Range loop

         Ada.Text_IO.Put_Line ("Level ="
            & ZLib.Compression_Level'Image (Level));

         --  Test generic interface.
         Open   (File_In, In_File, In_File_Name);
         Create (File_Out, Out_File, Z_File_Name);

         Stamp;

         --  Deflate using generic instantiation.

         ZLib.Deflate_Init
               (Filter   => Filter,
                Level    => Level,
                Strategy => Strategy,
                Header   => Header);

         Translate (Filter);
         Print_Statistic ("Generic compress", ZLib.Total_Out (Filter));
         ZLib.Close (Filter);

         Close (File_In);
         Close (File_Out);

         Open   (File_In, In_File, Z_File_Name);
         Create (File_Out, Out_File, Out_File_Name);

         Stamp;

         --  Inflate using generic instantiation.

         ZLib.Inflate_Init (Filter, Header => Header);

         Translate (Filter);
         Print_Statistic ("Generic decompress", ZLib.Total_Out (Filter));

         ZLib.Close (Filter);

         Close (File_In);
         Close (File_Out);

         Compare_Files (In_File_Name, Out_File_Name);

         --  Test stream interface.

         --  Compress to the back stream.

         Open   (File_In, In_File, In_File_Name);
         Create (File_Back, Out_File, Z_File_Name);

         Stamp;

         ZLib.Streams.Create
           (Stream          => File_Z,
            Mode            => ZLib.Streams.Out_Stream,
            Back            => ZLib.Streams.Stream_Access
                                 (Stream (File_Back)),
            Back_Compressed => True,
            Level           => Level,
            Strategy        => Strategy,
            Header          => Header);

         Copy_Streams
           (Source => Stream (File_In).all,
            Target => File_Z);

         --  Flushing internal buffers to the back stream.

         ZLib.Streams.Flush (File_Z, ZLib.Finish);

         Print_Statistic ("Write compress",
                          ZLib.Streams.Write_Total_Out (File_Z));

         ZLib.Streams.Close (File_Z);

         Close (File_In);
         Close (File_Back);

         --  Compare reading from original file and from
         --  decompression stream.

         Open (File_In,   In_File, In_File_Name);
         Open (File_Back, In_File, Z_File_Name);

         ZLib.Streams.Create
           (Stream          => File_Z,
            Mode            => ZLib.Streams.In_Stream,
            Back            => ZLib.Streams.Stream_Access
                                 (Stream (File_Back)),
            Back_Compressed => True,
            Header          => Header);

         Stamp;
         Compare_Streams (Stream (File_In).all, File_Z);

         Print_Statistic ("Read decompress",
                          ZLib.Streams.Read_Total_Out (File_Z));

         ZLib.Streams.Close (File_Z);
         Close (File_In);
         Close (File_Back);

         --  Compress by reading from compression stream.

         Open (File_Back, In_File, In_File_Name);
         Create (File_Out, Out_File, Z_File_Name);

         ZLib.Streams.Create
           (Stream          => File_Z,
            Mode            => ZLib.Streams.In_Stream,
            Back            => ZLib.Streams.Stream_Access
                                 (Stream (File_Back)),
            Back_Compressed => False,
            Level           => Level,
            Strategy        => Strategy,
            Header          => Header);

         Stamp;
         Copy_Streams
           (Source => File_Z,
            Target => Stream (File_Out).all);

         Print_Statistic ("Read compress",
                          ZLib.Streams.Read_Total_Out (File_Z));

         ZLib.Streams.Close (File_Z);

         Close (File_Out);
         Close (File_Back);

         --  Decompress to decompression stream.

         Open   (File_In,   In_File, Z_File_Name);
         Create (File_Back, Out_File, Out_File_Name);

         ZLib.Streams.Create
           (Stream          => File_Z,
            Mode            => ZLib.Streams.Out_Stream,
            Back            => ZLib.Streams.Stream_Access
                                 (Stream (File_Back)),
            Back_Compressed => False,
            Header          => Header);

         Stamp;

         Copy_Streams
           (Source => Stream (File_In).all,
            Target => File_Z);

         Print_Statistic ("Write decompress",
                          ZLib.Streams.Write_Total_Out (File_Z));

         ZLib.Streams.Close (File_Z);
         Close (File_In);
         Close (File_Back);

         Compare_Files (In_File_Name, Out_File_Name);
      end loop;

      Ada.Text_IO.Put_Line (Count'Image (File_Size) & " Ok.");

      exit when not Continuous;

      File_Size := File_Size + 1;
   end loop;
end Test;

Deleted compat/zlib/contrib/ada/zlib-streams.adb.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225

































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
----------------------------------------------------------------
--  ZLib for Ada thick binding.                               --
--                                                            --
--  Copyright (C) 2002-2003 Dmitriy Anisimkov                 --
--                                                            --
--  Open source license information is in the zlib.ads file.  --
----------------------------------------------------------------

--  $Id: zlib-streams.adb,v 1.10 2004/05/31 10:53:40 vagul Exp $

with Ada.Unchecked_Deallocation;

package body ZLib.Streams is

   -----------
   -- Close --
   -----------

   procedure Close (Stream : in out Stream_Type) is
      procedure Free is new Ada.Unchecked_Deallocation
         (Stream_Element_Array, Buffer_Access);
   begin
      if Stream.Mode = Out_Stream or Stream.Mode = Duplex then
         --  We should flush the data written by the writer.

         Flush (Stream, Finish);

         Close (Stream.Writer);
      end if;

      if Stream.Mode = In_Stream or Stream.Mode = Duplex then
         Close (Stream.Reader);
         Free (Stream.Buffer);
      end if;
   end Close;

   ------------
   -- Create --
   ------------

   procedure Create
     (Stream            :    out Stream_Type;
      Mode              : in     Stream_Mode;
      Back              : in     Stream_Access;
      Back_Compressed   : in     Boolean;
      Level             : in     Compression_Level := Default_Compression;
      Strategy          : in     Strategy_Type     := Default_Strategy;
      Header            : in     Header_Type       := Default;
      Read_Buffer_Size  : in     Ada.Streams.Stream_Element_Offset
                                    := Default_Buffer_Size;
      Write_Buffer_Size : in     Ada.Streams.Stream_Element_Offset
                                    := Default_Buffer_Size)
   is

      subtype Buffer_Subtype is Stream_Element_Array (1 .. Read_Buffer_Size);

      procedure Init_Filter
         (Filter   : in out Filter_Type;
          Compress : in     Boolean);

      -----------------
      -- Init_Filter --
      -----------------

      procedure Init_Filter
         (Filter   : in out Filter_Type;
          Compress : in     Boolean) is
      begin
         if Compress then
            Deflate_Init
              (Filter, Level, Strategy, Header => Header);
         else
            Inflate_Init (Filter, Header => Header);
         end if;
      end Init_Filter;

   begin
      Stream.Back := Back;
      Stream.Mode := Mode;

      if Mode = Out_Stream or Mode = Duplex then
         Init_Filter (Stream.Writer, Back_Compressed);
         Stream.Buffer_Size := Write_Buffer_Size;
      else
         Stream.Buffer_Size := 0;
      end if;

      if Mode = In_Stream or Mode = Duplex then
         Init_Filter (Stream.Reader, not Back_Compressed);

         Stream.Buffer     := new Buffer_Subtype;
         Stream.Rest_First := Stream.Buffer'Last + 1;
         Stream.Rest_Last  := Stream.Buffer'Last;
      end if;
   end Create;

   -----------
   -- Flush --
   -----------

   procedure Flush
     (Stream : in out Stream_Type;
      Mode   : in     Flush_Mode := Sync_Flush)
   is
      Buffer : Stream_Element_Array (1 .. Stream.Buffer_Size);
      Last   : Stream_Element_Offset;
   begin
      loop
         Flush (Stream.Writer, Buffer, Last, Mode);

         Ada.Streams.Write (Stream.Back.all, Buffer (1 .. Last));

         exit when Last < Buffer'Last;
      end loop;
   end Flush;

   -------------
   -- Is_Open --
   -------------

   function Is_Open (Stream : Stream_Type) return Boolean is
   begin
      return Is_Open (Stream.Reader) or else Is_Open (Stream.Writer);
   end Is_Open;

   ----------
   -- Read --
   ----------

   procedure Read
     (Stream : in out Stream_Type;
      Item   :    out Stream_Element_Array;
      Last   :    out Stream_Element_Offset)
   is

      procedure Read
        (Item : out Stream_Element_Array;
         Last : out Stream_Element_Offset);

      ----------
      -- Read --
      ----------

      procedure Read
        (Item : out Stream_Element_Array;
         Last : out Stream_Element_Offset) is
      begin
         Ada.Streams.Read (Stream.Back.all, Item, Last);
      end Read;

      procedure Read is new ZLib.Read
         (Read       => Read,
          Buffer     => Stream.Buffer.all,
          Rest_First => Stream.Rest_First,
          Rest_Last  => Stream.Rest_Last);

   begin
      Read (Stream.Reader, Item, Last);
   end Read;

   -------------------
   -- Read_Total_In --
   -------------------

   function Read_Total_In (Stream : in Stream_Type) return Count is
   begin
      return Total_In (Stream.Reader);
   end Read_Total_In;

   --------------------
   -- Read_Total_Out --
   --------------------

   function Read_Total_Out (Stream : in Stream_Type) return Count is
   begin
      return Total_Out (Stream.Reader);
   end Read_Total_Out;

   -----------
   -- Write --
   -----------

   procedure Write
     (Stream : in out Stream_Type;
      Item   : in     Stream_Element_Array)
   is

      procedure Write (Item : in Stream_Element_Array);

      -----------
      -- Write --
      -----------

      procedure Write (Item : in Stream_Element_Array) is
      begin
         Ada.Streams.Write (Stream.Back.all, Item);
      end Write;

      procedure Write is new ZLib.Write
         (Write       => Write,
          Buffer_Size => Stream.Buffer_Size);

   begin
      Write (Stream.Writer, Item, No_Flush);
   end Write;

   --------------------
   -- Write_Total_In --
   --------------------

   function Write_Total_In (Stream : in Stream_Type) return Count is
   begin
      return Total_In (Stream.Writer);
   end Write_Total_In;

   ---------------------
   -- Write_Total_Out --
   ---------------------

   function Write_Total_Out (Stream : in Stream_Type) return Count is
   begin
      return Total_Out (Stream.Writer);
   end Write_Total_Out;

end ZLib.Streams;

Deleted compat/zlib/contrib/ada/zlib-streams.ads.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114


















































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
----------------------------------------------------------------
--  ZLib for Ada thick binding.                               --
--                                                            --
--  Copyright (C) 2002-2003 Dmitriy Anisimkov                 --
--                                                            --
--  Open source license information is in the zlib.ads file.  --
----------------------------------------------------------------

--  $Id: zlib-streams.ads,v 1.12 2004/05/31 10:53:40 vagul Exp $

package ZLib.Streams is

   type Stream_Mode is (In_Stream, Out_Stream, Duplex);

   type Stream_Access is access all Ada.Streams.Root_Stream_Type'Class;

   type Stream_Type is
      new Ada.Streams.Root_Stream_Type with private;

   procedure Read
     (Stream : in out Stream_Type;
      Item   :    out Ada.Streams.Stream_Element_Array;
      Last   :    out Ada.Streams.Stream_Element_Offset);

   procedure Write
     (Stream : in out Stream_Type;
      Item   : in     Ada.Streams.Stream_Element_Array);

   procedure Flush
     (Stream : in out Stream_Type;
      Mode   : in     Flush_Mode := Sync_Flush);
   --  Flush the written data to the back stream,
   --  all data placed to the compressor is flushing to the Back stream.
   --  Should not be used until necessary, because it is decreasing
   --  compression.

   function Read_Total_In (Stream : in Stream_Type) return Count;
   pragma Inline (Read_Total_In);
   --  Return total number of bytes read from back stream so far.

   function Read_Total_Out (Stream : in Stream_Type) return Count;
   pragma Inline (Read_Total_Out);
   --  Return total number of bytes read so far.

   function Write_Total_In (Stream : in Stream_Type) return Count;
   pragma Inline (Write_Total_In);
   --  Return total number of bytes written so far.

   function Write_Total_Out (Stream : in Stream_Type) return Count;
   pragma Inline (Write_Total_Out);
   --  Return total number of bytes written to the back stream.

   procedure Create
     (Stream            :    out Stream_Type;
      Mode              : in     Stream_Mode;
      Back              : in     Stream_Access;
      Back_Compressed   : in     Boolean;
      Level             : in     Compression_Level := Default_Compression;
      Strategy          : in     Strategy_Type     := Default_Strategy;
      Header            : in     Header_Type       := Default;
      Read_Buffer_Size  : in     Ada.Streams.Stream_Element_Offset
                                    := Default_Buffer_Size;
      Write_Buffer_Size : in     Ada.Streams.Stream_Element_Offset
                                    := Default_Buffer_Size);
   --  Create the Comression/Decompression stream.
   --  If mode is In_Stream then Write operation is disabled.
   --  If mode is Out_Stream then Read operation is disabled.

   --  If Back_Compressed is true then
   --  Data written to the Stream is compressing to the Back stream
   --  and data read from the Stream is decompressed data from the Back stream.

   --  If Back_Compressed is false then
   --  Data written to the Stream is decompressing to the Back stream
   --  and data read from the Stream is compressed data from the Back stream.

   --  !!! When the Need_Header is False ZLib-Ada is using undocumented
   --  ZLib 1.1.4 functionality to do not create/wait for ZLib headers.

   function Is_Open (Stream : Stream_Type) return Boolean;

   procedure Close (Stream : in out Stream_Type);

private

   use Ada.Streams;

   type Buffer_Access is access all Stream_Element_Array;

   type Stream_Type
     is new Root_Stream_Type with
   record
      Mode       : Stream_Mode;

      Buffer     : Buffer_Access;
      Rest_First : Stream_Element_Offset;
      Rest_Last  : Stream_Element_Offset;
      --  Buffer for Read operation.
      --  We need to have this buffer in the record
      --  because not all read data from back stream
      --  could be processed during the read operation.

      Buffer_Size : Stream_Element_Offset;
      --  Buffer size for write operation.
      --  We do not need to have this buffer
      --  in the record because all data could be
      --  processed in the write operation.

      Back       : Stream_Access;
      Reader     : Filter_Type;
      Writer     : Filter_Type;
   end record;

end ZLib.Streams;

Deleted compat/zlib/contrib/ada/zlib-thin.adb.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141













































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
----------------------------------------------------------------
--  ZLib for Ada thick binding.                               --
--                                                            --
--  Copyright (C) 2002-2003 Dmitriy Anisimkov                 --
--                                                            --
--  Open source license information is in the zlib.ads file.  --
----------------------------------------------------------------

--  $Id: zlib-thin.adb,v 1.8 2003/12/14 18:27:31 vagul Exp $

package body ZLib.Thin is

   ZLIB_VERSION  : constant Chars_Ptr := zlibVersion;

   Z_Stream_Size : constant Int := Z_Stream'Size / System.Storage_Unit;

   --------------
   -- Avail_In --
   --------------

   function Avail_In (Strm : in Z_Stream) return UInt is
   begin
      return Strm.Avail_In;
   end Avail_In;

   ---------------
   -- Avail_Out --
   ---------------

   function Avail_Out (Strm : in Z_Stream) return UInt is
   begin
      return Strm.Avail_Out;
   end Avail_Out;

   ------------------
   -- Deflate_Init --
   ------------------

   function Deflate_Init
     (strm       : Z_Streamp;
      level      : Int;
      method     : Int;
      windowBits : Int;
      memLevel   : Int;
      strategy   : Int)
      return       Int is
   begin
      return deflateInit2
               (strm,
                level,
                method,
                windowBits,
                memLevel,
                strategy,
                ZLIB_VERSION,
                Z_Stream_Size);
   end Deflate_Init;

   ------------------
   -- Inflate_Init --
   ------------------

   function Inflate_Init (strm : Z_Streamp; windowBits : Int) return Int is
   begin
      return inflateInit2 (strm, windowBits, ZLIB_VERSION, Z_Stream_Size);
   end Inflate_Init;

   ------------------------
   -- Last_Error_Message --
   ------------------------

   function Last_Error_Message (Strm : in Z_Stream) return String is
      use Interfaces.C.Strings;
   begin
      if Strm.msg = Null_Ptr then
         return "";
      else
         return Value (Strm.msg);
      end if;
   end Last_Error_Message;

   ------------
   -- Set_In --
   ------------

   procedure Set_In
     (Strm   : in out Z_Stream;
      Buffer : in     Voidp;
      Size   : in     UInt) is
   begin
      Strm.Next_In  := Buffer;
      Strm.Avail_In := Size;
   end Set_In;

   ------------------
   -- Set_Mem_Func --
   ------------------

   procedure Set_Mem_Func
     (Strm   : in out Z_Stream;
      Opaque : in     Voidp;
      Alloc  : in     alloc_func;
      Free   : in     free_func) is
   begin
      Strm.opaque := Opaque;
      Strm.zalloc := Alloc;
      Strm.zfree  := Free;
   end Set_Mem_Func;

   -------------
   -- Set_Out --
   -------------

   procedure Set_Out
     (Strm   : in out Z_Stream;
      Buffer : in     Voidp;
      Size   : in     UInt) is
   begin
      Strm.Next_Out  := Buffer;
      Strm.Avail_Out := Size;
   end Set_Out;

   --------------
   -- Total_In --
   --------------

   function Total_In (Strm : in Z_Stream) return ULong is
   begin
      return Strm.Total_In;
   end Total_In;

   ---------------
   -- Total_Out --
   ---------------

   function Total_Out (Strm : in Z_Stream) return ULong is
   begin
      return Strm.Total_Out;
   end Total_Out;

end ZLib.Thin;

Deleted compat/zlib/contrib/ada/zlib-thin.ads.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450


































































































































































































































































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
----------------------------------------------------------------
--  ZLib for Ada thick binding.                               --
--                                                            --
--  Copyright (C) 2002-2003 Dmitriy Anisimkov                 --
--                                                            --
--  Open source license information is in the zlib.ads file.  --
----------------------------------------------------------------

--  $Id: zlib-thin.ads,v 1.11 2004/07/23 06:33:11 vagul Exp $

with Interfaces.C.Strings;

with System;

private package ZLib.Thin is

   --  From zconf.h

   MAX_MEM_LEVEL : constant := 9;         --  zconf.h:105
                                          --  zconf.h:105
   MAX_WBITS : constant := 15;      --  zconf.h:115
                                    --  32K LZ77 window
                                    --  zconf.h:115
   SEEK_SET : constant := 8#0000#;  --  zconf.h:244
                                    --  Seek from beginning of file.
                                    --  zconf.h:244
   SEEK_CUR : constant := 1;        --  zconf.h:245
                                    --  Seek from current position.
                                    --  zconf.h:245
   SEEK_END : constant := 2;        --  zconf.h:246
                                    --  Set file pointer to EOF plus "offset"
                                    --  zconf.h:246

   type Byte is new Interfaces.C.unsigned_char; --  8 bits
                                                --  zconf.h:214
   type UInt is new Interfaces.C.unsigned;      --  16 bits or more
                                                --  zconf.h:216
   type Int is new Interfaces.C.int;

   type ULong is new Interfaces.C.unsigned_long;     --  32 bits or more
                                                     --  zconf.h:217
   subtype Chars_Ptr is Interfaces.C.Strings.chars_ptr;

   type ULong_Access is access ULong;
   type Int_Access is access Int;

   subtype Voidp is System.Address;            --  zconf.h:232

   subtype Byte_Access is Voidp;

   Nul : constant Voidp := System.Null_Address;
   --  end from zconf

   Z_NO_FLUSH : constant := 8#0000#;   --  zlib.h:125
                                       --  zlib.h:125
   Z_PARTIAL_FLUSH : constant := 1;       --  zlib.h:126
                                          --  will be removed, use
                                          --  Z_SYNC_FLUSH instead
                                          --  zlib.h:126
   Z_SYNC_FLUSH : constant := 2;       --  zlib.h:127
                                       --  zlib.h:127
   Z_FULL_FLUSH : constant := 3;       --  zlib.h:128
                                       --  zlib.h:128
   Z_FINISH : constant := 4;        --  zlib.h:129
                                    --  zlib.h:129
   Z_OK : constant := 8#0000#;   --  zlib.h:132
                                 --  zlib.h:132
   Z_STREAM_END : constant := 1;       --  zlib.h:133
                                       --  zlib.h:133
   Z_NEED_DICT : constant := 2;        --  zlib.h:134
                                       --  zlib.h:134
   Z_ERRNO : constant := -1;        --  zlib.h:135
                                    --  zlib.h:135
   Z_STREAM_ERROR : constant := -2;       --  zlib.h:136
                                          --  zlib.h:136
   Z_DATA_ERROR : constant := -3;      --  zlib.h:137
                                       --  zlib.h:137
   Z_MEM_ERROR : constant := -4;       --  zlib.h:138
                                       --  zlib.h:138
   Z_BUF_ERROR : constant := -5;       --  zlib.h:139
                                       --  zlib.h:139
   Z_VERSION_ERROR : constant := -6;      --  zlib.h:140
                                          --  zlib.h:140
   Z_NO_COMPRESSION : constant := 8#0000#;   --  zlib.h:145
                                             --  zlib.h:145
   Z_BEST_SPEED : constant := 1;       --  zlib.h:146
                                       --  zlib.h:146
   Z_BEST_COMPRESSION : constant := 9;       --  zlib.h:147
                                             --  zlib.h:147
   Z_DEFAULT_COMPRESSION : constant := -1;      --  zlib.h:148
                                                --  zlib.h:148
   Z_FILTERED : constant := 1;      --  zlib.h:151
                                    --  zlib.h:151
   Z_HUFFMAN_ONLY : constant := 2;        --  zlib.h:152
                                          --  zlib.h:152
   Z_DEFAULT_STRATEGY : constant := 8#0000#; --  zlib.h:153
                                             --  zlib.h:153
   Z_BINARY : constant := 8#0000#;  --  zlib.h:156
                                    --  zlib.h:156
   Z_ASCII : constant := 1;      --  zlib.h:157
                                 --  zlib.h:157
   Z_UNKNOWN : constant := 2;       --  zlib.h:158
                                    --  zlib.h:158
   Z_DEFLATED : constant := 8;      --  zlib.h:161
                                    --  zlib.h:161
   Z_NULL : constant := 8#0000#; --  zlib.h:164
                                 --  for initializing zalloc, zfree, opaque
                                 --  zlib.h:164
   type gzFile is new Voidp;                  --  zlib.h:646

   type Z_Stream is private;

   type Z_Streamp is access all Z_Stream;     --  zlib.h:89

   type alloc_func is access function
     (Opaque : Voidp;
      Items  : UInt;
      Size   : UInt)
      return Voidp; --  zlib.h:63

   type free_func is access procedure (opaque : Voidp; address : Voidp);

   function zlibVersion return Chars_Ptr;

   function Deflate (strm : Z_Streamp; flush : Int) return Int;

   function DeflateEnd (strm : Z_Streamp) return Int;

   function Inflate (strm : Z_Streamp; flush : Int) return Int;

   function InflateEnd (strm : Z_Streamp) return Int;

   function deflateSetDictionary
     (strm       : Z_Streamp;
      dictionary : Byte_Access;
      dictLength : UInt)
      return       Int;

   function deflateCopy (dest : Z_Streamp; source : Z_Streamp) return Int;
   --  zlib.h:478

   function deflateReset (strm : Z_Streamp) return Int; -- zlib.h:495

   function deflateParams
     (strm     : Z_Streamp;
      level    : Int;
      strategy : Int)
      return     Int;       -- zlib.h:506

   function inflateSetDictionary
     (strm       : Z_Streamp;
      dictionary : Byte_Access;
      dictLength : UInt)
      return       Int; --  zlib.h:548

   function inflateSync (strm : Z_Streamp) return Int;  --  zlib.h:565

   function inflateReset (strm : Z_Streamp) return Int; --  zlib.h:580

   function compress
     (dest      : Byte_Access;
      destLen   : ULong_Access;
      source    : Byte_Access;
      sourceLen : ULong)
      return      Int;           -- zlib.h:601

   function compress2
     (dest      : Byte_Access;
      destLen   : ULong_Access;
      source    : Byte_Access;
      sourceLen : ULong;
      level     : Int)
      return      Int;          -- zlib.h:615

   function uncompress
     (dest      : Byte_Access;
      destLen   : ULong_Access;
      source    : Byte_Access;
      sourceLen : ULong)
      return      Int;

   function gzopen (path : Chars_Ptr; mode : Chars_Ptr) return gzFile;

   function gzdopen (fd : Int; mode : Chars_Ptr) return gzFile;

   function gzsetparams
     (file     : gzFile;
      level    : Int;
      strategy : Int)
      return     Int;

   function gzread
     (file : gzFile;
      buf  : Voidp;
      len  : UInt)
      return Int;

   function gzwrite
     (file : in gzFile;
      buf  : in Voidp;
      len  : in UInt)
      return Int;

   function gzprintf (file : in gzFile; format : in Chars_Ptr) return Int;

   function gzputs (file : in gzFile; s : in Chars_Ptr) return Int;

   function gzgets
     (file : gzFile;
      buf  : Chars_Ptr;
      len  : Int)
      return Chars_Ptr;

   function gzputc (file : gzFile; char : Int) return Int;

   function gzgetc (file : gzFile) return Int;

   function gzflush (file : gzFile; flush : Int) return Int;

   function gzseek
     (file   : gzFile;
      offset : Int;
      whence : Int)
      return   Int;

   function gzrewind (file : gzFile) return Int;

   function gztell (file : gzFile) return Int;

   function gzeof (file : gzFile) return Int;

   function gzclose (file : gzFile) return Int;

   function gzerror (file : gzFile; errnum : Int_Access) return Chars_Ptr;

   function adler32
     (adler : ULong;
      buf   : Byte_Access;
      len   : UInt)
      return  ULong;

   function crc32
     (crc  : ULong;
      buf  : Byte_Access;
      len  : UInt)
      return ULong;

   function deflateInit
     (strm        : Z_Streamp;
      level       : Int;
      version     : Chars_Ptr;
      stream_size : Int)
      return        Int;

   function deflateInit2
     (strm        : Z_Streamp;
      level       : Int;
      method      : Int;
      windowBits  : Int;
      memLevel    : Int;
      strategy    : Int;
      version     : Chars_Ptr;
      stream_size : Int)
      return        Int;

   function Deflate_Init
     (strm       : Z_Streamp;
      level      : Int;
      method     : Int;
      windowBits : Int;
      memLevel   : Int;
      strategy   : Int)
      return       Int;
   pragma Inline (Deflate_Init);

   function inflateInit
     (strm        : Z_Streamp;
      version     : Chars_Ptr;
      stream_size : Int)
      return        Int;

   function inflateInit2
     (strm        : in Z_Streamp;
      windowBits  : in Int;
      version     : in Chars_Ptr;
      stream_size : in Int)
      return      Int;

   function inflateBackInit
     (strm        : in Z_Streamp;
      windowBits  : in Int;
      window      : in Byte_Access;
      version     : in Chars_Ptr;
      stream_size : in Int)
      return      Int;
   --  Size of window have to be 2**windowBits.

   function Inflate_Init (strm : Z_Streamp; windowBits : Int) return Int;
   pragma Inline (Inflate_Init);

   function zError (err : Int) return Chars_Ptr;

   function inflateSyncPoint (z : Z_Streamp) return Int;

   function get_crc_table return ULong_Access;

   --  Interface to the available fields of the z_stream structure.
   --  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.

   procedure Set_In
     (Strm   : in out Z_Stream;
      Buffer : in Voidp;
      Size   : in UInt);
   pragma Inline (Set_In);

   procedure Set_Out
     (Strm   : in out Z_Stream;
      Buffer : in Voidp;
      Size   : in UInt);
   pragma Inline (Set_Out);

   procedure Set_Mem_Func
     (Strm   : in out Z_Stream;
      Opaque : in Voidp;
      Alloc  : in alloc_func;
      Free   : in free_func);
   pragma Inline (Set_Mem_Func);

   function Last_Error_Message (Strm : in Z_Stream) return String;
   pragma Inline (Last_Error_Message);

   function Avail_Out (Strm : in Z_Stream) return UInt;
   pragma Inline (Avail_Out);

   function Avail_In (Strm : in Z_Stream) return UInt;
   pragma Inline (Avail_In);

   function Total_In (Strm : in Z_Stream) return ULong;
   pragma Inline (Total_In);

   function Total_Out (Strm : in Z_Stream) return ULong;
   pragma Inline (Total_Out);

   function inflateCopy
     (dest   : in Z_Streamp;
      Source : in Z_Streamp)
      return Int;

   function compressBound (Source_Len : in ULong) return ULong;

   function deflateBound
     (Strm       : in Z_Streamp;
      Source_Len : in ULong)
      return     ULong;

   function gzungetc (C : in Int; File : in  gzFile) return Int;

   function zlibCompileFlags return ULong;

private

   type Z_Stream is record            -- zlib.h:68
      Next_In   : Voidp      := Nul;  -- next input byte
      Avail_In  : UInt       := 0;    -- number of bytes available at next_in
      Total_In  : ULong      := 0;    -- total nb of input bytes read so far
      Next_Out  : Voidp      := Nul;  -- next output byte should be put there
      Avail_Out : UInt       := 0;    -- remaining free space at next_out
      Total_Out : ULong      := 0;    -- total nb of bytes output so far
      msg       : Chars_Ptr;          -- last error message, NULL if no error
      state     : Voidp;              -- not visible by applications
      zalloc    : alloc_func := null; -- used to allocate the internal state
      zfree     : free_func  := null; -- used to free the internal state
      opaque    : Voidp;              -- private data object passed to
                                      --  zalloc and zfree
      data_type : Int;                -- best guess about the data type:
                                      --  ascii or binary
      adler     : ULong;              -- adler32 value of the uncompressed
                                      --  data
      reserved  : ULong;              -- reserved for future use
   end record;

   pragma Convention (C, Z_Stream);

   pragma Import (C, zlibVersion, "zlibVersion");
   pragma Import (C, Deflate, "deflate");
   pragma Import (C, DeflateEnd, "deflateEnd");
   pragma Import (C, Inflate, "inflate");
   pragma Import (C, InflateEnd, "inflateEnd");
   pragma Import (C, deflateSetDictionary, "deflateSetDictionary");
   pragma Import (C, deflateCopy, "deflateCopy");
   pragma Import (C, deflateReset, "deflateReset");
   pragma Import (C, deflateParams, "deflateParams");
   pragma Import (C, inflateSetDictionary, "inflateSetDictionary");
   pragma Import (C, inflateSync, "inflateSync");
   pragma Import (C, inflateReset, "inflateReset");
   pragma Import (C, compress, "compress");
   pragma Import (C, compress2, "compress2");
   pragma Import (C, uncompress, "uncompress");
   pragma Import (C, gzopen, "gzopen");
   pragma Import (C, gzdopen, "gzdopen");
   pragma Import (C, gzsetparams, "gzsetparams");
   pragma Import (C, gzread, "gzread");
   pragma Import (C, gzwrite, "gzwrite");
   pragma Import (C, gzprintf, "gzprintf");
   pragma Import (C, gzputs, "gzputs");
   pragma Import (C, gzgets, "gzgets");
   pragma Import (C, gzputc, "gzputc");
   pragma Import (C, gzgetc, "gzgetc");
   pragma Import (C, gzflush, "gzflush");
   pragma Import (C, gzseek, "gzseek");
   pragma Import (C, gzrewind, "gzrewind");
   pragma Import (C, gztell, "gztell");
   pragma Import (C, gzeof, "gzeof");
   pragma Import (C, gzclose, "gzclose");
   pragma Import (C, gzerror, "gzerror");
   pragma Import (C, adler32, "adler32");
   pragma Import (C, crc32, "crc32");
   pragma Import (C, deflateInit, "deflateInit_");
   pragma Import (C, inflateInit, "inflateInit_");
   pragma Import (C, deflateInit2, "deflateInit2_");
   pragma Import (C, inflateInit2, "inflateInit2_");
   pragma Import (C, zError, "zError");
   pragma Import (C, inflateSyncPoint, "inflateSyncPoint");
   pragma Import (C, get_crc_table, "get_crc_table");

   --  since zlib 1.2.0:

   pragma Import (C, inflateCopy, "inflateCopy");
   pragma Import (C, compressBound, "compressBound");
   pragma Import (C, deflateBound, "deflateBound");
   pragma Import (C, gzungetc, "gzungetc");
   pragma Import (C, zlibCompileFlags, "zlibCompileFlags");

   pragma Import (C, inflateBackInit, "inflateBackInit_");

   --  I stopped binding the inflateBack routines, because realize that
   --  it does not support zlib and gzip headers for now, and have no
   --  symmetric deflateBack routines.
   --  ZLib-Ada is symmetric regarding deflate/inflate data transformation
   --  and has a similar generic callback interface for the
   --  deflate/inflate transformation based on the regular Deflate/Inflate
   --  routines.

   --  pragma Import (C, inflateBack, "inflateBack");
   --  pragma Import (C, inflateBackEnd, "inflateBackEnd");

end ZLib.Thin;

Deleted compat/zlib/contrib/ada/zlib.adb.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
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





























































































































































































































































































































































































































































































































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
----------------------------------------------------------------
--  ZLib for Ada thick binding.                               --
--                                                            --
--  Copyright (C) 2002-2004 Dmitriy Anisimkov                 --
--                                                            --
--  Open source license information is in the zlib.ads file.  --
----------------------------------------------------------------

--  $Id: zlib.adb,v 1.31 2004/09/06 06:53:19 vagul Exp $

with Ada.Exceptions;
with Ada.Unchecked_Conversion;
with Ada.Unchecked_Deallocation;

with Interfaces.C.Strings;

with ZLib.Thin;

package body ZLib is

   use type Thin.Int;

   type Z_Stream is new Thin.Z_Stream;

   type Return_Code_Enum is
      (OK,
       STREAM_END,
       NEED_DICT,
       ERRNO,
       STREAM_ERROR,
       DATA_ERROR,
       MEM_ERROR,
       BUF_ERROR,
       VERSION_ERROR);

   type Flate_Step_Function is access
     function (Strm : in Thin.Z_Streamp; Flush : in Thin.Int) return Thin.Int;
   pragma Convention (C, Flate_Step_Function);

   type Flate_End_Function is access
      function (Ctrm : in Thin.Z_Streamp) return Thin.Int;
   pragma Convention (C, Flate_End_Function);

   type Flate_Type is record
      Step : Flate_Step_Function;
      Done : Flate_End_Function;
   end record;

   subtype Footer_Array is Stream_Element_Array (1 .. 8);

   Simple_GZip_Header : constant Stream_Element_Array (1 .. 10)
     := (16#1f#, 16#8b#,                 --  Magic header
         16#08#,                         --  Z_DEFLATED
         16#00#,                         --  Flags
         16#00#, 16#00#, 16#00#, 16#00#, --  Time
         16#00#,                         --  XFlags
         16#03#                          --  OS code
        );
   --  The simplest gzip header is not for informational, but just for
   --  gzip format compatibility.
   --  Note that some code below is using assumption
   --  Simple_GZip_Header'Last > Footer_Array'Last, so do not make
   --  Simple_GZip_Header'Last <= Footer_Array'Last.

   Return_Code : constant array (Thin.Int range <>) of Return_Code_Enum
     := (0 => OK,
         1 => STREAM_END,
         2 => NEED_DICT,
        -1 => ERRNO,
        -2 => STREAM_ERROR,
        -3 => DATA_ERROR,
        -4 => MEM_ERROR,
        -5 => BUF_ERROR,
        -6 => VERSION_ERROR);

   Flate : constant array (Boolean) of Flate_Type
     := (True  => (Step => Thin.Deflate'Access,
                   Done => Thin.DeflateEnd'Access),
         False => (Step => Thin.Inflate'Access,
                   Done => Thin.InflateEnd'Access));

   Flush_Finish : constant array (Boolean) of Flush_Mode
     := (True => Finish, False => No_Flush);

   procedure Raise_Error (Stream : in Z_Stream);
   pragma Inline (Raise_Error);

   procedure Raise_Error (Message : in String);
   pragma Inline (Raise_Error);

   procedure Check_Error (Stream : in Z_Stream; Code : in Thin.Int);

   procedure Free is new Ada.Unchecked_Deallocation
      (Z_Stream, Z_Stream_Access);

   function To_Thin_Access is new Ada.Unchecked_Conversion
     (Z_Stream_Access, Thin.Z_Streamp);

   procedure Translate_GZip
     (Filter    : in out Filter_Type;
      In_Data   : in     Ada.Streams.Stream_Element_Array;
      In_Last   :    out Ada.Streams.Stream_Element_Offset;
      Out_Data  :    out Ada.Streams.Stream_Element_Array;
      Out_Last  :    out Ada.Streams.Stream_Element_Offset;
      Flush     : in     Flush_Mode);
   --  Separate translate routine for make gzip header.

   procedure Translate_Auto
     (Filter    : in out Filter_Type;
      In_Data   : in     Ada.Streams.Stream_Element_Array;
      In_Last   :    out Ada.Streams.Stream_Element_Offset;
      Out_Data  :    out Ada.Streams.Stream_Element_Array;
      Out_Last  :    out Ada.Streams.Stream_Element_Offset;
      Flush     : in     Flush_Mode);
   --  translate routine without additional headers.

   -----------------
   -- Check_Error --
   -----------------

   procedure Check_Error (Stream : in Z_Stream; Code : in Thin.Int) is
      use type Thin.Int;
   begin
      if Code /= Thin.Z_OK then
         Raise_Error
            (Return_Code_Enum'Image (Return_Code (Code))
              & ": " & Last_Error_Message (Stream));
      end if;
   end Check_Error;

   -----------
   -- Close --
   -----------

   procedure Close
     (Filter       : in out Filter_Type;
      Ignore_Error : in     Boolean := False)
   is
      Code : Thin.Int;
   begin
      if not Ignore_Error and then not Is_Open (Filter) then
         raise Status_Error;
      end if;

      Code := Flate (Filter.Compression).Done (To_Thin_Access (Filter.Strm));

      if Ignore_Error or else Code = Thin.Z_OK then
         Free (Filter.Strm);
      else
         declare
            Error_Message : constant String
              := Last_Error_Message (Filter.Strm.all);
         begin
            Free (Filter.Strm);
            Ada.Exceptions.Raise_Exception
               (ZLib_Error'Identity,
                Return_Code_Enum'Image (Return_Code (Code))
                  & ": " & Error_Message);
         end;
      end if;
   end Close;

   -----------
   -- CRC32 --
   -----------

   function CRC32
     (CRC  : in Unsigned_32;
      Data : in Ada.Streams.Stream_Element_Array)
      return Unsigned_32
   is
      use Thin;
   begin
      return Unsigned_32 (crc32 (ULong (CRC),
                                 Data'Address,
                                 Data'Length));
   end CRC32;

   procedure CRC32
     (CRC  : in out Unsigned_32;
      Data : in     Ada.Streams.Stream_Element_Array) is
   begin
      CRC := CRC32 (CRC, Data);
   end CRC32;

   ------------------
   -- Deflate_Init --
   ------------------

   procedure Deflate_Init
     (Filter       : in out Filter_Type;
      Level        : in     Compression_Level  := Default_Compression;
      Strategy     : in     Strategy_Type      := Default_Strategy;
      Method       : in     Compression_Method := Deflated;
      Window_Bits  : in     Window_Bits_Type   := Default_Window_Bits;
      Memory_Level : in     Memory_Level_Type  := Default_Memory_Level;
      Header       : in     Header_Type        := Default)
   is
      use type Thin.Int;
      Win_Bits : Thin.Int := Thin.Int (Window_Bits);
   begin
      if Is_Open (Filter) then
         raise Status_Error;
      end if;

      --  We allow ZLib to make header only in case of default header type.
      --  Otherwise we would either do header by ourselfs, or do not do
      --  header at all.

      if Header = None or else Header = GZip then
         Win_Bits := -Win_Bits;
      end if;

      --  For the GZip CRC calculation and make headers.

      if Header = GZip then
         Filter.CRC    := 0;
         Filter.Offset := Simple_GZip_Header'First;
      else
         Filter.Offset := Simple_GZip_Header'Last + 1;
      end if;

      Filter.Strm        := new Z_Stream;
      Filter.Compression := True;
      Filter.Stream_End  := False;
      Filter.Header      := Header;

      if Thin.Deflate_Init
           (To_Thin_Access (Filter.Strm),
            Level      => Thin.Int (Level),
            method     => Thin.Int (Method),
            windowBits => Win_Bits,
            memLevel   => Thin.Int (Memory_Level),
            strategy   => Thin.Int (Strategy)) /= Thin.Z_OK
      then
         Raise_Error (Filter.Strm.all);
      end if;
   end Deflate_Init;

   -----------
   -- Flush --
   -----------

   procedure Flush
     (Filter    : in out Filter_Type;
      Out_Data  :    out Ada.Streams.Stream_Element_Array;
      Out_Last  :    out Ada.Streams.Stream_Element_Offset;
      Flush     : in     Flush_Mode)
   is
      No_Data : Stream_Element_Array := (1 .. 0 => 0);
      Last    : Stream_Element_Offset;
   begin
      Translate (Filter, No_Data, Last, Out_Data, Out_Last, Flush);
   end Flush;

   -----------------------
   -- Generic_Translate --
   -----------------------

   procedure Generic_Translate
     (Filter          : in out ZLib.Filter_Type;
      In_Buffer_Size  : in     Integer := Default_Buffer_Size;
      Out_Buffer_Size : in     Integer := Default_Buffer_Size)
   is
      In_Buffer  : Stream_Element_Array
                     (1 .. Stream_Element_Offset (In_Buffer_Size));
      Out_Buffer : Stream_Element_Array
                     (1 .. Stream_Element_Offset (Out_Buffer_Size));
      Last       : Stream_Element_Offset;
      In_Last    : Stream_Element_Offset;
      In_First   : Stream_Element_Offset;
      Out_Last   : Stream_Element_Offset;
   begin
      Main : loop
         Data_In (In_Buffer, Last);

         In_First := In_Buffer'First;

         loop
            Translate
              (Filter   => Filter,
               In_Data  => In_Buffer (In_First .. Last),
               In_Last  => In_Last,
               Out_Data => Out_Buffer,
               Out_Last => Out_Last,
               Flush    => Flush_Finish (Last < In_Buffer'First));

            if Out_Buffer'First <= Out_Last then
               Data_Out (Out_Buffer (Out_Buffer'First .. Out_Last));
            end if;

            exit Main when Stream_End (Filter);

            --  The end of in buffer.

            exit when In_Last = Last;

            In_First := In_Last + 1;
         end loop;
      end loop Main;

   end Generic_Translate;

   ------------------
   -- Inflate_Init --
   ------------------

   procedure Inflate_Init
     (Filter      : in out Filter_Type;
      Window_Bits : in     Window_Bits_Type := Default_Window_Bits;
      Header      : in     Header_Type      := Default)
   is
      use type Thin.Int;
      Win_Bits : Thin.Int := Thin.Int (Window_Bits);

      procedure Check_Version;
      --  Check the latest header types compatibility.

      procedure Check_Version is
      begin
         if Version <= "1.1.4" then
            Raise_Error
              ("Inflate header type " & Header_Type'Image (Header)
               & " incompatible with ZLib version " & Version);
         end if;
      end Check_Version;

   begin
      if Is_Open (Filter) then
         raise Status_Error;
      end if;

      case Header is
         when None =>
            Check_Version;

            --  Inflate data without headers determined
            --  by negative Win_Bits.

            Win_Bits := -Win_Bits;
         when GZip =>
            Check_Version;

            --  Inflate gzip data defined by flag 16.

            Win_Bits := Win_Bits + 16;
         when Auto =>
            Check_Version;

            --  Inflate with automatic detection
            --  of gzip or native header defined by flag 32.

            Win_Bits := Win_Bits + 32;
         when Default => null;
      end case;

      Filter.Strm        := new Z_Stream;
      Filter.Compression := False;
      Filter.Stream_End  := False;
      Filter.Header      := Header;

      if Thin.Inflate_Init
         (To_Thin_Access (Filter.Strm), Win_Bits) /= Thin.Z_OK
      then
         Raise_Error (Filter.Strm.all);
      end if;
   end Inflate_Init;

   -------------
   -- Is_Open --
   -------------

   function Is_Open (Filter : in Filter_Type) return Boolean is
   begin
      return Filter.Strm /= null;
   end Is_Open;

   -----------------
   -- Raise_Error --
   -----------------

   procedure Raise_Error (Message : in String) is
   begin
      Ada.Exceptions.Raise_Exception (ZLib_Error'Identity, Message);
   end Raise_Error;

   procedure Raise_Error (Stream : in Z_Stream) is
   begin
      Raise_Error (Last_Error_Message (Stream));
   end Raise_Error;

   ----------
   -- Read --
   ----------

   procedure Read
     (Filter : in out Filter_Type;
      Item   :    out Ada.Streams.Stream_Element_Array;
      Last   :    out Ada.Streams.Stream_Element_Offset;
      Flush  : in     Flush_Mode := No_Flush)
   is
      In_Last    : Stream_Element_Offset;
      Item_First : Ada.Streams.Stream_Element_Offset := Item'First;
      V_Flush    : Flush_Mode := Flush;

   begin
      pragma Assert (Rest_First in Buffer'First .. Buffer'Last + 1);
      pragma Assert (Rest_Last in Buffer'First - 1 .. Buffer'Last);

      loop
         if Rest_Last = Buffer'First - 1 then
            V_Flush := Finish;

         elsif Rest_First > Rest_Last then
            Read (Buffer, Rest_Last);
            Rest_First := Buffer'First;

            if Rest_Last < Buffer'First then
               V_Flush := Finish;
            end if;
         end if;

         Translate
           (Filter   => Filter,
            In_Data  => Buffer (Rest_First .. Rest_Last),
            In_Last  => In_Last,
            Out_Data => Item (Item_First .. Item'Last),
            Out_Last => Last,
            Flush    => V_Flush);

         Rest_First := In_Last + 1;

         exit when Stream_End (Filter)
           or else Last = Item'Last
           or else (Last >= Item'First and then Allow_Read_Some);

         Item_First := Last + 1;
      end loop;
   end Read;

   ----------------
   -- Stream_End --
   ----------------

   function Stream_End (Filter : in Filter_Type) return Boolean is
   begin
      if Filter.Header = GZip and Filter.Compression then
         return Filter.Stream_End
            and then Filter.Offset = Footer_Array'Last + 1;
      else
         return Filter.Stream_End;
      end if;
   end Stream_End;

   --------------
   -- Total_In --
   --------------

   function Total_In (Filter : in Filter_Type) return Count is
   begin
      return Count (Thin.Total_In (To_Thin_Access (Filter.Strm).all));
   end Total_In;

   ---------------
   -- Total_Out --
   ---------------

   function Total_Out (Filter : in Filter_Type) return Count is
   begin
      return Count (Thin.Total_Out (To_Thin_Access (Filter.Strm).all));
   end Total_Out;

   ---------------
   -- Translate --
   ---------------

   procedure Translate
     (Filter    : in out Filter_Type;
      In_Data   : in     Ada.Streams.Stream_Element_Array;
      In_Last   :    out Ada.Streams.Stream_Element_Offset;
      Out_Data  :    out Ada.Streams.Stream_Element_Array;
      Out_Last  :    out Ada.Streams.Stream_Element_Offset;
      Flush     : in     Flush_Mode) is
   begin
      if Filter.Header = GZip and then Filter.Compression then
         Translate_GZip
           (Filter   => Filter,
            In_Data  => In_Data,
            In_Last  => In_Last,
            Out_Data => Out_Data,
            Out_Last => Out_Last,
            Flush    => Flush);
      else
         Translate_Auto
           (Filter   => Filter,
            In_Data  => In_Data,
            In_Last  => In_Last,
            Out_Data => Out_Data,
            Out_Last => Out_Last,
            Flush    => Flush);
      end if;
   end Translate;

   --------------------
   -- Translate_Auto --
   --------------------

   procedure Translate_Auto
     (Filter    : in out Filter_Type;
      In_Data   : in     Ada.Streams.Stream_Element_Array;
      In_Last   :    out Ada.Streams.Stream_Element_Offset;
      Out_Data  :    out Ada.Streams.Stream_Element_Array;
      Out_Last  :    out Ada.Streams.Stream_Element_Offset;
      Flush     : in     Flush_Mode)
   is
      use type Thin.Int;
      Code : Thin.Int;

   begin
      if not Is_Open (Filter) then
         raise Status_Error;
      end if;

      if Out_Data'Length = 0 and then In_Data'Length = 0 then
         raise Constraint_Error;
      end if;

      Set_Out (Filter.Strm.all, Out_Data'Address, Out_Data'Length);
      Set_In  (Filter.Strm.all, In_Data'Address, In_Data'Length);

      Code := Flate (Filter.Compression).Step
        (To_Thin_Access (Filter.Strm),
         Thin.Int (Flush));

      if Code = Thin.Z_STREAM_END then
         Filter.Stream_End := True;
      else
         Check_Error (Filter.Strm.all, Code);
      end if;

      In_Last  := In_Data'Last
         - Stream_Element_Offset (Avail_In (Filter.Strm.all));
      Out_Last := Out_Data'Last
         - Stream_Element_Offset (Avail_Out (Filter.Strm.all));
   end Translate_Auto;

   --------------------
   -- Translate_GZip --
   --------------------

   procedure Translate_GZip
     (Filter    : in out Filter_Type;
      In_Data   : in     Ada.Streams.Stream_Element_Array;
      In_Last   :    out Ada.Streams.Stream_Element_Offset;
      Out_Data  :    out Ada.Streams.Stream_Element_Array;
      Out_Last  :    out Ada.Streams.Stream_Element_Offset;
      Flush     : in     Flush_Mode)
   is
      Out_First : Stream_Element_Offset;

      procedure Add_Data (Data : in Stream_Element_Array);
      --  Add data to stream from the Filter.Offset till necessary,
      --  used for add gzip headr/footer.

      procedure Put_32
        (Item : in out Stream_Element_Array;
         Data : in     Unsigned_32);
      pragma Inline (Put_32);

      --------------
      -- Add_Data --
      --------------

      procedure Add_Data (Data : in Stream_Element_Array) is
         Data_First : Stream_Element_Offset renames Filter.Offset;
         Data_Last  : Stream_Element_Offset;
         Data_Len   : Stream_Element_Offset; --  -1
         Out_Len    : Stream_Element_Offset; --  -1
      begin
         Out_First := Out_Last + 1;

         if Data_First > Data'Last then
            return;
         end if;

         Data_Len  := Data'Last     - Data_First;
         Out_Len   := Out_Data'Last - Out_First;

         if Data_Len <= Out_Len then
            Out_Last  := Out_First  + Data_Len;
            Data_Last := Data'Last;
         else
            Out_Last  := Out_Data'Last;
            Data_Last := Data_First + Out_Len;
         end if;

         Out_Data (Out_First .. Out_Last) := Data (Data_First .. Data_Last);

         Data_First := Data_Last + 1;
         Out_First  := Out_Last + 1;
      end Add_Data;

      ------------
      -- Put_32 --
      ------------

      procedure Put_32
        (Item : in out Stream_Element_Array;
         Data : in     Unsigned_32)
      is
         D : Unsigned_32 := Data;
      begin
         for J in Item'First .. Item'First + 3 loop
            Item (J) := Stream_Element (D and 16#FF#);
            D := Shift_Right (D, 8);
         end loop;
      end Put_32;

   begin
      Out_Last := Out_Data'First - 1;

      if not Filter.Stream_End then
         Add_Data (Simple_GZip_Header);

         Translate_Auto
           (Filter   => Filter,
            In_Data  => In_Data,
            In_Last  => In_Last,
            Out_Data => Out_Data (Out_First .. Out_Data'Last),
            Out_Last => Out_Last,
            Flush    => Flush);

         CRC32 (Filter.CRC, In_Data (In_Data'First .. In_Last));
      end if;

      if Filter.Stream_End and then Out_Last <= Out_Data'Last then
         --  This detection method would work only when
         --  Simple_GZip_Header'Last > Footer_Array'Last

         if Filter.Offset = Simple_GZip_Header'Last + 1 then
            Filter.Offset := Footer_Array'First;
         end if;

         declare
            Footer : Footer_Array;
         begin
            Put_32 (Footer, Filter.CRC);
            Put_32 (Footer (Footer'First + 4 .. Footer'Last),
                    Unsigned_32 (Total_In (Filter)));
            Add_Data (Footer);
         end;
      end if;
   end Translate_GZip;

   -------------
   -- Version --
   -------------

   function Version return String is
   begin
      return Interfaces.C.Strings.Value (Thin.zlibVersion);
   end Version;

   -----------
   -- Write --
   -----------

   procedure Write
     (Filter : in out Filter_Type;
      Item   : in     Ada.Streams.Stream_Element_Array;
      Flush  : in     Flush_Mode := No_Flush)
   is
      Buffer   : Stream_Element_Array (1 .. Buffer_Size);
      In_Last  : Stream_Element_Offset;
      Out_Last : Stream_Element_Offset;
      In_First : Stream_Element_Offset := Item'First;
   begin
      if Item'Length = 0 and Flush = No_Flush then
         return;
      end if;

      loop
         Translate
           (Filter   => Filter,
            In_Data  => Item (In_First .. Item'Last),
            In_Last  => In_Last,
            Out_Data => Buffer,
            Out_Last => Out_Last,
            Flush    => Flush);

         if Out_Last >= Buffer'First then
            Write (Buffer (1 .. Out_Last));
         end if;

         exit when In_Last = Item'Last or Stream_End (Filter);

         In_First := In_Last + 1;
      end loop;
   end Write;

end ZLib;

Deleted compat/zlib/contrib/ada/zlib.ads.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328








































































































































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
------------------------------------------------------------------------------
--                      ZLib for Ada thick binding.                         --
--                                                                          --
--              Copyright (C) 2002-2004 Dmitriy Anisimkov                   --
--                                                                          --
--  This library 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 2 of the License, or (at   --
--  your option) any later version.                                         --
--                                                                          --
--  This library 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 library; if not, write to the Free Software Foundation, --
--  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.          --
--                                                                          --
--  As a special exception, if other files instantiate generics from this   --
--  unit, or you link this unit with other files to produce an executable,  --
--  this  unit  does not  by itself cause  the resulting executable to be   --
--  covered by the GNU General Public License. This exception does not      --
--  however invalidate any other reasons why the executable file  might be  --
--  covered by the  GNU Public License.                                     --
------------------------------------------------------------------------------

--  $Id: zlib.ads,v 1.26 2004/09/06 06:53:19 vagul Exp $

with Ada.Streams;

with Interfaces;

package ZLib is

   ZLib_Error   : exception;
   Status_Error : exception;

   type Compression_Level is new Integer range -1 .. 9;

   type Flush_Mode is private;

   type Compression_Method is private;

   type Window_Bits_Type is new Integer range 8 .. 15;

   type Memory_Level_Type is new Integer range 1 .. 9;

   type Unsigned_32 is new Interfaces.Unsigned_32;

   type Strategy_Type is private;

   type Header_Type is (None, Auto, Default, GZip);
   --  Header type usage have a some limitation for inflate.
   --  See comment for Inflate_Init.

   subtype Count is Ada.Streams.Stream_Element_Count;

   Default_Memory_Level : constant Memory_Level_Type := 8;
   Default_Window_Bits  : constant Window_Bits_Type  := 15;

   ----------------------------------
   -- Compression method constants --
   ----------------------------------

   Deflated : constant Compression_Method;
   --  Only one method allowed in this ZLib version

   ---------------------------------
   -- Compression level constants --
   ---------------------------------

   No_Compression      : constant Compression_Level := 0;
   Best_Speed          : constant Compression_Level := 1;
   Best_Compression    : constant Compression_Level := 9;
   Default_Compression : constant Compression_Level := -1;

   --------------------------
   -- Flush mode constants --
   --------------------------

   No_Flush      : constant Flush_Mode;
   --  Regular way for compression, no flush

   Partial_Flush : constant Flush_Mode;
   --  Will be removed, use Z_SYNC_FLUSH instead

   Sync_Flush    : constant Flush_Mode;
   --  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.

   Block_Flush   : constant Flush_Mode;
   --  Z_BLOCK requests that inflate() stop
   --  if and when it get 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.

   Full_Flush    : constant Flush_Mode;
   --  All output is flushed as with 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
   --  Full_Flush too often can seriously degrade the compression.

   Finish        : constant Flush_Mode;
   --  Just for tell the compressor that input data is complete.

   ------------------------------------
   -- Compression strategy constants --
   ------------------------------------

   --  RLE stategy could be used only in version 1.2.0 and later.

   Filtered         : constant Strategy_Type;
   Huffman_Only     : constant Strategy_Type;
   RLE              : constant Strategy_Type;
   Default_Strategy : constant Strategy_Type;

   Default_Buffer_Size : constant := 4096;

   type Filter_Type is tagged limited private;
   --  The filter is for compression and for decompression.
   --  The usage of the type is depend of its initialization.

   function Version return String;
   pragma Inline (Version);
   --  Return string representation of the ZLib version.

   procedure Deflate_Init
     (Filter       : in out Filter_Type;
      Level        : in     Compression_Level  := Default_Compression;
      Strategy     : in     Strategy_Type      := Default_Strategy;
      Method       : in     Compression_Method := Deflated;
      Window_Bits  : in     Window_Bits_Type   := Default_Window_Bits;
      Memory_Level : in     Memory_Level_Type  := Default_Memory_Level;
      Header       : in     Header_Type        := Default);
   --  Compressor initialization.
   --  When Header parameter is Auto or Default, then default zlib header
   --  would be provided for compressed data.
   --  When Header is GZip, then gzip header would be set instead of
   --  default header.
   --  When Header is None, no header would be set for compressed data.

   procedure Inflate_Init
     (Filter      : in out Filter_Type;
      Window_Bits : in     Window_Bits_Type := Default_Window_Bits;
      Header      : in     Header_Type      := Default);
   --  Decompressor initialization.
   --  Default header type mean that ZLib default header is expecting in the
   --  input compressed stream.
   --  Header type None mean that no header is expecting in the input stream.
   --  GZip header type mean that GZip header is expecting in the
   --  input compressed stream.
   --  Auto header type mean that header type (GZip or Native) would be
   --  detected automatically in the input stream.
   --  Note that header types parameter values None, GZip and Auto are
   --  supported for inflate routine only in ZLib versions 1.2.0.2 and later.
   --  Deflate_Init is supporting all header types.

   function Is_Open (Filter : in Filter_Type) return Boolean;
   pragma Inline (Is_Open);
   --  Is the filter opened for compression or decompression.

   procedure Close
     (Filter       : in out Filter_Type;
      Ignore_Error : in     Boolean := False);
   --  Closing the compression or decompressor.
   --  If stream is closing before the complete and Ignore_Error is False,
   --  The exception would be raised.

   generic
      with procedure Data_In
        (Item : out Ada.Streams.Stream_Element_Array;
         Last : out Ada.Streams.Stream_Element_Offset);
      with procedure Data_Out
        (Item : in Ada.Streams.Stream_Element_Array);
   procedure Generic_Translate
     (Filter          : in out Filter_Type;
      In_Buffer_Size  : in     Integer := Default_Buffer_Size;
      Out_Buffer_Size : in     Integer := Default_Buffer_Size);
   --  Compress/decompress data fetch from Data_In routine and pass the result
   --  to the Data_Out routine. User should provide Data_In and Data_Out
   --  for compression/decompression data flow.
   --  Compression or decompression depend on Filter initialization.

   function Total_In (Filter : in Filter_Type) return Count;
   pragma Inline (Total_In);
   --  Returns total number of input bytes read so far

   function Total_Out (Filter : in Filter_Type) return Count;
   pragma Inline (Total_Out);
   --  Returns total number of bytes output so far

   function CRC32
     (CRC    : in Unsigned_32;
      Data   : in Ada.Streams.Stream_Element_Array)
      return Unsigned_32;
   pragma Inline (CRC32);
   --  Compute CRC32, it could be necessary for make gzip format

   procedure CRC32
     (CRC  : in out Unsigned_32;
      Data : in     Ada.Streams.Stream_Element_Array);
   pragma Inline (CRC32);
   --  Compute CRC32, it could be necessary for make gzip format

   -------------------------------------------------
   --  Below is more complex low level routines.  --
   -------------------------------------------------

   procedure Translate
     (Filter    : in out Filter_Type;
      In_Data   : in     Ada.Streams.Stream_Element_Array;
      In_Last   :    out Ada.Streams.Stream_Element_Offset;
      Out_Data  :    out Ada.Streams.Stream_Element_Array;
      Out_Last  :    out Ada.Streams.Stream_Element_Offset;
      Flush     : in     Flush_Mode);
   --  Compress/decompress the In_Data buffer and place the result into
   --  Out_Data. In_Last is the index of last element from In_Data accepted by
   --  the Filter. Out_Last is the last element of the received data from
   --  Filter. To tell the filter that incoming data are complete put the
   --  Flush parameter to Finish.

   function Stream_End (Filter : in Filter_Type) return Boolean;
   pragma Inline (Stream_End);
   --  Return the true when the stream is complete.

   procedure Flush
     (Filter    : in out Filter_Type;
      Out_Data  :    out Ada.Streams.Stream_Element_Array;
      Out_Last  :    out Ada.Streams.Stream_Element_Offset;
      Flush     : in     Flush_Mode);
   pragma Inline (Flush);
   --  Flushing the data from the compressor.

   generic
      with procedure Write
        (Item : in Ada.Streams.Stream_Element_Array);
      --  User should provide this routine for accept
      --  compressed/decompressed data.

      Buffer_Size : in Ada.Streams.Stream_Element_Offset
         := Default_Buffer_Size;
      --  Buffer size for Write user routine.

   procedure Write
     (Filter  : in out Filter_Type;
      Item    : in     Ada.Streams.Stream_Element_Array;
      Flush   : in     Flush_Mode := No_Flush);
   --  Compress/Decompress data from Item to the generic parameter procedure
   --  Write. Output buffer size could be set in Buffer_Size generic parameter.

   generic
      with procedure Read
        (Item : out Ada.Streams.Stream_Element_Array;
         Last : out Ada.Streams.Stream_Element_Offset);
      --  User should provide data for compression/decompression
      --  thru this routine.

      Buffer : in out Ada.Streams.Stream_Element_Array;
      --  Buffer for keep remaining data from the previous
      --  back read.

      Rest_First, Rest_Last : in out Ada.Streams.Stream_Element_Offset;
      --  Rest_First have to be initialized to Buffer'Last + 1
      --  Rest_Last have to be initialized to Buffer'Last
      --  before usage.

      Allow_Read_Some : in Boolean := False;
      --  Is it allowed to return Last < Item'Last before end of data.

   procedure Read
     (Filter : in out Filter_Type;
      Item   :    out Ada.Streams.Stream_Element_Array;
      Last   :    out Ada.Streams.Stream_Element_Offset;
      Flush  : in     Flush_Mode := No_Flush);
   --  Compress/Decompress data from generic parameter procedure Read to the
   --  Item. User should provide Buffer and initialized Rest_First, Rest_Last
   --  indicators. If Allow_Read_Some is True, Read routines could return
   --  Last < Item'Last only at end of stream.

private

   use Ada.Streams;

   pragma Assert (Ada.Streams.Stream_Element'Size    =    8);
   pragma Assert (Ada.Streams.Stream_Element'Modulus = 2**8);

   type Flush_Mode is new Integer range 0 .. 5;

   type Compression_Method is new Integer range 8 .. 8;

   type Strategy_Type is new Integer range 0 .. 3;

   No_Flush      : constant Flush_Mode := 0;
   Partial_Flush : constant Flush_Mode := 1;
   Sync_Flush    : constant Flush_Mode := 2;
   Full_Flush    : constant Flush_Mode := 3;
   Finish        : constant Flush_Mode := 4;
   Block_Flush   : constant Flush_Mode := 5;

   Filtered         : constant Strategy_Type := 1;
   Huffman_Only     : constant Strategy_Type := 2;
   RLE              : constant Strategy_Type := 3;
   Default_Strategy : constant Strategy_Type := 0;

   Deflated : constant Compression_Method := 8;

   type Z_Stream;

   type Z_Stream_Access is access all Z_Stream;

   type Filter_Type is tagged limited record
      Strm        : Z_Stream_Access;
      Compression : Boolean;
      Stream_End  : Boolean;
      Header      : Header_Type;
      CRC         : Unsigned_32;
      Offset      : Stream_Element_Offset;
      --  Offset for gzip header/footer output.
   end record;

end ZLib;

Deleted compat/zlib/contrib/ada/zlib.gpr.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20




















-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
project Zlib is

   for Languages use ("Ada");
   for Source_Dirs use (".");
   for Object_Dir use ".";
   for Main use ("test.adb", "mtest.adb", "read.adb", "buffer_demo");

   package Compiler is
      for Default_Switches ("ada") use ("-gnatwcfilopru", "-gnatVcdfimorst", "-gnatyabcefhiklmnoprst");
   end Compiler;

   package Linker is
      for Default_Switches ("ada") use ("-lz");
   end Linker;

   package Builder is
      for Default_Switches ("ada") use ("-s", "-gnatQ");
   end Builder;

end Zlib;

Deleted compat/zlib/contrib/amd64/amd64-match.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




































































































































































































































































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
/*
 * match.S -- optimized version of longest_match()
 * based on the similar work by Gilles Vollant, and Brian Raiter, written 1998
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the BSD License. Use by owners of Che Guevarra
 * parafernalia is prohibited, where possible, and highly discouraged
 * elsewhere.
 */

#ifndef NO_UNDERLINE
#	define	match_init	_match_init
#	define	longest_match	_longest_match
#endif

#define	scanend		ebx
#define	scanendw	bx
#define	chainlenwmask	edx /* high word: current chain len low word: s->wmask */
#define	curmatch	rsi
#define	curmatchd	esi
#define	windowbestlen	r8
#define	scanalign	r9
#define	scanalignd	r9d
#define	window		r10
#define	bestlen		r11
#define	bestlend	r11d
#define	scanstart	r12d
#define	scanstartw	r12w
#define scan		r13
#define nicematch	r14d
#define	limit		r15
#define	limitd		r15d
#define prev		rcx

/*
 * The 258 is a "magic number, not a parameter -- changing it
 * breaks the hell loose
 */
#define	MAX_MATCH	(258)
#define	MIN_MATCH	(3)
#define	MIN_LOOKAHEAD	(MAX_MATCH + MIN_MATCH + 1)
#define	MAX_MATCH_8	((MAX_MATCH + 7) & ~7)

/* stack frame offsets */
#define	LocalVarsSize	(112)
#define _chainlenwmask	( 8-LocalVarsSize)(%rsp)
#define _windowbestlen	(16-LocalVarsSize)(%rsp)
#define save_r14        (24-LocalVarsSize)(%rsp)
#define save_rsi        (32-LocalVarsSize)(%rsp)
#define save_rbx        (40-LocalVarsSize)(%rsp)
#define save_r12        (56-LocalVarsSize)(%rsp)
#define save_r13        (64-LocalVarsSize)(%rsp)
#define save_r15        (80-LocalVarsSize)(%rsp)


.globl	match_init, longest_match

/*
 * On AMD64 the first argument of a function (in our case -- the pointer to
 * deflate_state structure) is passed in %rdi, hence our offsets below are
 * all off of that.
 */

/* 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         (%3u)(%%rdi)\n",(int)(((char*)&(s->w_size))-((char*)s)));
printf("#define dsWMask         (%3u)(%%rdi)\n",(int)(((char*)&(s->w_mask))-((char*)s)));
printf("#define dsWindow        (%3u)(%%rdi)\n",(int)(((char*)&(s->window))-((char*)s)));
printf("#define dsPrev          (%3u)(%%rdi)\n",(int)(((char*)&(s->prev))-((char*)s)));
printf("#define dsMatchLen      (%3u)(%%rdi)\n",(int)(((char*)&(s->match_length))-((char*)s)));
printf("#define dsPrevMatch     (%3u)(%%rdi)\n",(int)(((char*)&(s->prev_match))-((char*)s)));
printf("#define dsStrStart      (%3u)(%%rdi)\n",(int)(((char*)&(s->strstart))-((char*)s)));
printf("#define dsMatchStart    (%3u)(%%rdi)\n",(int)(((char*)&(s->match_start))-((char*)s)));
printf("#define dsLookahead     (%3u)(%%rdi)\n",(int)(((char*)&(s->lookahead))-((char*)s)));
printf("#define dsPrevLen       (%3u)(%%rdi)\n",(int)(((char*)&(s->prev_length))-((char*)s)));
printf("#define dsMaxChainLen   (%3u)(%%rdi)\n",(int)(((char*)&(s->max_chain_length))-((char*)s)));
printf("#define dsGoodMatch     (%3u)(%%rdi)\n",(int)(((char*)&(s->good_match))-((char*)s)));
printf("#define dsNiceMatch     (%3u)(%%rdi)\n",(int)(((char*)&(s->nice_match))-((char*)s)));
}

*/


/*
  to compile for XCode 3.2 on MacOSX x86_64
  - run "gcc -g -c -DXCODE_MAC_X64_STRUCTURE amd64-match.S"
 */


#ifndef CURRENT_LINX_XCODE_MAC_X64_STRUCTURE
#define dsWSize		( 68)(%rdi)
#define dsWMask		( 76)(%rdi)
#define dsWindow	( 80)(%rdi)
#define dsPrev		( 96)(%rdi)
#define dsMatchLen	(144)(%rdi)
#define dsPrevMatch	(148)(%rdi)
#define dsStrStart	(156)(%rdi)
#define dsMatchStart	(160)(%rdi)
#define dsLookahead	(164)(%rdi)
#define dsPrevLen	(168)(%rdi)
#define dsMaxChainLen	(172)(%rdi)
#define dsGoodMatch	(188)(%rdi)
#define dsNiceMatch	(192)(%rdi)

#else 

#ifndef STRUCT_OFFSET
#	define STRUCT_OFFSET	(0)
#endif


#define dsWSize		( 56 + STRUCT_OFFSET)(%rdi)
#define dsWMask		( 64 + STRUCT_OFFSET)(%rdi)
#define dsWindow	( 72 + STRUCT_OFFSET)(%rdi)
#define dsPrev		( 88 + STRUCT_OFFSET)(%rdi)
#define dsMatchLen	(136 + STRUCT_OFFSET)(%rdi)
#define dsPrevMatch	(140 + STRUCT_OFFSET)(%rdi)
#define dsStrStart	(148 + STRUCT_OFFSET)(%rdi)
#define dsMatchStart	(152 + STRUCT_OFFSET)(%rdi)
#define dsLookahead	(156 + STRUCT_OFFSET)(%rdi)
#define dsPrevLen	(160 + STRUCT_OFFSET)(%rdi)
#define dsMaxChainLen	(164 + STRUCT_OFFSET)(%rdi)
#define dsGoodMatch	(180 + STRUCT_OFFSET)(%rdi)
#define dsNiceMatch	(184 + STRUCT_OFFSET)(%rdi)

#endif




.text

/* uInt longest_match(deflate_state *deflatestate, IPos curmatch) */

longest_match:
/*
 * Retrieve the function arguments. %curmatch will hold cur_match
 * throughout the entire function (passed via rsi on amd64).
 * rdi will hold the pointer to the deflate_state (first arg on amd64)
 */
		mov     %rsi, save_rsi
		mov     %rbx, save_rbx
		mov	%r12, save_r12
		mov     %r13, save_r13
		mov     %r14, save_r14
		mov     %r15, save_r15

/* uInt wmask = s->w_mask;						*/
/* unsigned chain_length = s->max_chain_length;				*/
/* if (s->prev_length >= s->good_match) {				*/
/*     chain_length >>= 2;						*/
/* }									*/

		movl	dsPrevLen, %eax
		movl	dsGoodMatch, %ebx
		cmpl	%ebx, %eax
		movl	dsWMask, %eax
		movl	dsMaxChainLen, %chainlenwmask
		jl	LastMatchGood
		shrl	$2, %chainlenwmask
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.				*/

		decl	%chainlenwmask
		shll	$16, %chainlenwmask
		orl	%eax, %chainlenwmask

/* if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead;	*/

		movl	dsNiceMatch, %eax
		movl	dsLookahead, %ebx
		cmpl	%eax, %ebx
		jl	LookaheadLess
		movl	%eax, %ebx
LookaheadLess:	movl	%ebx, %nicematch

/* register Bytef *scan = s->window + s->strstart;			*/

		mov	dsWindow, %window
		movl	dsStrStart, %limitd
		lea	(%limit, %window), %scan

/* Determine how many bytes the scan ptr is off from being		*/
/* dword-aligned.							*/

		mov	%scan, %scanalign
		negl	%scanalignd
		andl	$3, %scanalignd

/* IPos limit = s->strstart > (IPos)MAX_DIST(s) ?			*/
/*     s->strstart - (IPos)MAX_DIST(s) : NIL;				*/

		movl	dsWSize, %eax
		subl	$MIN_LOOKAHEAD, %eax
		xorl	%ecx, %ecx
		subl	%eax, %limitd
		cmovng	%ecx, %limitd

/* int best_len = s->prev_length;					*/

		movl	dsPrevLen, %bestlend

/* Store the sum of s->window + best_len in %windowbestlen locally, and in memory.	*/

		lea	(%window, %bestlen), %windowbestlen
		mov	%windowbestlen, _windowbestlen

/* register ush scan_start = *(ushf*)scan;				*/
/* register ush scan_end   = *(ushf*)(scan+best_len-1);			*/
/* Posf *prev = s->prev;						*/

		movzwl	(%scan), %scanstart
		movzwl	-1(%scan, %bestlen), %scanend
		mov	dsPrev, %prev

/* Jump into the main loop.						*/

		movl	%chainlenwmask, _chainlenwmask
		jmp	LoopEntry

.balign 16

/* 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.
 */
LookupLoop:
		andl	%chainlenwmask, %curmatchd
		movzwl	(%prev, %curmatch, 2), %curmatchd
		cmpl	%limitd, %curmatchd
		jbe	LeaveNow
		subl	$0x00010000, %chainlenwmask
		js	LeaveNow
LoopEntry:	cmpw	-1(%windowbestlen, %curmatch), %scanendw
		jne	LookupLoop
		cmpw	%scanstartw, (%window, %curmatch)
		jne	LookupLoop

/* Store the current value of chainlen.					*/
		movl	%chainlenwmask, _chainlenwmask

/* %scan is the string under scrutiny, and %prev 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).				*/

		mov	$(-MAX_MATCH_8), %rdx
		lea	(%curmatch, %window), %windowbestlen
		lea	MAX_MATCH_8(%windowbestlen, %scanalign), %windowbestlen
		lea	MAX_MATCH_8(%scan, %scanalign), %prev

/* the prefetching below makes very little difference... */
		prefetcht1	(%windowbestlen, %rdx)
		prefetcht1	(%prev, %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.
 *
 * 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 -- unrolling it, for example, makes no difference.
 */

#undef USE_SSE	/* works, but is 6-7% slower, than non-SSE... */

LoopCmps:
#ifdef USE_SSE
		/* Preload the SSE registers */
		movdqu	  (%windowbestlen, %rdx), %xmm1
		movdqu	  (%prev, %rdx), %xmm2
		pcmpeqb	%xmm2, %xmm1
		movdqu	16(%windowbestlen, %rdx), %xmm3
		movdqu	16(%prev, %rdx), %xmm4
		pcmpeqb	%xmm4, %xmm3
		movdqu	32(%windowbestlen, %rdx), %xmm5
		movdqu	32(%prev, %rdx), %xmm6
		pcmpeqb	%xmm6, %xmm5
		movdqu	48(%windowbestlen, %rdx), %xmm7
		movdqu	48(%prev, %rdx), %xmm8
		pcmpeqb	%xmm8, %xmm7

		/* Check the comparisions' results */
		pmovmskb %xmm1, %rax
		notw	%ax
		bsfw	%ax, %ax
		jnz	LeaveLoopCmps
		
		/* this is the only iteration of the loop with a possibility of having
		   incremented rdx by 0x108 (each loop iteration add 16*4 = 0x40 
		   and (0x40*4)+8=0x108 */
		add	$8, %rdx
		jz LenMaximum
		add	$8, %rdx

		
		pmovmskb %xmm3, %rax
		notw	%ax
		bsfw	%ax, %ax
		jnz	LeaveLoopCmps
		
		
		add	$16, %rdx


		pmovmskb %xmm5, %rax
		notw	%ax
		bsfw	%ax, %ax
		jnz	LeaveLoopCmps
		
		add	$16, %rdx


		pmovmskb %xmm7, %rax
		notw	%ax
		bsfw	%ax, %ax
		jnz	LeaveLoopCmps
		
		add	$16, %rdx
		
		jmp	LoopCmps
LeaveLoopCmps:	add	%rax, %rdx
#else
		mov	(%windowbestlen, %rdx), %rax
		xor	(%prev, %rdx), %rax
		jnz	LeaveLoopCmps
		
		mov	8(%windowbestlen, %rdx), %rax
		xor	8(%prev, %rdx), %rax
		jnz	LeaveLoopCmps8

		mov	16(%windowbestlen, %rdx), %rax
		xor	16(%prev, %rdx), %rax
		jnz	LeaveLoopCmps16
				
		add	$24, %rdx
		jnz	LoopCmps
		jmp	LenMaximum
#	if 0
/*
 * This three-liner is tantalizingly simple, but bsf is a slow instruction,
 * and the complicated alternative down below is quite a bit faster. Sad...
 */

LeaveLoopCmps:	bsf	%rax, %rax /* find the first non-zero bit */
		shrl	$3, %eax /* divide by 8 to get the byte */
		add	%rax, %rdx
#	else
LeaveLoopCmps16:
		add	$8, %rdx
LeaveLoopCmps8:
		add	$8, %rdx
LeaveLoopCmps:	testl   $0xFFFFFFFF, %eax /* Check the first 4 bytes */
		jnz     Check16
		add     $4, %rdx
		shr     $32, %rax
Check16:        testw   $0xFFFF, %ax
		jnz     LenLower
		add	$2, %rdx
		shrl	$16, %eax
LenLower:	subb	$1, %al
		adc	$0, %rdx
#	endif
#endif

/* 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	(%prev, %rdx), %rax
		sub	%scan, %rax
		cmpl	$MAX_MATCH, %eax
		jge	LenMaximum

/* 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.		*/

		cmpl	%bestlend, %eax
		jg	LongerMatch
		mov	_windowbestlen, %windowbestlen
		mov	dsPrev, %prev
		movl	_chainlenwmask, %edx
		jmp	LookupLoop

/*         s->match_start = cur_match;					*/
/*         best_len = len;						*/
/*         if (len >= nice_match) break;				*/
/*         scan_end = *(ushf*)(scan+best_len-1);			*/

LongerMatch:
		movl	%eax, %bestlend
		movl	%curmatchd, dsMatchStart
		cmpl	%nicematch, %eax
		jge	LeaveNow

		lea	(%window, %bestlen), %windowbestlen
		mov	%windowbestlen, _windowbestlen

		movzwl	-1(%scan, %rax), %scanend
		mov	dsPrev, %prev
		movl	_chainlenwmask, %chainlenwmask
		jmp	LookupLoop

/* Accept the current string, with the maximum possible length.		*/

LenMaximum:
		movl	$MAX_MATCH, %bestlend
		movl	%curmatchd, dsMatchStart

/* if ((uInt)best_len <= s->lookahead) return (uInt)best_len;		*/
/* return s->lookahead;							*/

LeaveNow:
		movl	dsLookahead, %eax
		cmpl	%eax, %bestlend
		cmovngl	%bestlend, %eax
LookaheadRet:

/* Restore the registers and return from whence we came.			*/

	mov	save_rsi, %rsi
	mov	save_rbx, %rbx
	mov	save_r12, %r12
	mov	save_r13, %r13
	mov	save_r14, %r14
	mov	save_r15, %r15

	ret

match_init:	ret

Deleted compat/zlib/contrib/asm686/README.686.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51



















































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
This is a patched version of zlib, modified to use
Pentium-Pro-optimized assembly code in the deflation algorithm. The
files changed/added by this patch are:

README.686
match.S

The speedup that this patch provides varies, depending on whether the
compiler used to build the original version of zlib falls afoul of the
PPro's speed traps. My own tests show a speedup of around 10-20% at
the default compression level, and 20-30% using -9, against a version
compiled using gcc 2.7.2.3. Your mileage may vary.

Note that this code has been tailored for the PPro/PII in particular,
and will not perform particuarly well on a Pentium.

If you are using an assembler other than GNU as, you will have to
translate match.S to use your assembler's syntax. (Have fun.)

Brian Raiter
breadbox@muppetlabs.com
April, 1998


Added for zlib 1.1.3:

The patches come from
http://www.muppetlabs.com/~breadbox/software/assembly.html

To compile zlib with this asm file, copy match.S to the zlib directory
then do:

CFLAGS="-O3 -DASMV" ./configure
make OBJA=match.o


Update:

I've been ignoring these assembly routines for years, believing that
gcc's generated code had caught up with it sometime around gcc 2.95
and the major rearchitecting of the Pentium 4. However, I recently
learned that, despite what I believed, this code still has some life
in it. On the Pentium 4 and AMD64 chips, it continues to run about 8%
faster than the code produced by gcc 4.1.

In acknowledgement of its continuing usefulness, I've altered the
license to match that of the rest of zlib. Share and Enjoy!

Brian Raiter
breadbox@muppetlabs.com
April, 2007

Deleted compat/zlib/contrib/asm686/match.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





































































































































































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
/* match.S -- x86 assembly version of the zlib longest_match() function.
 * Optimized for the Intel 686 chips (PPro and later).
 *
 * Copyright (C) 1998, 2007 Brian Raiter <breadbox@muppetlabs.com>
 *
 * 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.
 */

#ifndef NO_UNDERLINE
#define	match_init	_match_init
#define	longest_match	_longest_match
#endif

#define	MAX_MATCH	(258)
#define	MIN_MATCH	(3)
#define	MIN_LOOKAHEAD	(MAX_MATCH + MIN_MATCH + 1)
#define	MAX_MATCH_8	((MAX_MATCH + 7) & ~7)

/* stack frame offsets */

#define	chainlenwmask		0	/* high word: current chain len	*/
					/* low word: s->wmask		*/
#define	window			4	/* local copy of s->window	*/
#define	windowbestlen		8	/* s->window + bestlen		*/
#define	scanstart		16	/* first two bytes of string	*/
#define	scanend			12	/* last two bytes of string	*/
#define	scanalign		20	/* dword-misalignment of string	*/
#define	nicematch		24	/* a good enough match size	*/
#define	bestlen			28	/* size of best match so far	*/
#define	scan			32	/* ptr to string wanting match	*/

#define	LocalVarsSize		(36)
/*	saved ebx		36 */
/*	saved edi		40 */
/*	saved esi		44 */
/*	saved ebp		48 */
/*	return address		52 */
#define	deflatestate		56	/* the function arguments	*/
#define	curmatch		60

/* 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").
 */

#define zlib1222add		(8)

#define	dsWSize			(36+zlib1222add)
#define	dsWMask			(44+zlib1222add)
#define	dsWindow		(48+zlib1222add)
#define	dsPrev			(56+zlib1222add)
#define	dsMatchLen		(88+zlib1222add)
#define	dsPrevMatch		(92+zlib1222add)
#define	dsStrStart		(100+zlib1222add)
#define	dsMatchStart		(104+zlib1222add)
#define	dsLookahead		(108+zlib1222add)
#define	dsPrevLen		(112+zlib1222add)
#define	dsMaxChainLen		(116+zlib1222add)
#define	dsGoodMatch		(132+zlib1222add)
#define	dsNiceMatch		(136+zlib1222add)


.file "match.S"

.globl	match_init, longest_match

.text

/* uInt longest_match(deflate_state *deflatestate, IPos curmatch) */
.cfi_sections	.debug_frame

longest_match:

.cfi_startproc
/* Save registers that the compiler may be using, and adjust %esp to	*/
/* make room for our stack frame.					*/

		pushl	%ebp
		.cfi_def_cfa_offset 8
		.cfi_offset ebp, -8
		pushl	%edi
		.cfi_def_cfa_offset 12
		pushl	%esi
		.cfi_def_cfa_offset 16
		pushl	%ebx
		.cfi_def_cfa_offset 20
		subl	$LocalVarsSize, %esp
		.cfi_def_cfa_offset LocalVarsSize+20

/* Retrieve the function arguments. %ecx 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).						*/

		movl	deflatestate(%esp), %edx
		movl	curmatch(%esp), %ecx

/* uInt wmask = s->w_mask;						*/
/* unsigned chain_length = s->max_chain_length;				*/
/* if (s->prev_length >= s->good_match) {				*/
/*     chain_length >>= 2;						*/
/* }									*/
 
		movl	dsPrevLen(%edx), %eax
		movl	dsGoodMatch(%edx), %ebx
		cmpl	%ebx, %eax
		movl	dsWMask(%edx), %eax
		movl	dsMaxChainLen(%edx), %ebx
		jl	LastMatchGood
		shrl	$2, %ebx
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.				*/

		decl	%ebx
		shll	$16, %ebx
		orl	%eax, %ebx
		movl	%ebx, chainlenwmask(%esp)

/* if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead;	*/

		movl	dsNiceMatch(%edx), %eax
		movl	dsLookahead(%edx), %ebx
		cmpl	%eax, %ebx
		jl	LookaheadLess
		movl	%eax, %ebx
LookaheadLess:	movl	%ebx, nicematch(%esp)

/* register Bytef *scan = s->window + s->strstart;			*/

		movl	dsWindow(%edx), %esi
		movl	%esi, window(%esp)
		movl	dsStrStart(%edx), %ebp
		lea	(%esi,%ebp), %edi
		movl	%edi, scan(%esp)

/* Determine how many bytes the scan ptr is off from being		*/
/* dword-aligned.							*/

		movl	%edi, %eax
		negl	%eax
		andl	$3, %eax
		movl	%eax, scanalign(%esp)

/* IPos limit = s->strstart > (IPos)MAX_DIST(s) ?			*/
/*     s->strstart - (IPos)MAX_DIST(s) : NIL;				*/

		movl	dsWSize(%edx), %eax
		subl	$MIN_LOOKAHEAD, %eax
		subl	%eax, %ebp
		jg	LimitPositive
		xorl	%ebp, %ebp
LimitPositive:

/* int best_len = s->prev_length;					*/

		movl	dsPrevLen(%edx), %eax
		movl	%eax, bestlen(%esp)

/* Store the sum of s->window + best_len in %esi locally, and in %esi.	*/

		addl	%eax, %esi
		movl	%esi, windowbestlen(%esp)

/* register ush scan_start = *(ushf*)scan;				*/
/* register ush scan_end   = *(ushf*)(scan+best_len-1);			*/
/* Posf *prev = s->prev;						*/

		movzwl	(%edi), %ebx
		movl	%ebx, scanstart(%esp)
		movzwl	-1(%edi,%eax), %ebx
		movl	%ebx, scanend(%esp)
		movl	dsPrev(%edx), %edi

/* Jump into the main loop.						*/

		movl	chainlenwmask(%esp), %edx
		jmp	LoopEntry

.balign 16

/* 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
 * %ecx = curmatch
 * %edx = chainlenwmask - i.e., ((chainlen << 16) | wmask)
 * %esi = windowbestlen - i.e., (window + bestlen)
 * %edi = prev
 * %ebp = limit
 */
LookupLoop:
		andl	%edx, %ecx
		movzwl	(%edi,%ecx,2), %ecx
		cmpl	%ebp, %ecx
		jbe	LeaveNow
		subl	$0x00010000, %edx
		js	LeaveNow
LoopEntry:	movzwl	-1(%esi,%ecx), %eax
		cmpl	%ebx, %eax
		jnz	LookupLoop
		movl	window(%esp), %eax
		movzwl	(%eax,%ecx), %eax
		cmpl	scanstart(%esp), %eax
		jnz	LookupLoop

/* Store the current value of chainlen.					*/

		movl	%edx, chainlenwmask(%esp)

/* 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).				*/

		movl	window(%esp), %esi
		movl	scan(%esp), %edi
		addl	%ecx, %esi
		movl	scanalign(%esp), %eax
		movl	$(-MAX_MATCH_8), %edx
		lea	MAX_MATCH_8(%edi,%eax), %edi
		lea	MAX_MATCH_8(%esi,%eax), %esi

/* Test the strings for equality, 8 bytes at a time. At the end,
 * adjust %edx 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. (%esi 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:
		movl	(%esi,%edx), %eax
		xorl	(%edi,%edx), %eax
		jnz	LeaveLoopCmps
		movl	4(%esi,%edx), %eax
		xorl	4(%edi,%edx), %eax
		jnz	LeaveLoopCmps4
		addl	$8, %edx
		jnz	LoopCmps
		jmp	LenMaximum
LeaveLoopCmps4:	addl	$4, %edx
LeaveLoopCmps:	testl	$0x0000FFFF, %eax
		jnz	LenLower
		addl	$2, %edx
		shrl	$16, %eax
LenLower:	subb	$1, %al
		adcl	$0, %edx

/* 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	(%edi,%edx), %eax
		movl	scan(%esp), %edi
		subl	%edi, %eax
		cmpl	$MAX_MATCH, %eax
		jge	LenMaximum

/* 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.		*/

		movl	deflatestate(%esp), %edx
		movl	bestlen(%esp), %ebx
		cmpl	%ebx, %eax
		jg	LongerMatch
		movl	windowbestlen(%esp), %esi
		movl	dsPrev(%edx), %edi
		movl	scanend(%esp), %ebx
		movl	chainlenwmask(%esp), %edx
		jmp	LookupLoop

/*         s->match_start = cur_match;					*/
/*         best_len = len;						*/
/*         if (len >= nice_match) break;				*/
/*         scan_end = *(ushf*)(scan+best_len-1);			*/

LongerMatch:	movl	nicematch(%esp), %ebx
		movl	%eax, bestlen(%esp)
		movl	%ecx, dsMatchStart(%edx)
		cmpl	%ebx, %eax
		jge	LeaveNow
		movl	window(%esp), %esi
		addl	%eax, %esi
		movl	%esi, windowbestlen(%esp)
		movzwl	-1(%edi,%eax), %ebx
		movl	dsPrev(%edx), %edi
		movl	%ebx, scanend(%esp)
		movl	chainlenwmask(%esp), %edx
		jmp	LookupLoop

/* Accept the current string, with the maximum possible length.		*/

LenMaximum:	movl	deflatestate(%esp), %edx
		movl	$MAX_MATCH, bestlen(%esp)
		movl	%ecx, dsMatchStart(%edx)

/* if ((uInt)best_len <= s->lookahead) return (uInt)best_len;		*/
/* return s->lookahead;							*/

LeaveNow:
		movl	deflatestate(%esp), %edx
		movl	bestlen(%esp), %ebx
		movl	dsLookahead(%edx), %eax
		cmpl	%eax, %ebx
		jg	LookaheadRet
		movl	%ebx, %eax
LookaheadRet:

/* Restore the stack and return from whence we came.			*/

		addl	$LocalVarsSize, %esp
		.cfi_def_cfa_offset 20
		popl	%ebx
		.cfi_def_cfa_offset 16
		popl	%esi
		.cfi_def_cfa_offset 12
		popl	%edi
		.cfi_def_cfa_offset 8
		popl	%ebp
		.cfi_def_cfa_offset 4
.cfi_endproc
match_init:	ret

Changes to compat/zlib/contrib/blast/blast.h.

53
54
55
56
57
58
59
60

61
62
63
64
65
66
67
53
54
55
56
57
58
59

60
61
62
63
64
65
66
67







-
+







 * 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 are *in are consumed for input before infun() is used.
 * 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.
 *

Changes to compat/zlib/contrib/delphi/ZLib.pas.

148
149
150
151
152
153
154
155

156
157
158
159
160
161
162
148
149
150
151
152
153
154

155
156
157
158
159
160
161
162







-
+







       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.2.11';
  zlib_version = '1.3.1';

type
  EZlibError = class(Exception);
  ECompressionError = class(EZlibError);
  EDecompressionError = class(EZlibError);

implementation

Changes to compat/zlib/contrib/dotzlib/DotZLib/ChecksumImpl.cs.

30
31
32
33
34
35
36
37

38
39
40
41
42
43
44
30
31
32
33
34
35
36

37
38
39
40
41
42
43
44







-
+







        /// </summary>
        public ChecksumGeneratorBase()
        {
            _current = 0;
        }

        /// <summary>
        /// Initializes a new instance of the checksum generator basewith a specified value
        /// 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;
        }

57
58
59
60
61
62
63
64

65
66
67
68
69
70
71
57
58
59
60
61
62
63

64
65
66
67
68
69
70
71







-
+







        /// </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 implmeneted in terms of this one.
        /// <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>

Changes to compat/zlib/contrib/dotzlib/DotZLib/CodecBase.cs.

135
136
137
138
139
140
141
142

143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163

164
165
166
167
168
169
170
135
136
137
138
139
140
141

142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162

163
164
165
166
167
168
169
170







-
+




















-
+








        /// <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 dereived 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 proccesing
        /// 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);

Changes to compat/zlib/contrib/dotzlib/DotZLib/GZipStream.cs.

242
243
244
245
246
247
248
249

250
251
252
253
254
255
256
242
243
244
245
246
247
248

249
250
251
252
253
254
255
256







-
+







        /// <exception cref="NotSupportedException">Always thrown</exception>
        public override void SetLength(long value)
        {
            throw new NotSupportedException();
        }

        /// <summary>
        ///  Not suppported.
        ///  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)
        {
264
265
266
267
268
269
270
271

272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288

289
290
291
292
293
294
295
296
297
298
299
300
301
264
265
266
267
268
269
270

271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287

288
289
290
291
292
293
294
295
296
297
298
299
300
301







-
+
















-
+













        /// 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 suppported.
        /// 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 suppported.
        /// 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
    }
}

Changes to compat/zlib/contrib/dotzlib/DotZLib/UnitTests.cs.

152
153
154
155
156
157
158
159

160
161
162
163
164
165
166
152
153
154
155
156
157
158

159
160
161
162
163
164
165
166







-
+







    public class InfoTests
    {
        #region Info tests
        [Test]
        public void Info_Version()
        {
            Info info = new Info();
            Assert.AreEqual("1.2.11", Info.Version);
            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
    }

Changes to compat/zlib/contrib/dotzlib/readme.txt.

32
33
34
35
36
37
38
39

40
41
42
43
44
45
46
32
33
34
35
36
37
38

39
40
41
42
43
44
45
46







-
+







   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 esclude unit tests (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

Changes to compat/zlib/contrib/infback9/infback9.c.

12
13
14
15
16
17
18
19
20
21
22


23
24
25
26
27
28
29
30
31
12
13
14
15
16
17
18




19
20


21
22
23
24
25
26
27







-
-
-
-
+
+
-
-








/*
   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_(strm, window, version, stream_size)
z_stream FAR *strm;
unsigned char FAR *window;
const char *version;
int ZEXPORT inflateBack9Init_(z_stream FAR *strm, unsigned char FAR *window,
                              const char *version, int stream_size) {
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;
47
48
49
50
51
52
53
54

55
56
57
58
59
60
61
62
43
44
45
46
47
48
49

50

51
52
53
54
55
56
57







-
+
-







/*
   Build and output length and distance decoding tables for fixed code
   decoding.
 */
#ifdef MAKEFIXED
#include <stdio.h>

void makefixed9(void)
void makefixed9(void) {
{
    unsigned sym, bits, low, size;
    code *next, *lenfix, *distfix;
    struct inflate_state state;
    code fixed[544];

    /* literal/length table */
    sym = 0;
210
211
212
213
214
215
216
217
218

219
220
221

222
223
224
225
226
227
228
229
230
205
206
207
208
209
210
211


212



213


214
215
216
217
218
219
220







-
-
+
-
-
-
+
-
-







   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(strm, in, in_desc, out, out_desc)
z_stream FAR *strm;
int ZEXPORT inflateBack9(z_stream FAR *strm, in_func in, void FAR *in_desc,
in_func in;
void FAR *in_desc;
out_func out;
                         out_func out, void FAR *out_desc) {
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 */
599
600
601
602
603
604
605
606

607
608
609
610
611
612
613
614
615
589
590
591
592
593
594
595

596


597
598
599
600
601
602
603







-
+
-
-







    /* Return unused input */
  inf_leave:
    strm->next_in = next;
    strm->avail_in = have;
    return ret;
}

int ZEXPORT inflateBack9End(strm)
int ZEXPORT inflateBack9End(z_stream FAR *strm) {
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;
}

Changes to compat/zlib/contrib/infback9/infback9.h.

16
17
18
19
20
21
22
23
24
25
26
27
28
29
30








31
32
33
34
35
36
37
16
17
18
19
20
21
22








23
24
25
26
27
28
29
30
31
32
33
34
35
36
37







-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+







 * zlib.h must be included before this header file.
 */

#ifdef __cplusplus
extern "C" {
#endif

ZEXTERN int ZEXPORT inflateBack9 OF((z_stream FAR *strm,
                                    in_func in, void FAR *in_desc,
                                    out_func out, void FAR *out_desc));
ZEXTERN int ZEXPORT inflateBack9End OF((z_stream FAR *strm));
ZEXTERN int ZEXPORT inflateBack9Init_ OF((z_stream FAR *strm,
                                         unsigned char FAR *window,
                                         const char *version,
                                         int stream_size));
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

Changes to 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
1

2
3
4
5
6
7
8
9
10
11

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31



32


33


34

35
36
37
38
39
40
41

-
+









-
+



















-
-
-
+
-
-
+
-
-
+
-







/* inftree9.c -- generate Huffman trees for efficient decoding
 * Copyright (C) 1995-2017 Mark Adler
 * 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.2.11 Copyright 1995-2017 Mark Adler ";
   " 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(type, lens, codes, table, bits, work)
codetype type;
unsigned short FAR *lens;
int inflate_table9(codetype type, unsigned short FAR *lens, unsigned codes,
unsigned codes;
code FAR * FAR *table;
                   code FAR * FAR *table, unsigned FAR *bits,
unsigned FAR *bits;
unsigned short FAR *work;
                   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 */
60
61
62
63
64
65
66
67

68
69
70
71
72
73
74
55
56
57
58
59
60
61

62
63
64
65
66
67
68
69







-
+







    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, 77, 202};
        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,

Changes to compat/zlib/contrib/infback9/inftree9.h.

34
35
36
37
38
39
40
41

42
43
44
45


46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61



34
35
36
37
38
39
40

41
42
43


44
45
46
47
48
49
50
51
52
53
54
55
56
57
58



59
60
61







-
+


-
-
+
+













-
-
-
+
+
+
    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 distribtution.  The arguments to that
   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 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
   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 OF((codetype type, unsigned short FAR *lens,
                             unsigned codes, code FAR * FAR *table,
                             unsigned FAR *bits, unsigned short FAR *work));
extern int inflate_table9(codetype type, unsigned short FAR *lens,
                          unsigned codes, code FAR * FAR *table,
                          unsigned FAR *bits, unsigned short FAR *work);

Deleted compat/zlib/contrib/inflate86/inffas86.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





































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
/* inffas86.c is a hand tuned assembler version of
 *
 * inffast.c -- fast decoding
 * Copyright (C) 1995-2003 Mark Adler
 * For conditions of distribution and use, see copyright notice in zlib.h
 *
 * Copyright (C) 2003 Chris Anderson <christop@charm.net>
 * Please use the copyright conditions above.
 *
 * Dec-29-2003 -- I added AMD64 inflate asm support.  This version is also
 * slightly quicker on x86 systems because, instead of using rep movsb to copy
 * data, it uses rep movsw, which moves data in 2-byte chunks instead of single
 * bytes.  I've tested the AMD64 code on a Fedora Core 1 + the x86_64 updates
 * from http://fedora.linux.duke.edu/fc1_x86_64
 * which is running on an Athlon 64 3000+ / Gigabyte GA-K8VT800M system with
 * 1GB ram.  The 64-bit version is about 4% faster than the 32-bit version,
 * when decompressing mozilla-source-1.3.tar.gz.
 *
 * Mar-13-2003 -- Most of this is derived from inffast.S which is derived from
 * the gcc -S output of zlib-1.2.0/inffast.c.  Zlib-1.2.0 is in beta release at
 * the moment.  I have successfully compiled and tested this code with gcc2.96,
 * gcc3.2, icc5.0, msvc6.0.  It is very close to the speed of inffast.S
 * compiled with gcc -DNO_MMX, but inffast.S is still faster on the P3 with MMX
 * enabled.  I will attempt to merge the MMX code into this version.  Newer
 * versions of this and inffast.S can be found at
 * http://www.eetbeetee.com/zlib/ and http://www.charm.net/~christop/zlib/
 */

#include "zutil.h"
#include "inftrees.h"
#include "inflate.h"
#include "inffast.h"

/* Mark Adler's comments from inffast.c: */

/*
   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 inflate_fast(strm, start)
z_streamp strm;
unsigned start;         /* inflate()'s starting value for strm->avail_out */
{
    struct inflate_state FAR *state;
    struct inffast_ar {
/* 64   32                               x86  x86_64 */
/* ar offset                              register */
/*  0    0 */ void *esp;                /* esp save */
/*  8    4 */ void *ebp;                /* ebp save */
/* 16    8 */ unsigned char FAR *in;    /* esi rsi  local strm->next_in */
/* 24   12 */ unsigned char FAR *last;  /*     r9   while in < last */
/* 32   16 */ unsigned char FAR *out;   /* edi rdi  local strm->next_out */
/* 40   20 */ unsigned char FAR *beg;   /*          inflate()'s init next_out */
/* 48   24 */ unsigned char FAR *end;   /*     r10  while out < end */
/* 56   28 */ unsigned char FAR *window;/*          size of window, wsize!=0 */
/* 64   32 */ code const FAR *lcode;    /* ebp rbp  local strm->lencode */
/* 72   36 */ code const FAR *dcode;    /*     r11  local strm->distcode */
/* 80   40 */ unsigned long hold;       /* edx rdx  local strm->hold */
/* 88   44 */ unsigned bits;            /* ebx rbx  local strm->bits */
/* 92   48 */ unsigned wsize;           /*          window size */
/* 96   52 */ unsigned write;           /*          window write index */
/*100   56 */ unsigned lmask;           /*     r12  mask for lcode */
/*104   60 */ unsigned dmask;           /*     r13  mask for dcode */
/*108   64 */ unsigned len;             /*     r14  match length */
/*112   68 */ unsigned dist;            /*     r15  match distance */
/*116   72 */ unsigned status;          /*          set when state chng*/
    } ar;

#if defined( __GNUC__ ) && defined( __amd64__ ) && ! defined( __i386 )
#define PAD_AVAIL_IN 6
#define PAD_AVAIL_OUT 258
#else
#define PAD_AVAIL_IN 5
#define PAD_AVAIL_OUT 257
#endif

    /* copy state to local variables */
    state = (struct inflate_state FAR *)strm->state;
    ar.in = strm->next_in;
    ar.last = ar.in + (strm->avail_in - PAD_AVAIL_IN);
    ar.out = strm->next_out;
    ar.beg = ar.out - (start - strm->avail_out);
    ar.end = ar.out + (strm->avail_out - PAD_AVAIL_OUT);
    ar.wsize = state->wsize;
    ar.write = state->wnext;
    ar.window = state->window;
    ar.hold = state->hold;
    ar.bits = state->bits;
    ar.lcode = state->lencode;
    ar.dcode = state->distcode;
    ar.lmask = (1U << state->lenbits) - 1;
    ar.dmask = (1U << state->distbits) - 1;

    /* decode literals and length/distances until end-of-block or not enough
       input data or output space */

    /* align in on 1/2 hold size boundary */
    while (((unsigned long)(void *)ar.in & (sizeof(ar.hold) / 2 - 1)) != 0) {
        ar.hold += (unsigned long)*ar.in++ << ar.bits;
        ar.bits += 8;
    }

#if defined( __GNUC__ ) && defined( __amd64__ ) && ! defined( __i386 )
    __asm__ __volatile__ (
"        leaq    %0, %%rax\n"
"        movq    %%rbp, 8(%%rax)\n"       /* save regs rbp and rsp */
"        movq    %%rsp, (%%rax)\n"
"        movq    %%rax, %%rsp\n"          /* make rsp point to &ar */
"        movq    16(%%rsp), %%rsi\n"      /* rsi  = in */
"        movq    32(%%rsp), %%rdi\n"      /* rdi  = out */
"        movq    24(%%rsp), %%r9\n"       /* r9   = last */
"        movq    48(%%rsp), %%r10\n"      /* r10  = end */
"        movq    64(%%rsp), %%rbp\n"      /* rbp  = lcode */
"        movq    72(%%rsp), %%r11\n"      /* r11  = dcode */
"        movq    80(%%rsp), %%rdx\n"      /* rdx  = hold */
"        movl    88(%%rsp), %%ebx\n"      /* ebx  = bits */
"        movl    100(%%rsp), %%r12d\n"    /* r12d = lmask */
"        movl    104(%%rsp), %%r13d\n"    /* r13d = dmask */
                                          /* r14d = len */
                                          /* r15d = dist */
"        cld\n"
"        cmpq    %%rdi, %%r10\n"
"        je      .L_one_time\n"           /* if only one decode left */
"        cmpq    %%rsi, %%r9\n"
"        je      .L_one_time\n"
"        jmp     .L_do_loop\n"

".L_one_time:\n"
"        movq    %%r12, %%r8\n"           /* r8 = lmask */
"        cmpb    $32, %%bl\n"
"        ja      .L_get_length_code_one_time\n"

"        lodsl\n"                         /* eax = *(uint *)in++ */
"        movb    %%bl, %%cl\n"            /* cl = bits, needs it for shifting */
"        addb    $32, %%bl\n"             /* bits += 32 */
"        shlq    %%cl, %%rax\n"
"        orq     %%rax, %%rdx\n"          /* hold |= *((uint *)in)++ << bits */
"        jmp     .L_get_length_code_one_time\n"

".align 32,0x90\n"
".L_while_test:\n"
"        cmpq    %%rdi, %%r10\n"
"        jbe     .L_break_loop\n"
"        cmpq    %%rsi, %%r9\n"
"        jbe     .L_break_loop\n"

".L_do_loop:\n"
"        movq    %%r12, %%r8\n"           /* r8 = lmask */
"        cmpb    $32, %%bl\n"
"        ja      .L_get_length_code\n"    /* if (32 < bits) */

"        lodsl\n"                         /* eax = *(uint *)in++ */
"        movb    %%bl, %%cl\n"            /* cl = bits, needs it for shifting */
"        addb    $32, %%bl\n"             /* bits += 32 */
"        shlq    %%cl, %%rax\n"
"        orq     %%rax, %%rdx\n"          /* hold |= *((uint *)in)++ << bits */

".L_get_length_code:\n"
"        andq    %%rdx, %%r8\n"            /* r8 &= hold */
"        movl    (%%rbp,%%r8,4), %%eax\n"  /* eax = lcode[hold & lmask] */

"        movb    %%ah, %%cl\n"            /* cl = this.bits */
"        subb    %%ah, %%bl\n"            /* bits -= this.bits */
"        shrq    %%cl, %%rdx\n"           /* hold >>= this.bits */

"        testb   %%al, %%al\n"
"        jnz     .L_test_for_length_base\n" /* if (op != 0) 45.7% */

"        movq    %%r12, %%r8\n"            /* r8 = lmask */
"        shrl    $16, %%eax\n"            /* output this.val char */
"        stosb\n"

".L_get_length_code_one_time:\n"
"        andq    %%rdx, %%r8\n"            /* r8 &= hold */
"        movl    (%%rbp,%%r8,4), %%eax\n" /* eax = lcode[hold & lmask] */

".L_dolen:\n"
"        movb    %%ah, %%cl\n"            /* cl = this.bits */
"        subb    %%ah, %%bl\n"            /* bits -= this.bits */
"        shrq    %%cl, %%rdx\n"           /* hold >>= this.bits */

"        testb   %%al, %%al\n"
"        jnz     .L_test_for_length_base\n" /* if (op != 0) 45.7% */

"        shrl    $16, %%eax\n"            /* output this.val char */
"        stosb\n"
"        jmp     .L_while_test\n"

".align 32,0x90\n"
".L_test_for_length_base:\n"
"        movl    %%eax, %%r14d\n"         /* len = this */
"        shrl    $16, %%r14d\n"           /* len = this.val */
"        movb    %%al, %%cl\n"

"        testb   $16, %%al\n"
"        jz      .L_test_for_second_level_length\n" /* if ((op & 16) == 0) 8% */
"        andb    $15, %%cl\n"             /* op &= 15 */
"        jz      .L_decode_distance\n"    /* if (!op) */

".L_add_bits_to_len:\n"
"        subb    %%cl, %%bl\n"
"        xorl    %%eax, %%eax\n"
"        incl    %%eax\n"
"        shll    %%cl, %%eax\n"
"        decl    %%eax\n"
"        andl    %%edx, %%eax\n"          /* eax &= hold */
"        shrq    %%cl, %%rdx\n"
"        addl    %%eax, %%r14d\n"         /* len += hold & mask[op] */

".L_decode_distance:\n"
"        movq    %%r13, %%r8\n"           /* r8 = dmask */
"        cmpb    $32, %%bl\n"
"        ja      .L_get_distance_code\n"  /* if (32 < bits) */

"        lodsl\n"                         /* eax = *(uint *)in++ */
"        movb    %%bl, %%cl\n"            /* cl = bits, needs it for shifting */
"        addb    $32, %%bl\n"             /* bits += 32 */
"        shlq    %%cl, %%rax\n"
"        orq     %%rax, %%rdx\n"          /* hold |= *((uint *)in)++ << bits */

".L_get_distance_code:\n"
"        andq    %%rdx, %%r8\n"           /* r8 &= hold */
"        movl    (%%r11,%%r8,4), %%eax\n" /* eax = dcode[hold & dmask] */

".L_dodist:\n"
"        movl    %%eax, %%r15d\n"         /* dist = this */
"        shrl    $16, %%r15d\n"           /* dist = this.val */
"        movb    %%ah, %%cl\n"
"        subb    %%ah, %%bl\n"            /* bits -= this.bits */
"        shrq    %%cl, %%rdx\n"           /* hold >>= this.bits */
"        movb    %%al, %%cl\n"            /* cl = this.op */

"        testb   $16, %%al\n"             /* if ((op & 16) == 0) */
"        jz      .L_test_for_second_level_dist\n"
"        andb    $15, %%cl\n"             /* op &= 15 */
"        jz      .L_check_dist_one\n"

".L_add_bits_to_dist:\n"
"        subb    %%cl, %%bl\n"
"        xorl    %%eax, %%eax\n"
"        incl    %%eax\n"
"        shll    %%cl, %%eax\n"
"        decl    %%eax\n"                 /* (1 << op) - 1 */
"        andl    %%edx, %%eax\n"          /* eax &= hold */
"        shrq    %%cl, %%rdx\n"
"        addl    %%eax, %%r15d\n"         /* dist += hold & ((1 << op) - 1) */

".L_check_window:\n"
"        movq    %%rsi, %%r8\n"           /* save in so from can use it's reg */
"        movq    %%rdi, %%rax\n"
"        subq    40(%%rsp), %%rax\n"      /* nbytes = out - beg */

"        cmpl    %%r15d, %%eax\n"
"        jb      .L_clip_window\n"        /* if (dist > nbytes) 4.2% */

"        movl    %%r14d, %%ecx\n"         /* ecx = len */
"        movq    %%rdi, %%rsi\n"
"        subq    %%r15, %%rsi\n"          /* from = out - dist */

"        sarl    %%ecx\n"
"        jnc     .L_copy_two\n"           /* if len % 2 == 0 */

"        rep     movsw\n"
"        movb    (%%rsi), %%al\n"
"        movb    %%al, (%%rdi)\n"
"        incq    %%rdi\n"

"        movq    %%r8, %%rsi\n"           /* move in back to %rsi, toss from */
"        jmp     .L_while_test\n"

".L_copy_two:\n"
"        rep     movsw\n"
"        movq    %%r8, %%rsi\n"           /* move in back to %rsi, toss from */
"        jmp     .L_while_test\n"

".align 32,0x90\n"
".L_check_dist_one:\n"
"        cmpl    $1, %%r15d\n"            /* if dist 1, is a memset */
"        jne     .L_check_window\n"
"        cmpq    %%rdi, 40(%%rsp)\n"      /* if out == beg, outside window */
"        je      .L_check_window\n"

"        movl    %%r14d, %%ecx\n"         /* ecx = len */
"        movb    -1(%%rdi), %%al\n"
"        movb    %%al, %%ah\n"

"        sarl    %%ecx\n"
"        jnc     .L_set_two\n"
"        movb    %%al, (%%rdi)\n"
"        incq    %%rdi\n"

".L_set_two:\n"
"        rep     stosw\n"
"        jmp     .L_while_test\n"

".align 32,0x90\n"
".L_test_for_second_level_length:\n"
"        testb   $64, %%al\n"
"        jnz     .L_test_for_end_of_block\n" /* if ((op & 64) != 0) */

"        xorl    %%eax, %%eax\n"
"        incl    %%eax\n"
"        shll    %%cl, %%eax\n"
"        decl    %%eax\n"
"        andl    %%edx, %%eax\n"         /* eax &= hold */
"        addl    %%r14d, %%eax\n"        /* eax += len */
"        movl    (%%rbp,%%rax,4), %%eax\n" /* eax = lcode[val+(hold&mask[op])]*/
"        jmp     .L_dolen\n"

".align 32,0x90\n"
".L_test_for_second_level_dist:\n"
"        testb   $64, %%al\n"
"        jnz     .L_invalid_distance_code\n" /* if ((op & 64) != 0) */

"        xorl    %%eax, %%eax\n"
"        incl    %%eax\n"
"        shll    %%cl, %%eax\n"
"        decl    %%eax\n"
"        andl    %%edx, %%eax\n"         /* eax &= hold */
"        addl    %%r15d, %%eax\n"        /* eax += dist */
"        movl    (%%r11,%%rax,4), %%eax\n" /* eax = dcode[val+(hold&mask[op])]*/
"        jmp     .L_dodist\n"

".align 32,0x90\n"
".L_clip_window:\n"
"        movl    %%eax, %%ecx\n"         /* ecx = nbytes */
"        movl    92(%%rsp), %%eax\n"     /* eax = wsize, prepare for dist cmp */
"        negl    %%ecx\n"                /* nbytes = -nbytes */

"        cmpl    %%r15d, %%eax\n"
"        jb      .L_invalid_distance_too_far\n" /* if (dist > wsize) */

"        addl    %%r15d, %%ecx\n"         /* nbytes = dist - nbytes */
"        cmpl    $0, 96(%%rsp)\n"
"        jne     .L_wrap_around_window\n" /* if (write != 0) */

"        movq    56(%%rsp), %%rsi\n"     /* from  = window */
"        subl    %%ecx, %%eax\n"         /* eax  -= nbytes */
"        addq    %%rax, %%rsi\n"         /* from += wsize - nbytes */

"        movl    %%r14d, %%eax\n"        /* eax = len */
"        cmpl    %%ecx, %%r14d\n"
"        jbe     .L_do_copy\n"           /* if (nbytes >= len) */

"        subl    %%ecx, %%eax\n"         /* eax -= nbytes */
"        rep     movsb\n"
"        movq    %%rdi, %%rsi\n"
"        subq    %%r15, %%rsi\n"         /* from = &out[ -dist ] */
"        jmp     .L_do_copy\n"

".align 32,0x90\n"
".L_wrap_around_window:\n"
"        movl    96(%%rsp), %%eax\n"     /* eax = write */
"        cmpl    %%eax, %%ecx\n"
"        jbe     .L_contiguous_in_window\n" /* if (write >= nbytes) */

"        movl    92(%%rsp), %%esi\n"     /* from  = wsize */
"        addq    56(%%rsp), %%rsi\n"     /* from += window */
"        addq    %%rax, %%rsi\n"         /* from += write */
"        subq    %%rcx, %%rsi\n"         /* from -= nbytes */
"        subl    %%eax, %%ecx\n"         /* nbytes -= write */

"        movl    %%r14d, %%eax\n"        /* eax = len */
"        cmpl    %%ecx, %%eax\n"
"        jbe     .L_do_copy\n"           /* if (nbytes >= len) */

"        subl    %%ecx, %%eax\n"         /* len -= nbytes */
"        rep     movsb\n"
"        movq    56(%%rsp), %%rsi\n"     /* from = window */
"        movl    96(%%rsp), %%ecx\n"     /* nbytes = write */
"        cmpl    %%ecx, %%eax\n"
"        jbe     .L_do_copy\n"           /* if (nbytes >= len) */

"        subl    %%ecx, %%eax\n"         /* len -= nbytes */
"        rep     movsb\n"
"        movq    %%rdi, %%rsi\n"
"        subq    %%r15, %%rsi\n"         /* from = out - dist */
"        jmp     .L_do_copy\n"

".align 32,0x90\n"
".L_contiguous_in_window:\n"
"        movq    56(%%rsp), %%rsi\n"     /* rsi = window */
"        addq    %%rax, %%rsi\n"
"        subq    %%rcx, %%rsi\n"         /* from += write - nbytes */

"        movl    %%r14d, %%eax\n"        /* eax = len */
"        cmpl    %%ecx, %%eax\n"
"        jbe     .L_do_copy\n"           /* if (nbytes >= len) */

"        subl    %%ecx, %%eax\n"         /* len -= nbytes */
"        rep     movsb\n"
"        movq    %%rdi, %%rsi\n"
"        subq    %%r15, %%rsi\n"         /* from = out - dist */
"        jmp     .L_do_copy\n"           /* if (nbytes >= len) */

".align 32,0x90\n"
".L_do_copy:\n"
"        movl    %%eax, %%ecx\n"         /* ecx = len */
"        rep     movsb\n"

"        movq    %%r8, %%rsi\n"          /* move in back to %esi, toss from */
"        jmp     .L_while_test\n"

".L_test_for_end_of_block:\n"
"        testb   $32, %%al\n"
"        jz      .L_invalid_literal_length_code\n"
"        movl    $1, 116(%%rsp)\n"
"        jmp     .L_break_loop_with_status\n"

".L_invalid_literal_length_code:\n"
"        movl    $2, 116(%%rsp)\n"
"        jmp     .L_break_loop_with_status\n"

".L_invalid_distance_code:\n"
"        movl    $3, 116(%%rsp)\n"
"        jmp     .L_break_loop_with_status\n"

".L_invalid_distance_too_far:\n"
"        movl    $4, 116(%%rsp)\n"
"        jmp     .L_break_loop_with_status\n"

".L_break_loop:\n"
"        movl    $0, 116(%%rsp)\n"

".L_break_loop_with_status:\n"
/* put in, out, bits, and hold back into ar and pop esp */
"        movq    %%rsi, 16(%%rsp)\n"     /* in */
"        movq    %%rdi, 32(%%rsp)\n"     /* out */
"        movl    %%ebx, 88(%%rsp)\n"     /* bits */
"        movq    %%rdx, 80(%%rsp)\n"     /* hold */
"        movq    (%%rsp), %%rax\n"       /* restore rbp and rsp */
"        movq    8(%%rsp), %%rbp\n"
"        movq    %%rax, %%rsp\n"
          :
          : "m" (ar)
          : "memory", "%rax", "%rbx", "%rcx", "%rdx", "%rsi", "%rdi",
            "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15"
    );
#elif ( defined( __GNUC__ ) || defined( __ICC ) ) && defined( __i386 )
    __asm__ __volatile__ (
"        leal    %0, %%eax\n"
"        movl    %%esp, (%%eax)\n"        /* save esp, ebp */
"        movl    %%ebp, 4(%%eax)\n"
"        movl    %%eax, %%esp\n"
"        movl    8(%%esp), %%esi\n"       /* esi = in */
"        movl    16(%%esp), %%edi\n"      /* edi = out */
"        movl    40(%%esp), %%edx\n"      /* edx = hold */
"        movl    44(%%esp), %%ebx\n"      /* ebx = bits */
"        movl    32(%%esp), %%ebp\n"      /* ebp = lcode */

"        cld\n"
"        jmp     .L_do_loop\n"

".align 32,0x90\n"
".L_while_test:\n"
"        cmpl    %%edi, 24(%%esp)\n"      /* out < end */
"        jbe     .L_break_loop\n"
"        cmpl    %%esi, 12(%%esp)\n"      /* in < last */
"        jbe     .L_break_loop\n"

".L_do_loop:\n"
"        cmpb    $15, %%bl\n"
"        ja      .L_get_length_code\n"    /* if (15 < bits) */

"        xorl    %%eax, %%eax\n"
"        lodsw\n"                         /* al = *(ushort *)in++ */
"        movb    %%bl, %%cl\n"            /* cl = bits, needs it for shifting */
"        addb    $16, %%bl\n"             /* bits += 16 */
"        shll    %%cl, %%eax\n"
"        orl     %%eax, %%edx\n"        /* hold |= *((ushort *)in)++ << bits */

".L_get_length_code:\n"
"        movl    56(%%esp), %%eax\n"      /* eax = lmask */
"        andl    %%edx, %%eax\n"          /* eax &= hold */
"        movl    (%%ebp,%%eax,4), %%eax\n" /* eax = lcode[hold & lmask] */

".L_dolen:\n"
"        movb    %%ah, %%cl\n"            /* cl = this.bits */
"        subb    %%ah, %%bl\n"            /* bits -= this.bits */
"        shrl    %%cl, %%edx\n"           /* hold >>= this.bits */

"        testb   %%al, %%al\n"
"        jnz     .L_test_for_length_base\n" /* if (op != 0) 45.7% */

"        shrl    $16, %%eax\n"            /* output this.val char */
"        stosb\n"
"        jmp     .L_while_test\n"

".align 32,0x90\n"
".L_test_for_length_base:\n"
"        movl    %%eax, %%ecx\n"          /* len = this */
"        shrl    $16, %%ecx\n"            /* len = this.val */
"        movl    %%ecx, 64(%%esp)\n"      /* save len */
"        movb    %%al, %%cl\n"

"        testb   $16, %%al\n"
"        jz      .L_test_for_second_level_length\n" /* if ((op & 16) == 0) 8% */
"        andb    $15, %%cl\n"             /* op &= 15 */
"        jz      .L_decode_distance\n"    /* if (!op) */
"        cmpb    %%cl, %%bl\n"
"        jae     .L_add_bits_to_len\n"    /* if (op <= bits) */

"        movb    %%cl, %%ch\n"            /* stash op in ch, freeing cl */
"        xorl    %%eax, %%eax\n"
"        lodsw\n"                         /* al = *(ushort *)in++ */
"        movb    %%bl, %%cl\n"            /* cl = bits, needs it for shifting */
"        addb    $16, %%bl\n"             /* bits += 16 */
"        shll    %%cl, %%eax\n"
"        orl     %%eax, %%edx\n"         /* hold |= *((ushort *)in)++ << bits */
"        movb    %%ch, %%cl\n"            /* move op back to ecx */

".L_add_bits_to_len:\n"
"        subb    %%cl, %%bl\n"
"        xorl    %%eax, %%eax\n"
"        incl    %%eax\n"
"        shll    %%cl, %%eax\n"
"        decl    %%eax\n"
"        andl    %%edx, %%eax\n"          /* eax &= hold */
"        shrl    %%cl, %%edx\n"
"        addl    %%eax, 64(%%esp)\n"      /* len += hold & mask[op] */

".L_decode_distance:\n"
"        cmpb    $15, %%bl\n"
"        ja      .L_get_distance_code\n"  /* if (15 < bits) */

"        xorl    %%eax, %%eax\n"
"        lodsw\n"                         /* al = *(ushort *)in++ */
"        movb    %%bl, %%cl\n"            /* cl = bits, needs it for shifting */
"        addb    $16, %%bl\n"             /* bits += 16 */
"        shll    %%cl, %%eax\n"
"        orl     %%eax, %%edx\n"         /* hold |= *((ushort *)in)++ << bits */

".L_get_distance_code:\n"
"        movl    60(%%esp), %%eax\n"      /* eax = dmask */
"        movl    36(%%esp), %%ecx\n"      /* ecx = dcode */
"        andl    %%edx, %%eax\n"          /* eax &= hold */
"        movl    (%%ecx,%%eax,4), %%eax\n"/* eax = dcode[hold & dmask] */

".L_dodist:\n"
"        movl    %%eax, %%ebp\n"          /* dist = this */
"        shrl    $16, %%ebp\n"            /* dist = this.val */
"        movb    %%ah, %%cl\n"
"        subb    %%ah, %%bl\n"            /* bits -= this.bits */
"        shrl    %%cl, %%edx\n"           /* hold >>= this.bits */
"        movb    %%al, %%cl\n"            /* cl = this.op */

"        testb   $16, %%al\n"             /* if ((op & 16) == 0) */
"        jz      .L_test_for_second_level_dist\n"
"        andb    $15, %%cl\n"             /* op &= 15 */
"        jz      .L_check_dist_one\n"
"        cmpb    %%cl, %%bl\n"
"        jae     .L_add_bits_to_dist\n"   /* if (op <= bits) 97.6% */

"        movb    %%cl, %%ch\n"            /* stash op in ch, freeing cl */
"        xorl    %%eax, %%eax\n"
"        lodsw\n"                         /* al = *(ushort *)in++ */
"        movb    %%bl, %%cl\n"            /* cl = bits, needs it for shifting */
"        addb    $16, %%bl\n"             /* bits += 16 */
"        shll    %%cl, %%eax\n"
"        orl     %%eax, %%edx\n"        /* hold |= *((ushort *)in)++ << bits */
"        movb    %%ch, %%cl\n"            /* move op back to ecx */

".L_add_bits_to_dist:\n"
"        subb    %%cl, %%bl\n"
"        xorl    %%eax, %%eax\n"
"        incl    %%eax\n"
"        shll    %%cl, %%eax\n"
"        decl    %%eax\n"                 /* (1 << op) - 1 */
"        andl    %%edx, %%eax\n"          /* eax &= hold */
"        shrl    %%cl, %%edx\n"
"        addl    %%eax, %%ebp\n"          /* dist += hold & ((1 << op) - 1) */

".L_check_window:\n"
"        movl    %%esi, 8(%%esp)\n"       /* save in so from can use it's reg */
"        movl    %%edi, %%eax\n"
"        subl    20(%%esp), %%eax\n"      /* nbytes = out - beg */

"        cmpl    %%ebp, %%eax\n"
"        jb      .L_clip_window\n"        /* if (dist > nbytes) 4.2% */

"        movl    64(%%esp), %%ecx\n"      /* ecx = len */
"        movl    %%edi, %%esi\n"
"        subl    %%ebp, %%esi\n"          /* from = out - dist */

"        sarl    %%ecx\n"
"        jnc     .L_copy_two\n"           /* if len % 2 == 0 */

"        rep     movsw\n"
"        movb    (%%esi), %%al\n"
"        movb    %%al, (%%edi)\n"
"        incl    %%edi\n"

"        movl    8(%%esp), %%esi\n"       /* move in back to %esi, toss from */
"        movl    32(%%esp), %%ebp\n"      /* ebp = lcode */
"        jmp     .L_while_test\n"

".L_copy_two:\n"
"        rep     movsw\n"
"        movl    8(%%esp), %%esi\n"       /* move in back to %esi, toss from */
"        movl    32(%%esp), %%ebp\n"      /* ebp = lcode */
"        jmp     .L_while_test\n"

".align 32,0x90\n"
".L_check_dist_one:\n"
"        cmpl    $1, %%ebp\n"            /* if dist 1, is a memset */
"        jne     .L_check_window\n"
"        cmpl    %%edi, 20(%%esp)\n"
"        je      .L_check_window\n"      /* out == beg, if outside window */

"        movl    64(%%esp), %%ecx\n"      /* ecx = len */
"        movb    -1(%%edi), %%al\n"
"        movb    %%al, %%ah\n"

"        sarl    %%ecx\n"
"        jnc     .L_set_two\n"
"        movb    %%al, (%%edi)\n"
"        incl    %%edi\n"

".L_set_two:\n"
"        rep     stosw\n"
"        movl    32(%%esp), %%ebp\n"      /* ebp = lcode */
"        jmp     .L_while_test\n"

".align 32,0x90\n"
".L_test_for_second_level_length:\n"
"        testb   $64, %%al\n"
"        jnz     .L_test_for_end_of_block\n" /* if ((op & 64) != 0) */

"        xorl    %%eax, %%eax\n"
"        incl    %%eax\n"
"        shll    %%cl, %%eax\n"
"        decl    %%eax\n"
"        andl    %%edx, %%eax\n"         /* eax &= hold */
"        addl    64(%%esp), %%eax\n"     /* eax += len */
"        movl    (%%ebp,%%eax,4), %%eax\n" /* eax = lcode[val+(hold&mask[op])]*/
"        jmp     .L_dolen\n"

".align 32,0x90\n"
".L_test_for_second_level_dist:\n"
"        testb   $64, %%al\n"
"        jnz     .L_invalid_distance_code\n" /* if ((op & 64) != 0) */

"        xorl    %%eax, %%eax\n"
"        incl    %%eax\n"
"        shll    %%cl, %%eax\n"
"        decl    %%eax\n"
"        andl    %%edx, %%eax\n"         /* eax &= hold */
"        addl    %%ebp, %%eax\n"         /* eax += dist */
"        movl    36(%%esp), %%ecx\n"     /* ecx = dcode */
"        movl    (%%ecx,%%eax,4), %%eax\n" /* eax = dcode[val+(hold&mask[op])]*/
"        jmp     .L_dodist\n"

".align 32,0x90\n"
".L_clip_window:\n"
"        movl    %%eax, %%ecx\n"
"        movl    48(%%esp), %%eax\n"     /* eax = wsize */
"        negl    %%ecx\n"                /* nbytes = -nbytes */
"        movl    28(%%esp), %%esi\n"     /* from = window */

"        cmpl    %%ebp, %%eax\n"
"        jb      .L_invalid_distance_too_far\n" /* if (dist > wsize) */

"        addl    %%ebp, %%ecx\n"         /* nbytes = dist - nbytes */
"        cmpl    $0, 52(%%esp)\n"
"        jne     .L_wrap_around_window\n" /* if (write != 0) */

"        subl    %%ecx, %%eax\n"
"        addl    %%eax, %%esi\n"         /* from += wsize - nbytes */

"        movl    64(%%esp), %%eax\n"     /* eax = len */
"        cmpl    %%ecx, %%eax\n"
"        jbe     .L_do_copy\n"           /* if (nbytes >= len) */

"        subl    %%ecx, %%eax\n"         /* len -= nbytes */
"        rep     movsb\n"
"        movl    %%edi, %%esi\n"
"        subl    %%ebp, %%esi\n"         /* from = out - dist */
"        jmp     .L_do_copy\n"

".align 32,0x90\n"
".L_wrap_around_window:\n"
"        movl    52(%%esp), %%eax\n"     /* eax = write */
"        cmpl    %%eax, %%ecx\n"
"        jbe     .L_contiguous_in_window\n" /* if (write >= nbytes) */

"        addl    48(%%esp), %%esi\n"     /* from += wsize */
"        addl    %%eax, %%esi\n"         /* from += write */
"        subl    %%ecx, %%esi\n"         /* from -= nbytes */
"        subl    %%eax, %%ecx\n"         /* nbytes -= write */

"        movl    64(%%esp), %%eax\n"     /* eax = len */
"        cmpl    %%ecx, %%eax\n"
"        jbe     .L_do_copy\n"           /* if (nbytes >= len) */

"        subl    %%ecx, %%eax\n"         /* len -= nbytes */
"        rep     movsb\n"
"        movl    28(%%esp), %%esi\n"     /* from = window */
"        movl    52(%%esp), %%ecx\n"     /* nbytes = write */
"        cmpl    %%ecx, %%eax\n"
"        jbe     .L_do_copy\n"           /* if (nbytes >= len) */

"        subl    %%ecx, %%eax\n"         /* len -= nbytes */
"        rep     movsb\n"
"        movl    %%edi, %%esi\n"
"        subl    %%ebp, %%esi\n"         /* from = out - dist */
"        jmp     .L_do_copy\n"

".align 32,0x90\n"
".L_contiguous_in_window:\n"
"        addl    %%eax, %%esi\n"
"        subl    %%ecx, %%esi\n"         /* from += write - nbytes */

"        movl    64(%%esp), %%eax\n"     /* eax = len */
"        cmpl    %%ecx, %%eax\n"
"        jbe     .L_do_copy\n"           /* if (nbytes >= len) */

"        subl    %%ecx, %%eax\n"         /* len -= nbytes */
"        rep     movsb\n"
"        movl    %%edi, %%esi\n"
"        subl    %%ebp, %%esi\n"         /* from = out - dist */
"        jmp     .L_do_copy\n"           /* if (nbytes >= len) */

".align 32,0x90\n"
".L_do_copy:\n"
"        movl    %%eax, %%ecx\n"
"        rep     movsb\n"

"        movl    8(%%esp), %%esi\n"      /* move in back to %esi, toss from */
"        movl    32(%%esp), %%ebp\n"     /* ebp = lcode */
"        jmp     .L_while_test\n"

".L_test_for_end_of_block:\n"
"        testb   $32, %%al\n"
"        jz      .L_invalid_literal_length_code\n"
"        movl    $1, 72(%%esp)\n"
"        jmp     .L_break_loop_with_status\n"

".L_invalid_literal_length_code:\n"
"        movl    $2, 72(%%esp)\n"
"        jmp     .L_break_loop_with_status\n"

".L_invalid_distance_code:\n"
"        movl    $3, 72(%%esp)\n"
"        jmp     .L_break_loop_with_status\n"

".L_invalid_distance_too_far:\n"
"        movl    8(%%esp), %%esi\n"
"        movl    $4, 72(%%esp)\n"
"        jmp     .L_break_loop_with_status\n"

".L_break_loop:\n"
"        movl    $0, 72(%%esp)\n"

".L_break_loop_with_status:\n"
/* put in, out, bits, and hold back into ar and pop esp */
"        movl    %%esi, 8(%%esp)\n"      /* save in */
"        movl    %%edi, 16(%%esp)\n"     /* save out */
"        movl    %%ebx, 44(%%esp)\n"     /* save bits */
"        movl    %%edx, 40(%%esp)\n"     /* save hold */
"        movl    4(%%esp), %%ebp\n"      /* restore esp, ebp */
"        movl    (%%esp), %%esp\n"
          :
          : "m" (ar)
          : "memory", "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi"
    );
#elif defined( _MSC_VER ) && ! defined( _M_AMD64 )
    __asm {
	lea	eax, ar
	mov	[eax], esp         /* save esp, ebp */
	mov	[eax+4], ebp
	mov	esp, eax
	mov	esi, [esp+8]       /* esi = in */
	mov	edi, [esp+16]      /* edi = out */
	mov	edx, [esp+40]      /* edx = hold */
	mov	ebx, [esp+44]      /* ebx = bits */
	mov	ebp, [esp+32]      /* ebp = lcode */

	cld
	jmp	L_do_loop

ALIGN 4
L_while_test:
	cmp	[esp+24], edi
	jbe	L_break_loop
	cmp	[esp+12], esi
	jbe	L_break_loop

L_do_loop:
	cmp	bl, 15
	ja	L_get_length_code    /* if (15 < bits) */

	xor	eax, eax
	lodsw                         /* al = *(ushort *)in++ */
	mov	cl, bl            /* cl = bits, needs it for shifting */
	add	bl, 16             /* bits += 16 */
	shl	eax, cl
	or	edx, eax        /* hold |= *((ushort *)in)++ << bits */

L_get_length_code:
	mov	eax, [esp+56]      /* eax = lmask */
	and	eax, edx          /* eax &= hold */
	mov	eax, [ebp+eax*4] /* eax = lcode[hold & lmask] */

L_dolen:
	mov	cl, ah            /* cl = this.bits */
	sub	bl, ah            /* bits -= this.bits */
	shr	edx, cl           /* hold >>= this.bits */

	test	al, al
	jnz	L_test_for_length_base /* if (op != 0) 45.7% */

	shr	eax, 16            /* output this.val char */
	stosb
	jmp	L_while_test

ALIGN 4
L_test_for_length_base:
	mov	ecx, eax          /* len = this */
	shr	ecx, 16            /* len = this.val */
	mov	[esp+64], ecx      /* save len */
	mov	cl, al

	test	al, 16
	jz	L_test_for_second_level_length /* if ((op & 16) == 0) 8% */
	and	cl, 15             /* op &= 15 */
	jz	L_decode_distance    /* if (!op) */
	cmp	bl, cl
	jae	L_add_bits_to_len    /* if (op <= bits) */

	mov	ch, cl            /* stash op in ch, freeing cl */
	xor	eax, eax
	lodsw                         /* al = *(ushort *)in++ */
	mov	cl, bl            /* cl = bits, needs it for shifting */
	add	bl, 16             /* bits += 16 */
	shl	eax, cl
	or	edx, eax         /* hold |= *((ushort *)in)++ << bits */
	mov	cl, ch            /* move op back to ecx */

L_add_bits_to_len:
	sub	bl, cl
	xor	eax, eax
	inc	eax
	shl	eax, cl
	dec	eax
	and	eax, edx          /* eax &= hold */
	shr	edx, cl
	add	[esp+64], eax      /* len += hold & mask[op] */

L_decode_distance:
	cmp	bl, 15
	ja	L_get_distance_code  /* if (15 < bits) */

	xor	eax, eax
	lodsw                         /* al = *(ushort *)in++ */
	mov	cl, bl            /* cl = bits, needs it for shifting */
	add	bl, 16             /* bits += 16 */
	shl	eax, cl
	or	edx, eax         /* hold |= *((ushort *)in)++ << bits */

L_get_distance_code:
	mov	eax, [esp+60]      /* eax = dmask */
	mov	ecx, [esp+36]      /* ecx = dcode */
	and	eax, edx          /* eax &= hold */
	mov	eax, [ecx+eax*4]/* eax = dcode[hold & dmask] */

L_dodist:
	mov	ebp, eax          /* dist = this */
	shr	ebp, 16            /* dist = this.val */
	mov	cl, ah
	sub	bl, ah            /* bits -= this.bits */
	shr	edx, cl           /* hold >>= this.bits */
	mov	cl, al            /* cl = this.op */

	test	al, 16             /* if ((op & 16) == 0) */
	jz	L_test_for_second_level_dist
	and	cl, 15             /* op &= 15 */
	jz	L_check_dist_one
	cmp	bl, cl
	jae	L_add_bits_to_dist   /* if (op <= bits) 97.6% */

	mov	ch, cl            /* stash op in ch, freeing cl */
	xor	eax, eax
	lodsw                         /* al = *(ushort *)in++ */
	mov	cl, bl            /* cl = bits, needs it for shifting */
	add	bl, 16             /* bits += 16 */
	shl	eax, cl
	or	edx, eax        /* hold |= *((ushort *)in)++ << bits */
	mov	cl, ch            /* move op back to ecx */

L_add_bits_to_dist:
	sub	bl, cl
	xor	eax, eax
	inc	eax
	shl	eax, cl
	dec	eax                 /* (1 << op) - 1 */
	and	eax, edx          /* eax &= hold */
	shr	edx, cl
	add	ebp, eax          /* dist += hold & ((1 << op) - 1) */

L_check_window:
	mov	[esp+8], esi       /* save in so from can use it's reg */
	mov	eax, edi
	sub	eax, [esp+20]      /* nbytes = out - beg */

	cmp	eax, ebp
	jb	L_clip_window        /* if (dist > nbytes) 4.2% */

	mov	ecx, [esp+64]      /* ecx = len */
	mov	esi, edi
	sub	esi, ebp          /* from = out - dist */

	sar	ecx, 1
	jnc	L_copy_two

	rep     movsw
	mov	al, [esi]
	mov	[edi], al
	inc	edi

	mov	esi, [esp+8]      /* move in back to %esi, toss from */
	mov	ebp, [esp+32]     /* ebp = lcode */
	jmp	L_while_test

L_copy_two:
	rep     movsw
	mov	esi, [esp+8]      /* move in back to %esi, toss from */
	mov	ebp, [esp+32]     /* ebp = lcode */
	jmp	L_while_test

ALIGN 4
L_check_dist_one:
	cmp	ebp, 1            /* if dist 1, is a memset */
	jne	L_check_window
	cmp	[esp+20], edi
	je	L_check_window    /* out == beg, if outside window */

	mov	ecx, [esp+64]     /* ecx = len */
	mov	al, [edi-1]
	mov	ah, al

	sar	ecx, 1
	jnc	L_set_two
	mov	[edi], al         /* memset out with from[-1] */
	inc	edi

L_set_two:
	rep     stosw
	mov	ebp, [esp+32]     /* ebp = lcode */
	jmp	L_while_test

ALIGN 4
L_test_for_second_level_length:
	test	al, 64
	jnz	L_test_for_end_of_block /* if ((op & 64) != 0) */

	xor	eax, eax
	inc	eax
	shl	eax, cl
	dec	eax
	and	eax, edx         /* eax &= hold */
	add	eax, [esp+64]     /* eax += len */
	mov	eax, [ebp+eax*4] /* eax = lcode[val+(hold&mask[op])]*/
	jmp	L_dolen

ALIGN 4
L_test_for_second_level_dist:
	test	al, 64
	jnz	L_invalid_distance_code /* if ((op & 64) != 0) */

	xor	eax, eax
	inc	eax
	shl	eax, cl
	dec	eax
	and	eax, edx         /* eax &= hold */
	add	eax, ebp         /* eax += dist */
	mov	ecx, [esp+36]     /* ecx = dcode */
	mov	eax, [ecx+eax*4] /* eax = dcode[val+(hold&mask[op])]*/
	jmp	L_dodist

ALIGN 4
L_clip_window:
	mov	ecx, eax
	mov	eax, [esp+48]     /* eax = wsize */
	neg	ecx                /* nbytes = -nbytes */
	mov	esi, [esp+28]     /* from = window */

	cmp	eax, ebp
	jb	L_invalid_distance_too_far /* if (dist > wsize) */

	add	ecx, ebp         /* nbytes = dist - nbytes */
	cmp	dword ptr [esp+52], 0
	jne	L_wrap_around_window /* if (write != 0) */

	sub	eax, ecx
	add	esi, eax         /* from += wsize - nbytes */

	mov	eax, [esp+64]    /* eax = len */
	cmp	eax, ecx
	jbe	L_do_copy          /* if (nbytes >= len) */

	sub	eax, ecx         /* len -= nbytes */
	rep     movsb
	mov	esi, edi
	sub	esi, ebp         /* from = out - dist */
	jmp	L_do_copy

ALIGN 4
L_wrap_around_window:
	mov	eax, [esp+52]    /* eax = write */
	cmp	ecx, eax
	jbe	L_contiguous_in_window /* if (write >= nbytes) */

	add	esi, [esp+48]    /* from += wsize */
	add	esi, eax         /* from += write */
	sub	esi, ecx         /* from -= nbytes */
	sub	ecx, eax         /* nbytes -= write */

	mov	eax, [esp+64]    /* eax = len */
	cmp	eax, ecx
	jbe	L_do_copy          /* if (nbytes >= len) */

	sub	eax, ecx         /* len -= nbytes */
	rep     movsb
	mov	esi, [esp+28]     /* from = window */
	mov	ecx, [esp+52]     /* nbytes = write */
	cmp	eax, ecx
	jbe	L_do_copy          /* if (nbytes >= len) */

	sub	eax, ecx         /* len -= nbytes */
	rep     movsb
	mov	esi, edi
	sub	esi, ebp         /* from = out - dist */
	jmp	L_do_copy

ALIGN 4
L_contiguous_in_window:
	add	esi, eax
	sub	esi, ecx         /* from += write - nbytes */

	mov	eax, [esp+64]    /* eax = len */
	cmp	eax, ecx
	jbe	L_do_copy          /* if (nbytes >= len) */

	sub	eax, ecx         /* len -= nbytes */
	rep     movsb
	mov	esi, edi
	sub	esi, ebp         /* from = out - dist */
	jmp	L_do_copy

ALIGN 4
L_do_copy:
	mov	ecx, eax
	rep     movsb

	mov	esi, [esp+8]      /* move in back to %esi, toss from */
	mov	ebp, [esp+32]     /* ebp = lcode */
	jmp	L_while_test

L_test_for_end_of_block:
	test	al, 32
	jz	L_invalid_literal_length_code
	mov	dword ptr [esp+72], 1
	jmp	L_break_loop_with_status

L_invalid_literal_length_code:
	mov	dword ptr [esp+72], 2
	jmp	L_break_loop_with_status

L_invalid_distance_code:
	mov	dword ptr [esp+72], 3
	jmp	L_break_loop_with_status

L_invalid_distance_too_far:
	mov	esi, [esp+4]
	mov	dword ptr [esp+72], 4
	jmp	L_break_loop_with_status

L_break_loop:
	mov	dword ptr [esp+72], 0

L_break_loop_with_status:
/* put in, out, bits, and hold back into ar and pop esp */
	mov	[esp+8], esi     /* save in */
	mov	[esp+16], edi    /* save out */
	mov	[esp+44], ebx    /* save bits */
	mov	[esp+40], edx    /* save hold */
	mov	ebp, [esp+4]     /* restore esp, ebp */
	mov	esp, [esp]
    }
#else
#error "x86 architecture not defined"
#endif

    if (ar.status > 1) {
        if (ar.status == 2)
            strm->msg = "invalid literal/length code";
        else if (ar.status == 3)
            strm->msg = "invalid distance code";
        else
            strm->msg = "invalid distance too far back";
        state->mode = BAD;
    }
    else if ( ar.status == 1 ) {
        state->mode = TYPE;
    }

    /* return unused bytes (on entry, bits < 8, so in won't go too far back) */
    ar.len = ar.bits >> 3;
    ar.in -= ar.len;
    ar.bits -= ar.len << 3;
    ar.hold &= (1U << ar.bits) - 1;

    /* update state and return */
    strm->next_in = ar.in;
    strm->next_out = ar.out;
    strm->avail_in = (unsigned)(ar.in < ar.last ?
                                PAD_AVAIL_IN + (ar.last - ar.in) :
                                PAD_AVAIL_IN - (ar.in - ar.last));
    strm->avail_out = (unsigned)(ar.out < ar.end ?
                                 PAD_AVAIL_OUT + (ar.end - ar.out) :
                                 PAD_AVAIL_OUT - (ar.out - ar.end));
    state->hold = ar.hold;
    state->bits = ar.bits;
    return;
}

Deleted compat/zlib/contrib/inflate86/inffast.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
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
























































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
/*
 * inffast.S is a hand tuned assembler version of:
 *
 * inffast.c -- fast decoding
 * Copyright (C) 1995-2003 Mark Adler
 * For conditions of distribution and use, see copyright notice in zlib.h
 *
 * Copyright (C) 2003 Chris Anderson <christop@charm.net>
 * Please use the copyright conditions above.
 *
 * This version (Jan-23-2003) of inflate_fast was coded and tested under
 * GNU/Linux on a pentium 3, using the gcc-3.2 compiler distribution.  On that
 * machine, I found that gzip style archives decompressed about 20% faster than
 * the gcc-3.2 -O3 -fomit-frame-pointer compiled version.  Your results will
 * depend on how large of a buffer is used for z_stream.next_in & next_out
 * (8K-32K worked best for my 256K cpu cache) and how much overhead there is in
 * stream processing I/O and crc32/addler32.  In my case, this routine used
 * 70% of the cpu time and crc32 used 20%.
 *
 * I am confident that this version will work in the general case, but I have
 * not tested a wide variety of datasets or a wide variety of platforms.
 *
 * Jan-24-2003 -- Added -DUSE_MMX define for slightly faster inflating.
 * It should be a runtime flag instead of compile time flag...
 *
 * Jan-26-2003 -- Added runtime check for MMX support with cpuid instruction.
 * With -DUSE_MMX, only MMX code is compiled.  With -DNO_MMX, only non-MMX code
 * is compiled.  Without either option, runtime detection is enabled.  Runtime
 * detection should work on all modern cpus and the recomended algorithm (flip
 * ID bit on eflags and then use the cpuid instruction) is used in many
 * multimedia applications.  Tested under win2k with gcc-2.95 and gas-2.12
 * distributed with cygwin3.  Compiling with gcc-2.95 -c inffast.S -o
 * inffast.obj generates a COFF object which can then be linked with MSVC++
 * compiled code.  Tested under FreeBSD 4.7 with gcc-2.95.
 *
 * Jan-28-2003 -- Tested Athlon XP... MMX mode is slower than no MMX (and
 * slower than compiler generated code).  Adjusted cpuid check to use the MMX
 * code only for Pentiums < P4 until I have more data on the P4.  Speed
 * improvment is only about 15% on the Athlon when compared with code generated
 * with MSVC++.  Not sure yet, but I think the P4 will also be slower using the
 * MMX mode because many of it's x86 ALU instructions execute in .5 cycles and
 * have less latency than MMX ops.  Added code to buffer the last 11 bytes of
 * the input stream since the MMX code grabs bits in chunks of 32, which
 * differs from the inffast.c algorithm.  I don't think there would have been
 * read overruns where a page boundary was crossed (a segfault), but there
 * could have been overruns when next_in ends on unaligned memory (unintialized
 * memory read).
 *
 * Mar-13-2003 -- P4 MMX is slightly slower than P4 NO_MMX.  I created a C
 * version of the non-MMX code so that it doesn't depend on zstrm and zstate
 * structure offsets which are hard coded in this file.  This was last tested
 * with zlib-1.2.0 which is currently in beta testing, newer versions of this
 * and inffas86.c can be found at http://www.eetbeetee.com/zlib/ and
 * http://www.charm.net/~christop/zlib/
 */


/*
 * if you have underscore linking problems (_inflate_fast undefined), try
 * using -DGAS_COFF
 */
#if ! defined( GAS_COFF ) && ! defined( GAS_ELF )

#if defined( WIN32 ) || defined( __CYGWIN__ )
#define GAS_COFF /* windows object format */
#else
#define GAS_ELF
#endif

#endif /* ! GAS_COFF && ! GAS_ELF */


#if defined( GAS_COFF )

/* coff externals have underscores */
#define inflate_fast _inflate_fast
#define inflate_fast_use_mmx _inflate_fast_use_mmx

#endif /* GAS_COFF */


.file "inffast.S"

.globl inflate_fast

.text
.align 4,0
.L_invalid_literal_length_code_msg:
.string "invalid literal/length code"

.align 4,0
.L_invalid_distance_code_msg:
.string "invalid distance code"

.align 4,0
.L_invalid_distance_too_far_msg:
.string "invalid distance too far back"

#if ! defined( NO_MMX )
.align 4,0
.L_mask: /* mask[N] = ( 1 << N ) - 1 */
.long 0
.long 1
.long 3
.long 7
.long 15
.long 31
.long 63
.long 127
.long 255
.long 511
.long 1023
.long 2047
.long 4095
.long 8191
.long 16383
.long 32767
.long 65535
.long 131071
.long 262143
.long 524287
.long 1048575
.long 2097151
.long 4194303
.long 8388607
.long 16777215
.long 33554431
.long 67108863
.long 134217727
.long 268435455
.long 536870911
.long 1073741823
.long 2147483647
.long 4294967295
#endif /* NO_MMX */

.text

/*
 * struct z_stream offsets, in zlib.h
 */
#define next_in_strm   0   /* strm->next_in */
#define avail_in_strm  4   /* strm->avail_in */
#define next_out_strm  12  /* strm->next_out */
#define avail_out_strm 16  /* strm->avail_out */
#define msg_strm       24  /* strm->msg */
#define state_strm     28  /* strm->state */

/*
 * struct inflate_state offsets, in inflate.h
 */
#define mode_state     0   /* state->mode */
#define wsize_state    32  /* state->wsize */
#define write_state    40  /* state->write */
#define window_state   44  /* state->window */
#define hold_state     48  /* state->hold */
#define bits_state     52  /* state->bits */
#define lencode_state  68  /* state->lencode */
#define distcode_state 72  /* state->distcode */
#define lenbits_state  76  /* state->lenbits */
#define distbits_state 80  /* state->distbits */

/*
 * inflate_fast's activation record
 */
#define local_var_size 64 /* how much local space for vars */
#define strm_sp        88 /* first arg: z_stream * (local_var_size + 24) */
#define start_sp       92 /* second arg: unsigned int (local_var_size + 28) */

/*
 * offsets for local vars on stack
 */
#define out            60  /* unsigned char* */
#define window         56  /* unsigned char* */
#define wsize          52  /* unsigned int */
#define write          48  /* unsigned int */
#define in             44  /* unsigned char* */
#define beg            40  /* unsigned char* */
#define buf            28  /* char[ 12 ] */
#define len            24  /* unsigned int */
#define last           20  /* unsigned char* */
#define end            16  /* unsigned char* */
#define dcode          12  /* code* */
#define lcode           8  /* code* */
#define dmask           4  /* unsigned int */
#define lmask           0  /* unsigned int */

/*
 * typedef enum inflate_mode consts, in inflate.h
 */
#define INFLATE_MODE_TYPE 11  /* state->mode flags enum-ed in inflate.h */
#define INFLATE_MODE_BAD  26


#if ! defined( USE_MMX ) && ! defined( NO_MMX )

#define RUN_TIME_MMX

#define CHECK_MMX    1
#define DO_USE_MMX   2
#define DONT_USE_MMX 3

.globl inflate_fast_use_mmx

.data

.align 4,0
inflate_fast_use_mmx: /* integer flag for run time control 1=check,2=mmx,3=no */
.long CHECK_MMX

#if defined( GAS_ELF )
/* elf info */
.type   inflate_fast_use_mmx,@object
.size   inflate_fast_use_mmx,4
#endif

#endif /* RUN_TIME_MMX */

#if defined( GAS_COFF )
/* coff info: scl 2 = extern, type 32 = function */
.def inflate_fast; .scl 2; .type 32; .endef
#endif

.text

.align 32,0x90
inflate_fast:
        pushl   %edi
        pushl   %esi
        pushl   %ebp
        pushl   %ebx
        pushf   /* save eflags (strm_sp, state_sp assumes this is 32 bits) */
        subl    $local_var_size, %esp
        cld

#define strm_r  %esi
#define state_r %edi

        movl    strm_sp(%esp), strm_r
        movl    state_strm(strm_r), state_r

        /* in = strm->next_in;
         * out = strm->next_out;
         * last = in + strm->avail_in - 11;
         * beg = out - (start - strm->avail_out);
         * end = out + (strm->avail_out - 257);
         */
        movl    avail_in_strm(strm_r), %edx
        movl    next_in_strm(strm_r), %eax

        addl    %eax, %edx      /* avail_in += next_in */
        subl    $11, %edx       /* avail_in -= 11 */

        movl    %eax, in(%esp)
        movl    %edx, last(%esp)

        movl    start_sp(%esp), %ebp
        movl    avail_out_strm(strm_r), %ecx
        movl    next_out_strm(strm_r), %ebx

        subl    %ecx, %ebp      /* start -= avail_out */
        negl    %ebp            /* start = -start */
        addl    %ebx, %ebp      /* start += next_out */

        subl    $257, %ecx      /* avail_out -= 257 */
        addl    %ebx, %ecx      /* avail_out += out */

        movl    %ebx, out(%esp)
        movl    %ebp, beg(%esp)
        movl    %ecx, end(%esp)

        /* wsize = state->wsize;
         * write = state->write;
         * window = state->window;
         * hold = state->hold;
         * bits = state->bits;
         * lcode = state->lencode;
         * dcode = state->distcode;
         * lmask = ( 1 << state->lenbits ) - 1;
         * dmask = ( 1 << state->distbits ) - 1;
         */

        movl    lencode_state(state_r), %eax
        movl    distcode_state(state_r), %ecx

        movl    %eax, lcode(%esp)
        movl    %ecx, dcode(%esp)

        movl    $1, %eax
        movl    lenbits_state(state_r), %ecx
        shll    %cl, %eax
        decl    %eax
        movl    %eax, lmask(%esp)

        movl    $1, %eax
        movl    distbits_state(state_r), %ecx
        shll    %cl, %eax
        decl    %eax
        movl    %eax, dmask(%esp)

        movl    wsize_state(state_r), %eax
        movl    write_state(state_r), %ecx
        movl    window_state(state_r), %edx

        movl    %eax, wsize(%esp)
        movl    %ecx, write(%esp)
        movl    %edx, window(%esp)

        movl    hold_state(state_r), %ebp
        movl    bits_state(state_r), %ebx

#undef strm_r
#undef state_r

#define in_r       %esi
#define from_r     %esi
#define out_r      %edi

        movl    in(%esp), in_r
        movl    last(%esp), %ecx
        cmpl    in_r, %ecx
        ja      .L_align_long           /* if in < last */

        addl    $11, %ecx               /* ecx = &in[ avail_in ] */
        subl    in_r, %ecx              /* ecx = avail_in */
        movl    $12, %eax
        subl    %ecx, %eax              /* eax = 12 - avail_in */
        leal    buf(%esp), %edi
        rep     movsb                   /* memcpy( buf, in, avail_in ) */
        movl    %eax, %ecx
        xorl    %eax, %eax
        rep     stosb         /* memset( &buf[ avail_in ], 0, 12 - avail_in ) */
        leal    buf(%esp), in_r         /* in = buf */
        movl    in_r, last(%esp)        /* last = in, do just one iteration */
        jmp     .L_is_aligned

        /* align in_r on long boundary */
.L_align_long:
        testl   $3, in_r
        jz      .L_is_aligned
        xorl    %eax, %eax
        movb    (in_r), %al
        incl    in_r
        movl    %ebx, %ecx
        addl    $8, %ebx
        shll    %cl, %eax
        orl     %eax, %ebp
        jmp     .L_align_long

.L_is_aligned:
        movl    out(%esp), out_r

#if defined( NO_MMX )
        jmp     .L_do_loop
#endif

#if defined( USE_MMX )
        jmp     .L_init_mmx
#endif

/*** Runtime MMX check ***/

#if defined( RUN_TIME_MMX )
.L_check_mmx:
        cmpl    $DO_USE_MMX, inflate_fast_use_mmx
        je      .L_init_mmx
        ja      .L_do_loop /* > 2 */

        pushl   %eax
        pushl   %ebx
        pushl   %ecx
        pushl   %edx
        pushf
        movl    (%esp), %eax      /* copy eflags to eax */
        xorl    $0x200000, (%esp) /* try toggling ID bit of eflags (bit 21)
                                   * to see if cpu supports cpuid...
                                   * ID bit method not supported by NexGen but
                                   * bios may load a cpuid instruction and
                                   * cpuid may be disabled on Cyrix 5-6x86 */
        popf
        pushf
        popl    %edx              /* copy new eflags to edx */
        xorl    %eax, %edx        /* test if ID bit is flipped */
        jz      .L_dont_use_mmx   /* not flipped if zero */
        xorl    %eax, %eax
        cpuid
        cmpl    $0x756e6547, %ebx /* check for GenuineIntel in ebx,ecx,edx */
        jne     .L_dont_use_mmx
        cmpl    $0x6c65746e, %ecx
        jne     .L_dont_use_mmx
        cmpl    $0x49656e69, %edx
        jne     .L_dont_use_mmx
        movl    $1, %eax
        cpuid                     /* get cpu features */
        shrl    $8, %eax
        andl    $15, %eax
        cmpl    $6, %eax          /* check for Pentium family, is 0xf for P4 */
        jne     .L_dont_use_mmx
        testl   $0x800000, %edx   /* test if MMX feature is set (bit 23) */
        jnz     .L_use_mmx
        jmp     .L_dont_use_mmx
.L_use_mmx:
        movl    $DO_USE_MMX, inflate_fast_use_mmx
        jmp     .L_check_mmx_pop
.L_dont_use_mmx:
        movl    $DONT_USE_MMX, inflate_fast_use_mmx
.L_check_mmx_pop:
        popl    %edx
        popl    %ecx
        popl    %ebx
        popl    %eax
        jmp     .L_check_mmx
#endif


/*** Non-MMX code ***/

#if defined ( NO_MMX ) || defined( RUN_TIME_MMX )

#define hold_r     %ebp
#define bits_r     %bl
#define bitslong_r %ebx

.align 32,0x90
.L_while_test:
        /* while (in < last && out < end)
         */
        cmpl    out_r, end(%esp)
        jbe     .L_break_loop           /* if (out >= end) */

        cmpl    in_r, last(%esp)
        jbe     .L_break_loop

.L_do_loop:
        /* regs: %esi = in, %ebp = hold, %bl = bits, %edi = out
         *
         * do {
         *   if (bits < 15) {
         *     hold |= *((unsigned short *)in)++ << bits;
         *     bits += 16
         *   }
         *   this = lcode[hold & lmask]
         */
        cmpb    $15, bits_r
        ja      .L_get_length_code      /* if (15 < bits) */

        xorl    %eax, %eax
        lodsw                           /* al = *(ushort *)in++ */
        movb    bits_r, %cl             /* cl = bits, needs it for shifting */
        addb    $16, bits_r             /* bits += 16 */
        shll    %cl, %eax
        orl     %eax, hold_r            /* hold |= *((ushort *)in)++ << bits */

.L_get_length_code:
        movl    lmask(%esp), %edx       /* edx = lmask */
        movl    lcode(%esp), %ecx       /* ecx = lcode */
        andl    hold_r, %edx            /* edx &= hold */
        movl    (%ecx,%edx,4), %eax     /* eax = lcode[hold & lmask] */

.L_dolen:
        /* regs: %esi = in, %ebp = hold, %bl = bits, %edi = out
         *
         * dolen:
         *    bits -= this.bits;
         *    hold >>= this.bits
         */
        movb    %ah, %cl                /* cl = this.bits */
        subb    %ah, bits_r             /* bits -= this.bits */
        shrl    %cl, hold_r             /* hold >>= this.bits */

        /* check if op is a literal
         * if (op == 0) {
         *    PUP(out) = this.val;
         *  }
         */
        testb   %al, %al
        jnz     .L_test_for_length_base /* if (op != 0) 45.7% */

        shrl    $16, %eax               /* output this.val char */
        stosb
        jmp     .L_while_test

.L_test_for_length_base:
        /* regs: %esi = in, %ebp = hold, %bl = bits, %edi = out, %edx = len
         *
         * else if (op & 16) {
         *   len = this.val
         *   op &= 15
         *   if (op) {
         *     if (op > bits) {
         *       hold |= *((unsigned short *)in)++ << bits;
         *       bits += 16
         *     }
         *     len += hold & mask[op];
         *     bits -= op;
         *     hold >>= op;
         *   }
         */
#define len_r %edx
        movl    %eax, len_r             /* len = this */
        shrl    $16, len_r              /* len = this.val */
        movb    %al, %cl

        testb   $16, %al
        jz      .L_test_for_second_level_length /* if ((op & 16) == 0) 8% */
        andb    $15, %cl                /* op &= 15 */
        jz      .L_save_len             /* if (!op) */
        cmpb    %cl, bits_r
        jae     .L_add_bits_to_len      /* if (op <= bits) */

        movb    %cl, %ch                /* stash op in ch, freeing cl */
        xorl    %eax, %eax
        lodsw                           /* al = *(ushort *)in++ */
        movb    bits_r, %cl             /* cl = bits, needs it for shifting */
        addb    $16, bits_r             /* bits += 16 */
        shll    %cl, %eax
        orl     %eax, hold_r            /* hold |= *((ushort *)in)++ << bits */
        movb    %ch, %cl                /* move op back to ecx */

.L_add_bits_to_len:
        movl    $1, %eax
        shll    %cl, %eax
        decl    %eax
        subb    %cl, bits_r
        andl    hold_r, %eax            /* eax &= hold */
        shrl    %cl, hold_r
        addl    %eax, len_r             /* len += hold & mask[op] */

.L_save_len:
        movl    len_r, len(%esp)        /* save len */
#undef  len_r

.L_decode_distance:
        /* regs: %esi = in, %ebp = hold, %bl = bits, %edi = out, %edx = dist
         *
         *   if (bits < 15) {
         *     hold |= *((unsigned short *)in)++ << bits;
         *     bits += 16
         *   }
         *   this = dcode[hold & dmask];
         * dodist:
         *   bits -= this.bits;
         *   hold >>= this.bits;
         *   op = this.op;
         */

        cmpb    $15, bits_r
        ja      .L_get_distance_code    /* if (15 < bits) */

        xorl    %eax, %eax
        lodsw                           /* al = *(ushort *)in++ */
        movb    bits_r, %cl             /* cl = bits, needs it for shifting */
        addb    $16, bits_r             /* bits += 16 */
        shll    %cl, %eax
        orl     %eax, hold_r            /* hold |= *((ushort *)in)++ << bits */

.L_get_distance_code:
        movl    dmask(%esp), %edx       /* edx = dmask */
        movl    dcode(%esp), %ecx       /* ecx = dcode */
        andl    hold_r, %edx            /* edx &= hold */
        movl    (%ecx,%edx,4), %eax     /* eax = dcode[hold & dmask] */

#define dist_r %edx
.L_dodist:
        movl    %eax, dist_r            /* dist = this */
        shrl    $16, dist_r             /* dist = this.val */
        movb    %ah, %cl
        subb    %ah, bits_r             /* bits -= this.bits */
        shrl    %cl, hold_r             /* hold >>= this.bits */

        /* if (op & 16) {
         *   dist = this.val
         *   op &= 15
         *   if (op > bits) {
         *     hold |= *((unsigned short *)in)++ << bits;
         *     bits += 16
         *   }
         *   dist += hold & mask[op];
         *   bits -= op;
         *   hold >>= op;
         */
        movb    %al, %cl                /* cl = this.op */

        testb   $16, %al                /* if ((op & 16) == 0) */
        jz      .L_test_for_second_level_dist
        andb    $15, %cl                /* op &= 15 */
        jz      .L_check_dist_one
        cmpb    %cl, bits_r
        jae     .L_add_bits_to_dist     /* if (op <= bits) 97.6% */

        movb    %cl, %ch                /* stash op in ch, freeing cl */
        xorl    %eax, %eax
        lodsw                           /* al = *(ushort *)in++ */
        movb    bits_r, %cl             /* cl = bits, needs it for shifting */
        addb    $16, bits_r             /* bits += 16 */
        shll    %cl, %eax
        orl     %eax, hold_r            /* hold |= *((ushort *)in)++ << bits */
        movb    %ch, %cl                /* move op back to ecx */

.L_add_bits_to_dist:
        movl    $1, %eax
        shll    %cl, %eax
        decl    %eax                    /* (1 << op) - 1 */
        subb    %cl, bits_r
        andl    hold_r, %eax            /* eax &= hold */
        shrl    %cl, hold_r
        addl    %eax, dist_r            /* dist += hold & ((1 << op) - 1) */
        jmp     .L_check_window

.L_check_window:
        /* regs: %esi = from, %ebp = hold, %bl = bits, %edi = out, %edx = dist
         *       %ecx = nbytes
         *
         * nbytes = out - beg;
         * if (dist <= nbytes) {
         *   from = out - dist;
         *   do {
         *     PUP(out) = PUP(from);
         *   } while (--len > 0) {
         * }
         */

        movl    in_r, in(%esp)          /* save in so from can use it's reg */
        movl    out_r, %eax
        subl    beg(%esp), %eax         /* nbytes = out - beg */

        cmpl    dist_r, %eax
        jb      .L_clip_window          /* if (dist > nbytes) 4.2% */

        movl    len(%esp), %ecx
        movl    out_r, from_r
        subl    dist_r, from_r          /* from = out - dist */

        subl    $3, %ecx
        movb    (from_r), %al
        movb    %al, (out_r)
        movb    1(from_r), %al
        movb    2(from_r), %dl
        addl    $3, from_r
        movb    %al, 1(out_r)
        movb    %dl, 2(out_r)
        addl    $3, out_r
        rep     movsb

        movl    in(%esp), in_r          /* move in back to %esi, toss from */
        jmp     .L_while_test

.align 16,0x90
.L_check_dist_one:
        cmpl    $1, dist_r
        jne     .L_check_window
        cmpl    out_r, beg(%esp)
        je      .L_check_window

        decl    out_r
        movl    len(%esp), %ecx
        movb    (out_r), %al
        subl    $3, %ecx

        movb    %al, 1(out_r)
        movb    %al, 2(out_r)
        movb    %al, 3(out_r)
        addl    $4, out_r
        rep     stosb

        jmp     .L_while_test

.align 16,0x90
.L_test_for_second_level_length:
        /* else if ((op & 64) == 0) {
         *   this = lcode[this.val + (hold & mask[op])];
         * }
         */
        testb   $64, %al
        jnz     .L_test_for_end_of_block  /* if ((op & 64) != 0) */

        movl    $1, %eax
        shll    %cl, %eax
        decl    %eax
        andl    hold_r, %eax            /* eax &= hold */
        addl    %edx, %eax              /* eax += this.val */
        movl    lcode(%esp), %edx       /* edx = lcode */
        movl    (%edx,%eax,4), %eax     /* eax = lcode[val + (hold&mask[op])] */
        jmp     .L_dolen

.align 16,0x90
.L_test_for_second_level_dist:
        /* else if ((op & 64) == 0) {
         *   this = dcode[this.val + (hold & mask[op])];
         * }
         */
        testb   $64, %al
        jnz     .L_invalid_distance_code  /* if ((op & 64) != 0) */

        movl    $1, %eax
        shll    %cl, %eax
        decl    %eax
        andl    hold_r, %eax            /* eax &= hold */
        addl    %edx, %eax              /* eax += this.val */
        movl    dcode(%esp), %edx       /* edx = dcode */
        movl    (%edx,%eax,4), %eax     /* eax = dcode[val + (hold&mask[op])] */
        jmp     .L_dodist

.align 16,0x90
.L_clip_window:
        /* regs: %esi = from, %ebp = hold, %bl = bits, %edi = out, %edx = dist
         *       %ecx = nbytes
         *
         * else {
         *   if (dist > wsize) {
         *     invalid distance
         *   }
         *   from = window;
         *   nbytes = dist - nbytes;
         *   if (write == 0) {
         *     from += wsize - nbytes;
         */
#define nbytes_r %ecx
        movl    %eax, nbytes_r
        movl    wsize(%esp), %eax       /* prepare for dist compare */
        negl    nbytes_r                /* nbytes = -nbytes */
        movl    window(%esp), from_r    /* from = window */

        cmpl    dist_r, %eax
        jb      .L_invalid_distance_too_far /* if (dist > wsize) */

        addl    dist_r, nbytes_r        /* nbytes = dist - nbytes */
        cmpl    $0, write(%esp)
        jne     .L_wrap_around_window   /* if (write != 0) */

        subl    nbytes_r, %eax
        addl    %eax, from_r            /* from += wsize - nbytes */

        /* regs: %esi = from, %ebp = hold, %bl = bits, %edi = out, %edx = dist
         *       %ecx = nbytes, %eax = len
         *
         *     if (nbytes < len) {
         *       len -= nbytes;
         *       do {
         *         PUP(out) = PUP(from);
         *       } while (--nbytes);
         *       from = out - dist;
         *     }
         *   }
         */
#define len_r %eax
        movl    len(%esp), len_r
        cmpl    nbytes_r, len_r
        jbe     .L_do_copy1             /* if (nbytes >= len) */

        subl    nbytes_r, len_r         /* len -= nbytes */
        rep     movsb
        movl    out_r, from_r
        subl    dist_r, from_r          /* from = out - dist */
        jmp     .L_do_copy1

        cmpl    nbytes_r, len_r
        jbe     .L_do_copy1             /* if (nbytes >= len) */

        subl    nbytes_r, len_r         /* len -= nbytes */
        rep     movsb
        movl    out_r, from_r
        subl    dist_r, from_r          /* from = out - dist */
        jmp     .L_do_copy1

.L_wrap_around_window:
        /* regs: %esi = from, %ebp = hold, %bl = bits, %edi = out, %edx = dist
         *       %ecx = nbytes, %eax = write, %eax = len
         *
         *   else if (write < nbytes) {
         *     from += wsize + write - nbytes;
         *     nbytes -= write;
         *     if (nbytes < len) {
         *       len -= nbytes;
         *       do {
         *         PUP(out) = PUP(from);
         *       } while (--nbytes);
         *       from = window;
         *       nbytes = write;
         *       if (nbytes < len) {
         *         len -= nbytes;
         *         do {
         *           PUP(out) = PUP(from);
         *         } while(--nbytes);
         *         from = out - dist;
         *       }
         *     }
         *   }
         */
#define write_r %eax
        movl    write(%esp), write_r
        cmpl    write_r, nbytes_r
        jbe     .L_contiguous_in_window /* if (write >= nbytes) */

        addl    wsize(%esp), from_r
        addl    write_r, from_r
        subl    nbytes_r, from_r        /* from += wsize + write - nbytes */
        subl    write_r, nbytes_r       /* nbytes -= write */
#undef write_r

        movl    len(%esp), len_r
        cmpl    nbytes_r, len_r
        jbe     .L_do_copy1             /* if (nbytes >= len) */

        subl    nbytes_r, len_r         /* len -= nbytes */
        rep     movsb
        movl    window(%esp), from_r    /* from = window */
        movl    write(%esp), nbytes_r   /* nbytes = write */
        cmpl    nbytes_r, len_r
        jbe     .L_do_copy1             /* if (nbytes >= len) */

        subl    nbytes_r, len_r         /* len -= nbytes */
        rep     movsb
        movl    out_r, from_r
        subl    dist_r, from_r          /* from = out - dist */
        jmp     .L_do_copy1

.L_contiguous_in_window:
        /* regs: %esi = from, %ebp = hold, %bl = bits, %edi = out, %edx = dist
         *       %ecx = nbytes, %eax = write, %eax = len
         *
         *   else {
         *     from += write - nbytes;
         *     if (nbytes < len) {
         *       len -= nbytes;
         *       do {
         *         PUP(out) = PUP(from);
         *       } while (--nbytes);
         *       from = out - dist;
         *     }
         *   }
         */
#define write_r %eax
        addl    write_r, from_r
        subl    nbytes_r, from_r        /* from += write - nbytes */
#undef write_r

        movl    len(%esp), len_r
        cmpl    nbytes_r, len_r
        jbe     .L_do_copy1             /* if (nbytes >= len) */

        subl    nbytes_r, len_r         /* len -= nbytes */
        rep     movsb
        movl    out_r, from_r
        subl    dist_r, from_r          /* from = out - dist */

.L_do_copy1:
        /* regs: %esi = from, %esi = in, %ebp = hold, %bl = bits, %edi = out
         *       %eax = len
         *
         *     while (len > 0) {
         *       PUP(out) = PUP(from);
         *       len--;
         *     }
         *   }
         * } while (in < last && out < end);
         */
#undef nbytes_r
#define in_r %esi
        movl    len_r, %ecx
        rep     movsb

        movl    in(%esp), in_r          /* move in back to %esi, toss from */
        jmp     .L_while_test

#undef len_r
#undef dist_r

#endif /* NO_MMX || RUN_TIME_MMX */


/*** MMX code ***/

#if defined( USE_MMX ) || defined( RUN_TIME_MMX )

.align 32,0x90
.L_init_mmx:
        emms

#undef  bits_r
#undef  bitslong_r
#define bitslong_r %ebp
#define hold_mm    %mm0
        movd    %ebp, hold_mm
        movl    %ebx, bitslong_r

#define used_mm   %mm1
#define dmask2_mm %mm2
#define lmask2_mm %mm3
#define lmask_mm  %mm4
#define dmask_mm  %mm5
#define tmp_mm    %mm6

        movd    lmask(%esp), lmask_mm
        movq    lmask_mm, lmask2_mm
        movd    dmask(%esp), dmask_mm
        movq    dmask_mm, dmask2_mm
        pxor    used_mm, used_mm
        movl    lcode(%esp), %ebx       /* ebx = lcode */
        jmp     .L_do_loop_mmx

.align 32,0x90
.L_while_test_mmx:
        /* while (in < last && out < end)
         */
        cmpl    out_r, end(%esp)
        jbe     .L_break_loop           /* if (out >= end) */

        cmpl    in_r, last(%esp)
        jbe     .L_break_loop

.L_do_loop_mmx:
        psrlq   used_mm, hold_mm        /* hold_mm >>= last bit length */

        cmpl    $32, bitslong_r
        ja      .L_get_length_code_mmx  /* if (32 < bits) */

        movd    bitslong_r, tmp_mm
        movd    (in_r), %mm7
        addl    $4, in_r
        psllq   tmp_mm, %mm7
        addl    $32, bitslong_r
        por     %mm7, hold_mm           /* hold_mm |= *((uint *)in)++ << bits */

.L_get_length_code_mmx:
        pand    hold_mm, lmask_mm
        movd    lmask_mm, %eax
        movq    lmask2_mm, lmask_mm
        movl    (%ebx,%eax,4), %eax     /* eax = lcode[hold & lmask] */

.L_dolen_mmx:
        movzbl  %ah, %ecx               /* ecx = this.bits */
        movd    %ecx, used_mm
        subl    %ecx, bitslong_r        /* bits -= this.bits */

        testb   %al, %al
        jnz     .L_test_for_length_base_mmx /* if (op != 0) 45.7% */

        shrl    $16, %eax               /* output this.val char */
        stosb
        jmp     .L_while_test_mmx

.L_test_for_length_base_mmx:
#define len_r  %edx
        movl    %eax, len_r             /* len = this */
        shrl    $16, len_r              /* len = this.val */

        testb   $16, %al
        jz      .L_test_for_second_level_length_mmx /* if ((op & 16) == 0) 8% */
        andl    $15, %eax               /* op &= 15 */
        jz      .L_decode_distance_mmx  /* if (!op) */

        psrlq   used_mm, hold_mm        /* hold_mm >>= last bit length */
        movd    %eax, used_mm
        movd    hold_mm, %ecx
        subl    %eax, bitslong_r
        andl    .L_mask(,%eax,4), %ecx
        addl    %ecx, len_r             /* len += hold & mask[op] */

.L_decode_distance_mmx:
        psrlq   used_mm, hold_mm        /* hold_mm >>= last bit length */

        cmpl    $32, bitslong_r
        ja      .L_get_dist_code_mmx    /* if (32 < bits) */

        movd    bitslong_r, tmp_mm
        movd    (in_r), %mm7
        addl    $4, in_r
        psllq   tmp_mm, %mm7
        addl    $32, bitslong_r
        por     %mm7, hold_mm           /* hold_mm |= *((uint *)in)++ << bits */

.L_get_dist_code_mmx:
        movl    dcode(%esp), %ebx       /* ebx = dcode */
        pand    hold_mm, dmask_mm
        movd    dmask_mm, %eax
        movq    dmask2_mm, dmask_mm
        movl    (%ebx,%eax,4), %eax     /* eax = dcode[hold & lmask] */

.L_dodist_mmx:
#define dist_r %ebx
        movzbl  %ah, %ecx               /* ecx = this.bits */
        movl    %eax, dist_r
        shrl    $16, dist_r             /* dist  = this.val */
        subl    %ecx, bitslong_r        /* bits -= this.bits */
        movd    %ecx, used_mm

        testb   $16, %al                /* if ((op & 16) == 0) */
        jz      .L_test_for_second_level_dist_mmx
        andl    $15, %eax               /* op &= 15 */
        jz      .L_check_dist_one_mmx

.L_add_bits_to_dist_mmx:
        psrlq   used_mm, hold_mm        /* hold_mm >>= last bit length */
        movd    %eax, used_mm           /* save bit length of current op */
        movd    hold_mm, %ecx           /* get the next bits on input stream */
        subl    %eax, bitslong_r        /* bits -= op bits */
        andl    .L_mask(,%eax,4), %ecx  /* ecx   = hold & mask[op] */
        addl    %ecx, dist_r            /* dist += hold & mask[op] */

.L_check_window_mmx:
        movl    in_r, in(%esp)          /* save in so from can use it's reg */
        movl    out_r, %eax
        subl    beg(%esp), %eax         /* nbytes = out - beg */

        cmpl    dist_r, %eax
        jb      .L_clip_window_mmx      /* if (dist > nbytes) 4.2% */

        movl    len_r, %ecx
        movl    out_r, from_r
        subl    dist_r, from_r          /* from = out - dist */

        subl    $3, %ecx
        movb    (from_r), %al
        movb    %al, (out_r)
        movb    1(from_r), %al
        movb    2(from_r), %dl
        addl    $3, from_r
        movb    %al, 1(out_r)
        movb    %dl, 2(out_r)
        addl    $3, out_r
        rep     movsb

        movl    in(%esp), in_r          /* move in back to %esi, toss from */
        movl    lcode(%esp), %ebx       /* move lcode back to %ebx, toss dist */
        jmp     .L_while_test_mmx

.align 16,0x90
.L_check_dist_one_mmx:
        cmpl    $1, dist_r
        jne     .L_check_window_mmx
        cmpl    out_r, beg(%esp)
        je      .L_check_window_mmx

        decl    out_r
        movl    len_r, %ecx
        movb    (out_r), %al
        subl    $3, %ecx

        movb    %al, 1(out_r)
        movb    %al, 2(out_r)
        movb    %al, 3(out_r)
        addl    $4, out_r
        rep     stosb

        movl    lcode(%esp), %ebx       /* move lcode back to %ebx, toss dist */
        jmp     .L_while_test_mmx

.align 16,0x90
.L_test_for_second_level_length_mmx:
        testb   $64, %al
        jnz     .L_test_for_end_of_block  /* if ((op & 64) != 0) */

        andl    $15, %eax
        psrlq   used_mm, hold_mm        /* hold_mm >>= last bit length */
        movd    hold_mm, %ecx
        andl    .L_mask(,%eax,4), %ecx
        addl    len_r, %ecx
        movl    (%ebx,%ecx,4), %eax     /* eax = lcode[hold & lmask] */
        jmp     .L_dolen_mmx

.align 16,0x90
.L_test_for_second_level_dist_mmx:
        testb   $64, %al
        jnz     .L_invalid_distance_code  /* if ((op & 64) != 0) */

        andl    $15, %eax
        psrlq   used_mm, hold_mm        /* hold_mm >>= last bit length */
        movd    hold_mm, %ecx
        andl    .L_mask(,%eax,4), %ecx
        movl    dcode(%esp), %eax       /* ecx = dcode */
        addl    dist_r, %ecx
        movl    (%eax,%ecx,4), %eax     /* eax = lcode[hold & lmask] */
        jmp     .L_dodist_mmx

.align 16,0x90
.L_clip_window_mmx:
#define nbytes_r %ecx
        movl    %eax, nbytes_r
        movl    wsize(%esp), %eax       /* prepare for dist compare */
        negl    nbytes_r                /* nbytes = -nbytes */
        movl    window(%esp), from_r    /* from = window */

        cmpl    dist_r, %eax
        jb      .L_invalid_distance_too_far /* if (dist > wsize) */

        addl    dist_r, nbytes_r        /* nbytes = dist - nbytes */
        cmpl    $0, write(%esp)
        jne     .L_wrap_around_window_mmx /* if (write != 0) */

        subl    nbytes_r, %eax
        addl    %eax, from_r            /* from += wsize - nbytes */

        cmpl    nbytes_r, len_r
        jbe     .L_do_copy1_mmx         /* if (nbytes >= len) */

        subl    nbytes_r, len_r         /* len -= nbytes */
        rep     movsb
        movl    out_r, from_r
        subl    dist_r, from_r          /* from = out - dist */
        jmp     .L_do_copy1_mmx

        cmpl    nbytes_r, len_r
        jbe     .L_do_copy1_mmx         /* if (nbytes >= len) */

        subl    nbytes_r, len_r         /* len -= nbytes */
        rep     movsb
        movl    out_r, from_r
        subl    dist_r, from_r          /* from = out - dist */
        jmp     .L_do_copy1_mmx

.L_wrap_around_window_mmx:
#define write_r %eax
        movl    write(%esp), write_r
        cmpl    write_r, nbytes_r
        jbe     .L_contiguous_in_window_mmx /* if (write >= nbytes) */

        addl    wsize(%esp), from_r
        addl    write_r, from_r
        subl    nbytes_r, from_r        /* from += wsize + write - nbytes */
        subl    write_r, nbytes_r       /* nbytes -= write */
#undef write_r

        cmpl    nbytes_r, len_r
        jbe     .L_do_copy1_mmx         /* if (nbytes >= len) */

        subl    nbytes_r, len_r         /* len -= nbytes */
        rep     movsb
        movl    window(%esp), from_r    /* from = window */
        movl    write(%esp), nbytes_r   /* nbytes = write */
        cmpl    nbytes_r, len_r
        jbe     .L_do_copy1_mmx         /* if (nbytes >= len) */

        subl    nbytes_r, len_r         /* len -= nbytes */
        rep     movsb
        movl    out_r, from_r
        subl    dist_r, from_r          /* from = out - dist */
        jmp     .L_do_copy1_mmx

.L_contiguous_in_window_mmx:
#define write_r %eax
        addl    write_r, from_r
        subl    nbytes_r, from_r        /* from += write - nbytes */
#undef write_r

        cmpl    nbytes_r, len_r
        jbe     .L_do_copy1_mmx         /* if (nbytes >= len) */

        subl    nbytes_r, len_r         /* len -= nbytes */
        rep     movsb
        movl    out_r, from_r
        subl    dist_r, from_r          /* from = out - dist */

.L_do_copy1_mmx:
#undef nbytes_r
#define in_r %esi
        movl    len_r, %ecx
        rep     movsb

        movl    in(%esp), in_r          /* move in back to %esi, toss from */
        movl    lcode(%esp), %ebx       /* move lcode back to %ebx, toss dist */
        jmp     .L_while_test_mmx

#undef hold_r
#undef bitslong_r

#endif /* USE_MMX || RUN_TIME_MMX */


/*** USE_MMX, NO_MMX, and RUNTIME_MMX from here on ***/

.L_invalid_distance_code:
        /* else {
         *   strm->msg = "invalid distance code";
         *   state->mode = BAD;
         * }
         */
        movl    $.L_invalid_distance_code_msg, %ecx
        movl    $INFLATE_MODE_BAD, %edx
        jmp     .L_update_stream_state

.L_test_for_end_of_block:
        /* else if (op & 32) {
         *   state->mode = TYPE;
         *   break;
         * }
         */
        testb   $32, %al
        jz      .L_invalid_literal_length_code  /* if ((op & 32) == 0) */

        movl    $0, %ecx
        movl    $INFLATE_MODE_TYPE, %edx
        jmp     .L_update_stream_state

.L_invalid_literal_length_code:
        /* else {
         *   strm->msg = "invalid literal/length code";
         *   state->mode = BAD;
         * }
         */
        movl    $.L_invalid_literal_length_code_msg, %ecx
        movl    $INFLATE_MODE_BAD, %edx
        jmp     .L_update_stream_state

.L_invalid_distance_too_far:
        /* strm->msg = "invalid distance too far back";
         * state->mode = BAD;
         */
        movl    in(%esp), in_r          /* from_r has in's reg, put in back */
        movl    $.L_invalid_distance_too_far_msg, %ecx
        movl    $INFLATE_MODE_BAD, %edx
        jmp     .L_update_stream_state

.L_update_stream_state:
        /* set strm->msg = %ecx, strm->state->mode = %edx */
        movl    strm_sp(%esp), %eax
        testl   %ecx, %ecx              /* if (msg != NULL) */
        jz      .L_skip_msg
        movl    %ecx, msg_strm(%eax)    /* strm->msg = msg */
.L_skip_msg:
        movl    state_strm(%eax), %eax  /* state = strm->state */
        movl    %edx, mode_state(%eax)  /* state->mode = edx (BAD | TYPE) */
        jmp     .L_break_loop

.align 32,0x90
.L_break_loop:

/*
 * Regs:
 *
 * bits = %ebp when mmx, and in %ebx when non-mmx
 * hold = %hold_mm when mmx, and in %ebp when non-mmx
 * in   = %esi
 * out  = %edi
 */

#if defined( USE_MMX ) || defined( RUN_TIME_MMX )

#if defined( RUN_TIME_MMX )

        cmpl    $DO_USE_MMX, inflate_fast_use_mmx
        jne     .L_update_next_in

#endif /* RUN_TIME_MMX */

        movl    %ebp, %ebx

.L_update_next_in:

#endif

#define strm_r  %eax
#define state_r %edx

        /* len = bits >> 3;
         * in -= len;
         * bits -= len << 3;
         * hold &= (1U << bits) - 1;
         * state->hold = hold;
         * state->bits = bits;
         * strm->next_in = in;
         * strm->next_out = out;
         */
        movl    strm_sp(%esp), strm_r
        movl    %ebx, %ecx
        movl    state_strm(strm_r), state_r
        shrl    $3, %ecx
        subl    %ecx, in_r
        shll    $3, %ecx
        subl    %ecx, %ebx
        movl    out_r, next_out_strm(strm_r)
        movl    %ebx, bits_state(state_r)
        movl    %ebx, %ecx

        leal    buf(%esp), %ebx
        cmpl    %ebx, last(%esp)
        jne     .L_buf_not_used         /* if buf != last */

        subl    %ebx, in_r              /* in -= buf */
        movl    next_in_strm(strm_r), %ebx
        movl    %ebx, last(%esp)        /* last = strm->next_in */
        addl    %ebx, in_r              /* in += strm->next_in */
        movl    avail_in_strm(strm_r), %ebx
        subl    $11, %ebx
        addl    %ebx, last(%esp)    /* last = &strm->next_in[ avail_in - 11 ] */

.L_buf_not_used:
        movl    in_r, next_in_strm(strm_r)

        movl    $1, %ebx
        shll    %cl, %ebx
        decl    %ebx

#if defined( USE_MMX ) || defined( RUN_TIME_MMX )

#if defined( RUN_TIME_MMX )

        cmpl    $DO_USE_MMX, inflate_fast_use_mmx
        jne     .L_update_hold

#endif /* RUN_TIME_MMX */

        psrlq   used_mm, hold_mm        /* hold_mm >>= last bit length */
        movd    hold_mm, %ebp

        emms

.L_update_hold:

#endif /* USE_MMX || RUN_TIME_MMX */

        andl    %ebx, %ebp
        movl    %ebp, hold_state(state_r)

#define last_r %ebx

        /* strm->avail_in = in < last ? 11 + (last - in) : 11 - (in - last) */
        movl    last(%esp), last_r
        cmpl    in_r, last_r
        jbe     .L_last_is_smaller     /* if (in >= last) */

        subl    in_r, last_r           /* last -= in */
        addl    $11, last_r            /* last += 11 */
        movl    last_r, avail_in_strm(strm_r)
        jmp     .L_fixup_out
.L_last_is_smaller:
        subl    last_r, in_r           /* in -= last */
        negl    in_r                   /* in = -in */
        addl    $11, in_r              /* in += 11 */
        movl    in_r, avail_in_strm(strm_r)

#undef last_r
#define end_r %ebx

.L_fixup_out:
        /* strm->avail_out = out < end ? 257 + (end - out) : 257 - (out - end)*/
        movl    end(%esp), end_r
        cmpl    out_r, end_r
        jbe     .L_end_is_smaller      /* if (out >= end) */

        subl    out_r, end_r           /* end -= out */
        addl    $257, end_r            /* end += 257 */
        movl    end_r, avail_out_strm(strm_r)
        jmp     .L_done
.L_end_is_smaller:
        subl    end_r, out_r           /* out -= end */
        negl    out_r                  /* out = -out */
        addl    $257, out_r            /* out += 257 */
        movl    out_r, avail_out_strm(strm_r)

#undef end_r
#undef strm_r
#undef state_r

.L_done:
        addl    $local_var_size, %esp
        popf
        popl    %ebx
        popl    %ebp
        popl    %esi
        popl    %edi
        ret

#if defined( GAS_ELF )
/* elf info */
.type inflate_fast,@function
.size inflate_fast,.-inflate_fast
#endif

Changes to compat/zlib/contrib/iostream3/zfstream.h.

409
410
411
412
413
414
415
416

417
418
419
420
421
422
423
409
410
411
412
413
414
415

416
417
418
419
420
421
422
423







-
+







 *  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 insertor to peek at internals
    // 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),
448
449
450
451
452
453
454
455

456
457
458
459
460
461
462
463
464
465
466
448
449
450
451
452
453
454

455
456
457
458
459
460
461
462
463
464
465
466







-
+











  inline
  gzomanip2<T1,T2>::gzomanip2(gzofstream &(*f)(gzofstream &, T1, T2),
                              T1 v1,
                              T2 v2)
  : func(f), val1(v1), val2(v2)
  { }

// Insertor applies underlying manipulator function to stream
// 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

Deleted compat/zlib/contrib/masmx64/bld_ml64.bat.

1
2


-
-
ml64.exe /Flinffasx64 /c /Zi inffasx64.asm
ml64.exe /Flgvmat64   /c /Zi gvmat64.asm

Deleted compat/zlib/contrib/masmx64/gvmat64.asm.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
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









































































































































































































































































































































































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
;uInt longest_match_x64(
;    deflate_state *s,
;    IPos cur_match);                             /* current match */

; gvmat64.asm -- 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)
; 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 infozip Zip, I use option:
;   ml64.exe /Flgvmat64 /c /Zi /DINFOZIP gvmat64.asm
;
; to compile this file for zLib, I use option:
;   ml64.exe /Flgvmat64 /c /Zi gvmat64.asm
; Be carrefull to adapt zlib1222add below to your version of zLib
;   (if you use a version of zLib before 1.0.4 or after 1.2.2.2, change
;    value of zlib1222add later)
;
; This file compile with Microsoft Macro Assembler (x64) for AMD64
;
;   ml64.exe is given with Visual Studio 2005/2008/2010 and Windows WDK
;
;   (you can get Windows WDK with ml64 for AMD64 from
;      http://www.microsoft.com/whdc/Devtools/wdk/default.mspx for low price)
;


;uInt longest_match(s, cur_match)
;    deflate_state *s;
;    IPos cur_match;                             /* current match */
.code
longest_match PROC


;LocalVarsSize   equ 88
 LocalVarsSize   equ 72

; register used : rax,rbx,rcx,rdx,rsi,rdi,r8,r9,r10,r11,r12
; free register :  r14,r15
; register can be saved : rsp

 chainlenwmask   equ  rsp + 8 - LocalVarsSize    ; high word: current chain len
                                                 ; low word: s->wmask
;window          equ  rsp + xx - LocalVarsSize   ; local copy of s->window ; stored in r10
;windowbestlen   equ  rsp + xx - LocalVarsSize   ; s->window + bestlen , use r10+r11
;scanstart       equ  rsp + xx - LocalVarsSize   ; first two bytes of string ; stored in r12w
;scanend         equ  rsp + xx - LocalVarsSize   ; last two bytes of string use ebx
;scanalign       equ  rsp + xx - LocalVarsSize   ; dword-misalignment of string r13
;bestlen         equ  rsp + xx - LocalVarsSize   ; size of best match so far -> r11d
;scan            equ  rsp + xx - LocalVarsSize   ; ptr to string wanting match -> r9
IFDEF INFOZIP
ELSE
 nicematch       equ  (rsp + 16 - LocalVarsSize) ; a good enough match size
ENDIF

save_rdi        equ  rsp + 24 - LocalVarsSize
save_rsi        equ  rsp + 32 - LocalVarsSize
save_rbx        equ  rsp + 40 - LocalVarsSize
save_rbp        equ  rsp + 48 - LocalVarsSize
save_r12        equ  rsp + 56 - LocalVarsSize
save_r13        equ  rsp + 64 - LocalVarsSize
;save_r14        equ  rsp + 72 - LocalVarsSize
;save_r15        equ  rsp + 80 - LocalVarsSize


; summary of register usage
; scanend     ebx
; scanendw    bx
; chainlenwmask   edx
; curmatch    rsi
; curmatchd   esi
; windowbestlen   r8
; scanalign   r9
; scanalignd  r9d
; window      r10
; bestlen     r11
; bestlend    r11d
; scanstart   r12d
; scanstartw  r12w
; scan        r13
; nicematch   r14d
; limit       r15
; limitd      r15d
; prev        rcx

;  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


    MAX_MATCH           equ     258
    MIN_MATCH           equ     3
    MIN_LOOKAHEAD       equ     (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").


IFDEF INFOZIP

_DATA   SEGMENT
COMM    window_size:DWORD
; WMask ; 7fff
COMM    window:BYTE:010040H
COMM    prev:WORD:08000H
; MatchLen : unused
; PrevMatch : unused
COMM    strstart:DWORD
COMM    match_start:DWORD
; Lookahead : ignore
COMM    prev_length:DWORD ; PrevLen
COMM    max_chain_length:DWORD
COMM    good_match:DWORD
COMM    nice_match:DWORD
prev_ad equ OFFSET prev
window_ad equ OFFSET window
nicematch equ nice_match
_DATA ENDS
WMask equ 07fffh

ELSE

  IFNDEF zlib1222add
    zlib1222add equ 8
  ENDIF
dsWSize         equ 56+zlib1222add+(zlib1222add/2)
dsWMask         equ 64+zlib1222add+(zlib1222add/2)
dsWindow        equ 72+zlib1222add
dsPrev          equ 88+zlib1222add
dsMatchLen      equ 128+zlib1222add
dsPrevMatch     equ 132+zlib1222add
dsStrStart      equ 140+zlib1222add
dsMatchStart    equ 144+zlib1222add
dsLookahead     equ 148+zlib1222add
dsPrevLen       equ 152+zlib1222add
dsMaxChainLen   equ 156+zlib1222add
dsGoodMatch     equ 172+zlib1222add
dsNiceMatch     equ 176+zlib1222add

window_size     equ [ rcx + dsWSize]
WMask           equ [ rcx + dsWMask]
window_ad       equ [ rcx + dsWindow]
prev_ad         equ [ rcx + dsPrev]
strstart        equ [ rcx + dsStrStart]
match_start     equ [ rcx + dsMatchStart]
Lookahead       equ [ rcx + dsLookahead] ; 0ffffffffh on infozip
prev_length     equ [ rcx + dsPrevLen]
max_chain_length equ [ rcx + dsMaxChainLen]
good_match      equ [ rcx + dsGoodMatch]
nice_match      equ [ rcx + dsNiceMatch]
ENDIF

; parameter 1 in r8(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.



;;; 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.

; parameter 1 in rcx (deflate_state* s), param 2 in edx -> r8 (cur match)

; this clear high 32 bits of r8, which can be garbage in both r8 and rdx

        mov [save_rdi],rdi
        mov [save_rsi],rsi
        mov [save_rbx],rbx
        mov [save_rbp],rbp
IFDEF INFOZIP
        mov r8d,ecx
ELSE
        mov r8d,edx
ENDIF
        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;

IFDEF INFOZIP
        mov [chainlenwmask], ebx
; on infozip nice_match = [nice_match]
ELSE
        mov eax, nice_match
        mov [chainlenwmask], ebx
        mov r10d, Lookahead
        cmp r10d, eax
        cmovnl r10d, eax
        mov [nicematch],r10d
ENDIF

;;; 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;
IFDEF INFOZIP
        mov eax,07efah ; MAX_DIST = (WSIZE-MIN_LOOKAHEAD) (0x8000-(3+8+1))
ELSE
        mov eax, window_size
        sub eax, MIN_LOOKAHEAD
ENDIF
        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, 00010000h
        js  LeaveNow

LoopEntry1:
        cmp bx,word ptr [rsi + r8 - 1]
        jz  LookupLoopIsZero

LookupLoop2:
        and r8d, edx

        movzx   r8d, word ptr [rdi + r8*2]
        cmp r8d, ebp
        jbe LeaveNow
        sub edx, 00010000h
        js  LeaveNow

LoopEntry2:
        cmp bx,word ptr [rsi + r8 - 1]
        jz  LookupLoopIsZero

LookupLoop4:
        and r8d, edx

        movzx   r8d, word ptr [rdi + r8*2]
        cmp r8d, ebp
        jbe LeaveNow
        sub edx, 00010000h
        js  LeaveNow

LoopEntry4:

        cmp bx,word ptr [rsi + r8 - 1]
        jnz LookupLoop1
        jmp LookupLoopIsZero


;;; 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

LookupLoop:
        and r8d, edx

        movzx   r8d, word ptr [rdi + r8*2]
        cmp r8d, ebp
        jbe LeaveNow
        sub edx, 00010000h
        js  LeaveNow

LoopEntry:

        cmp bx,word ptr [rsi + r8 - 1]
        jnz LookupLoop1
LookupLoopIsZero:
        cmp     r12w, word ptr [r10 + r8]
        jnz LookupLoop1


;;; 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, 0fffffffffffffef8h; -(MAX_MATCH_8)
        lea rsi, [rsi + r13 + 0108h] ;MAX_MATCH_8]
        lea rdi, [r9 + r13 + 0108h] ;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

        jnz short LoopCmps
        jmp short LenMaximum
LeaveLoopCmps16: add rdx,8
LeaveLoopCmps8: add rdx,8
LeaveLoopCmps:

        test    eax, 0000FFFFh
        jnz LenLower

        test eax,0ffffffffh

        jnz LenLower32

        add rdx,4
        shr rax,32
        or ax,ax
        jnz LenLower

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
        jge LenMaximum

;;; 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]
        jmp LookupLoop

;;;         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]
        jge LeaveNow

        lea rsi,[r10+rax]

        movzx   ebx, word ptr [r9 + rax - 1]
        mov rdi, prev_ad
        mov edx, [chainlenwmask]
        jmp LookupLoop

;;; 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:
IFDEF INFOZIP
        mov eax,r11d
ELSE
        mov eax, Lookahead
        cmp r11d, eax
        cmovng eax, r11d
ENDIF

;;; 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
longest_match   ENDP

match_init PROC
  ret 0
match_init ENDP


END

Deleted compat/zlib/contrib/masmx64/inffas8664.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


























































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
/* inffas8664.c is a hand tuned assembler version of inffast.c - fast decoding
 * version for AMD64 on Windows using Microsoft C compiler
 *
 * Copyright (C) 1995-2003 Mark Adler
 * For conditions of distribution and use, see copyright notice in zlib.h
 *
 * Copyright (C) 2003 Chris Anderson <christop@charm.net>
 * Please use the copyright conditions above.
 *
 * 2005 - Adaptation to Microsoft C Compiler for AMD64 by Gilles Vollant
 *
 * inffas8664.c call function inffas8664fnc in inffasx64.asm
 *  inffasx64.asm is automatically convert from AMD64 portion of inffas86.c
 *
 * Dec-29-2003 -- I added AMD64 inflate asm support.  This version is also
 * slightly quicker on x86 systems because, instead of using rep movsb to copy
 * data, it uses rep movsw, which moves data in 2-byte chunks instead of single
 * bytes.  I've tested the AMD64 code on a Fedora Core 1 + the x86_64 updates
 * from http://fedora.linux.duke.edu/fc1_x86_64
 * which is running on an Athlon 64 3000+ / Gigabyte GA-K8VT800M system with
 * 1GB ram.  The 64-bit version is about 4% faster than the 32-bit version,
 * when decompressing mozilla-source-1.3.tar.gz.
 *
 * Mar-13-2003 -- Most of this is derived from inffast.S which is derived from
 * the gcc -S output of zlib-1.2.0/inffast.c.  Zlib-1.2.0 is in beta release at
 * the moment.  I have successfully compiled and tested this code with gcc2.96,
 * gcc3.2, icc5.0, msvc6.0.  It is very close to the speed of inffast.S
 * compiled with gcc -DNO_MMX, but inffast.S is still faster on the P3 with MMX
 * enabled.  I will attempt to merge the MMX code into this version.  Newer
 * versions of this and inffast.S can be found at
 * http://www.eetbeetee.com/zlib/ and http://www.charm.net/~christop/zlib/
 *
 */

#include <stdio.h>
#include "zutil.h"
#include "inftrees.h"
#include "inflate.h"
#include "inffast.h"

/* Mark Adler's comments from inffast.c: */

/*
   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.
 */



    typedef struct inffast_ar {
/* 64   32                               x86  x86_64 */
/* ar offset                              register */
/*  0    0 */ void *esp;                /* esp save */
/*  8    4 */ void *ebp;                /* ebp save */
/* 16    8 */ unsigned char FAR *in;    /* esi rsi  local strm->next_in */
/* 24   12 */ unsigned char FAR *last;  /*     r9   while in < last */
/* 32   16 */ unsigned char FAR *out;   /* edi rdi  local strm->next_out */
/* 40   20 */ unsigned char FAR *beg;   /*          inflate()'s init next_out */
/* 48   24 */ unsigned char FAR *end;   /*     r10  while out < end */
/* 56   28 */ unsigned char FAR *window;/*          size of window, wsize!=0 */
/* 64   32 */ code const FAR *lcode;    /* ebp rbp  local strm->lencode */
/* 72   36 */ code const FAR *dcode;    /*     r11  local strm->distcode */
/* 80   40 */ size_t /*unsigned long */hold;       /* edx rdx  local strm->hold */
/* 88   44 */ unsigned bits;            /* ebx rbx  local strm->bits */
/* 92   48 */ unsigned wsize;           /*          window size */
/* 96   52 */ unsigned write;           /*          window write index */
/*100   56 */ unsigned lmask;           /*     r12  mask for lcode */
/*104   60 */ unsigned dmask;           /*     r13  mask for dcode */
/*108   64 */ unsigned len;             /*     r14  match length */
/*112   68 */ unsigned dist;            /*     r15  match distance */
/*116   72 */ unsigned status;          /*          set when state chng*/
    } type_ar;
#ifdef ASMINF

void inflate_fast(strm, start)
z_streamp strm;
unsigned start;         /* inflate()'s starting value for strm->avail_out */
{
    struct inflate_state FAR *state;
    type_ar ar;
    void inffas8664fnc(struct inffast_ar * par);



#if (defined( __GNUC__ ) && defined( __amd64__ ) && ! defined( __i386 )) || (defined(_MSC_VER) && defined(_M_AMD64))
#define PAD_AVAIL_IN 6
#define PAD_AVAIL_OUT 258
#else
#define PAD_AVAIL_IN 5
#define PAD_AVAIL_OUT 257
#endif

    /* copy state to local variables */
    state = (struct inflate_state FAR *)strm->state;

    ar.in = strm->next_in;
    ar.last = ar.in + (strm->avail_in - PAD_AVAIL_IN);
    ar.out = strm->next_out;
    ar.beg = ar.out - (start - strm->avail_out);
    ar.end = ar.out + (strm->avail_out - PAD_AVAIL_OUT);
    ar.wsize = state->wsize;
    ar.write = state->wnext;
    ar.window = state->window;
    ar.hold = state->hold;
    ar.bits = state->bits;
    ar.lcode = state->lencode;
    ar.dcode = state->distcode;
    ar.lmask = (1U << state->lenbits) - 1;
    ar.dmask = (1U << state->distbits) - 1;

    /* decode literals and length/distances until end-of-block or not enough
       input data or output space */

    /* align in on 1/2 hold size boundary */
    while (((size_t)(void *)ar.in & (sizeof(ar.hold) / 2 - 1)) != 0) {
        ar.hold += (unsigned long)*ar.in++ << ar.bits;
        ar.bits += 8;
    }

    inffas8664fnc(&ar);

    if (ar.status > 1) {
        if (ar.status == 2)
            strm->msg = "invalid literal/length code";
        else if (ar.status == 3)
            strm->msg = "invalid distance code";
        else
            strm->msg = "invalid distance too far back";
        state->mode = BAD;
    }
    else if ( ar.status == 1 ) {
        state->mode = TYPE;
    }

    /* return unused bytes (on entry, bits < 8, so in won't go too far back) */
    ar.len = ar.bits >> 3;
    ar.in -= ar.len;
    ar.bits -= ar.len << 3;
    ar.hold &= (1U << ar.bits) - 1;

    /* update state and return */
    strm->next_in = ar.in;
    strm->next_out = ar.out;
    strm->avail_in = (unsigned)(ar.in < ar.last ?
                                PAD_AVAIL_IN + (ar.last - ar.in) :
                                PAD_AVAIL_IN - (ar.in - ar.last));
    strm->avail_out = (unsigned)(ar.out < ar.end ?
                                 PAD_AVAIL_OUT + (ar.end - ar.out) :
                                 PAD_AVAIL_OUT - (ar.out - ar.end));
    state->hold = (unsigned long)ar.hold;
    state->bits = ar.bits;
    return;
}

#endif

Deleted compat/zlib/contrib/masmx64/inffasx64.asm.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396












































































































































































































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
; inffasx64.asm is a hand tuned assembler version of inffast.c - fast decoding
; version for AMD64 on Windows using Microsoft C compiler
;
; inffasx64.asm is automatically convert from AMD64 portion of inffas86.c
; inffasx64.asm is called by inffas8664.c, which contain more info.


; to compile this file, I use option
;   ml64.exe /Flinffasx64 /c /Zi inffasx64.asm
;   with Microsoft Macro Assembler (x64) for AMD64
;

; This file compile with Microsoft Macro Assembler (x64) for AMD64
;
;   ml64.exe is given with Visual Studio 2005/2008/2010 and Windows WDK
;
;   (you can get Windows WDK with ml64 for AMD64 from
;      http://www.microsoft.com/whdc/Devtools/wdk/default.mspx for low price)
;


.code
inffas8664fnc PROC

; 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, r-9, r10, and r11, which are scratch.


	mov [rsp-8],rsi
	mov [rsp-16],rdi
	mov [rsp-24],r12
	mov [rsp-32],r13
	mov [rsp-40],r14
	mov [rsp-48],r15
	mov [rsp-56],rbx

	mov rax,rcx

	mov	[rax+8], rbp       ; /* save regs rbp and rsp */
	mov	[rax], rsp

	mov	rsp, rax          ; /* make rsp point to &ar */

	mov	rsi, [rsp+16]      ; /* rsi  = in */
	mov	rdi, [rsp+32]      ; /* rdi  = out */
	mov	r9, [rsp+24]       ; /* r9   = last */
	mov	r10, [rsp+48]      ; /* r10  = end */
	mov	rbp, [rsp+64]      ; /* rbp  = lcode */
	mov	r11, [rsp+72]      ; /* r11  = dcode */
	mov	rdx, [rsp+80]      ; /* rdx  = hold */
	mov	ebx, [rsp+88]      ; /* ebx  = bits */
	mov	r12d, [rsp+100]    ; /* r12d = lmask */
	mov	r13d, [rsp+104]    ; /* r13d = dmask */
                                          ; /* r14d = len */
                                          ; /* r15d = dist */


	cld
	cmp	r10, rdi
	je	L_one_time           ; /* if only one decode left */
	cmp	r9, rsi

    jne L_do_loop


L_one_time:
	mov	r8, r12           ; /* r8 = lmask */
	cmp	bl, 32
	ja	L_get_length_code_one_time

	lodsd                         ; /* eax = *(uint *)in++ */
	mov	cl, bl            ; /* cl = bits, needs it for shifting */
	add	bl, 32             ; /* bits += 32 */
	shl	rax, cl
	or	rdx, rax          ; /* hold |= *((uint *)in)++ << bits */
	jmp	L_get_length_code_one_time

ALIGN 4
L_while_test:
	cmp	r10, rdi
	jbe	L_break_loop
	cmp	r9, rsi
	jbe	L_break_loop

L_do_loop:
	mov	r8, r12           ; /* r8 = lmask */
	cmp	bl, 32
	ja	L_get_length_code    ; /* if (32 < bits) */

	lodsd                         ; /* eax = *(uint *)in++ */
	mov	cl, bl            ; /* cl = bits, needs it for shifting */
	add	bl, 32             ; /* bits += 32 */
	shl	rax, cl
	or	rdx, rax          ; /* hold |= *((uint *)in)++ << bits */

L_get_length_code:
	and	r8, rdx            ; /* r8 &= hold */
	mov	eax, [rbp+r8*4]  ; /* eax = lcode[hold & lmask] */

	mov	cl, ah            ; /* cl = this.bits */
	sub	bl, ah            ; /* bits -= this.bits */
	shr	rdx, cl           ; /* hold >>= this.bits */

	test	al, al
	jnz	L_test_for_length_base ; /* if (op != 0) 45.7% */

	mov	r8, r12            ; /* r8 = lmask */
	shr	eax, 16            ; /* output this.val char */
	stosb

L_get_length_code_one_time:
	and	r8, rdx            ; /* r8 &= hold */
	mov	eax, [rbp+r8*4] ; /* eax = lcode[hold & lmask] */

L_dolen:
	mov	cl, ah            ; /* cl = this.bits */
	sub	bl, ah            ; /* bits -= this.bits */
	shr	rdx, cl           ; /* hold >>= this.bits */

	test	al, al
	jnz	L_test_for_length_base ; /* if (op != 0) 45.7% */

	shr	eax, 16            ; /* output this.val char */
	stosb
	jmp	L_while_test

ALIGN 4
L_test_for_length_base:
	mov	r14d, eax         ; /* len = this */
	shr	r14d, 16           ; /* len = this.val */
	mov	cl, al

	test	al, 16
	jz	L_test_for_second_level_length ; /* if ((op & 16) == 0) 8% */
	and	cl, 15             ; /* op &= 15 */
	jz	L_decode_distance    ; /* if (!op) */

L_add_bits_to_len:
	sub	bl, cl
	xor	eax, eax
	inc	eax
	shl	eax, cl
	dec	eax
	and	eax, edx          ; /* eax &= hold */
	shr	rdx, cl
	add	r14d, eax         ; /* len += hold & mask[op] */

L_decode_distance:
	mov	r8, r13           ; /* r8 = dmask */
	cmp	bl, 32
	ja	L_get_distance_code  ; /* if (32 < bits) */

	lodsd                         ; /* eax = *(uint *)in++ */
	mov	cl, bl            ; /* cl = bits, needs it for shifting */
	add	bl, 32             ; /* bits += 32 */
	shl	rax, cl
	or	rdx, rax          ; /* hold |= *((uint *)in)++ << bits */

L_get_distance_code:
	and	r8, rdx           ; /* r8 &= hold */
	mov	eax, [r11+r8*4] ; /* eax = dcode[hold & dmask] */

L_dodist:
	mov	r15d, eax         ; /* dist = this */
	shr	r15d, 16           ; /* dist = this.val */
	mov	cl, ah
	sub	bl, ah            ; /* bits -= this.bits */
	shr	rdx, cl           ; /* hold >>= this.bits */
	mov	cl, al            ; /* cl = this.op */

	test	al, 16             ; /* if ((op & 16) == 0) */
	jz	L_test_for_second_level_dist
	and	cl, 15             ; /* op &= 15 */
	jz	L_check_dist_one

L_add_bits_to_dist:
	sub	bl, cl
	xor	eax, eax
	inc	eax
	shl	eax, cl
	dec	eax                 ; /* (1 << op) - 1 */
	and	eax, edx          ; /* eax &= hold */
	shr	rdx, cl
	add	r15d, eax         ; /* dist += hold & ((1 << op) - 1) */

L_check_window:
	mov	r8, rsi           ; /* save in so from can use it's reg */
	mov	rax, rdi
	sub	rax, [rsp+40]      ; /* nbytes = out - beg */

	cmp	eax, r15d
	jb	L_clip_window        ; /* if (dist > nbytes) 4.2% */

	mov	ecx, r14d         ; /* ecx = len */
	mov	rsi, rdi
	sub	rsi, r15          ; /* from = out - dist */

	sar	ecx, 1
	jnc	L_copy_two           ; /* if len % 2 == 0 */

	rep     movsw
	mov	al, [rsi]
	mov	[rdi], al
	inc	rdi

	mov	rsi, r8           ; /* move in back to %rsi, toss from */
	jmp	L_while_test

L_copy_two:
	rep     movsw
	mov	rsi, r8           ; /* move in back to %rsi, toss from */
	jmp	L_while_test

ALIGN 4
L_check_dist_one:
	cmp	r15d, 1            ; /* if dist 1, is a memset */
	jne	L_check_window
	cmp	[rsp+40], rdi      ; /* if out == beg, outside window */
	je	L_check_window

	mov	ecx, r14d         ; /* ecx = len */
	mov	al, [rdi-1]
	mov	ah, al

	sar	ecx, 1
	jnc	L_set_two
	mov	[rdi], al
	inc	rdi

L_set_two:
	rep     stosw
	jmp	L_while_test

ALIGN 4
L_test_for_second_level_length:
	test	al, 64
	jnz	L_test_for_end_of_block ; /* if ((op & 64) != 0) */

	xor	eax, eax
	inc	eax
	shl	eax, cl
	dec	eax
	and	eax, edx         ; /* eax &= hold */
	add	eax, r14d        ; /* eax += len */
	mov	eax, [rbp+rax*4] ; /* eax = lcode[val+(hold&mask[op])]*/
	jmp	L_dolen

ALIGN 4
L_test_for_second_level_dist:
	test	al, 64
	jnz	L_invalid_distance_code ; /* if ((op & 64) != 0) */

	xor	eax, eax
	inc	eax
	shl	eax, cl
	dec	eax
	and	eax, edx         ; /* eax &= hold */
	add	eax, r15d        ; /* eax += dist */
	mov	eax, [r11+rax*4] ; /* eax = dcode[val+(hold&mask[op])]*/
	jmp	L_dodist

ALIGN 4
L_clip_window:
	mov	ecx, eax         ; /* ecx = nbytes */
	mov	eax, [rsp+92]     ; /* eax = wsize, prepare for dist cmp */
	neg	ecx                ; /* nbytes = -nbytes */

	cmp	eax, r15d
	jb	L_invalid_distance_too_far ; /* if (dist > wsize) */

	add	ecx, r15d         ; /* nbytes = dist - nbytes */
	cmp	dword ptr [rsp+96], 0
	jne	L_wrap_around_window ; /* if (write != 0) */

	mov	rsi, [rsp+56]     ; /* from  = window */
	sub	eax, ecx         ; /* eax  -= nbytes */
	add	rsi, rax         ; /* from += wsize - nbytes */

	mov	eax, r14d        ; /* eax = len */
	cmp	r14d, ecx
	jbe	L_do_copy           ; /* if (nbytes >= len) */

	sub	eax, ecx         ; /* eax -= nbytes */
	rep     movsb
	mov	rsi, rdi
	sub	rsi, r15         ; /* from = &out[ -dist ] */
	jmp	L_do_copy

ALIGN 4
L_wrap_around_window:
	mov	eax, [rsp+96]     ; /* eax = write */
	cmp	ecx, eax
	jbe	L_contiguous_in_window ; /* if (write >= nbytes) */

	mov	esi, [rsp+92]     ; /* from  = wsize */
	add	rsi, [rsp+56]     ; /* from += window */
	add	rsi, rax         ; /* from += write */
	sub	rsi, rcx         ; /* from -= nbytes */
	sub	ecx, eax         ; /* nbytes -= write */

	mov	eax, r14d        ; /* eax = len */
	cmp	eax, ecx
	jbe	L_do_copy           ; /* if (nbytes >= len) */

	sub	eax, ecx         ; /* len -= nbytes */
	rep     movsb
	mov	rsi, [rsp+56]     ; /* from = window */
	mov	ecx, [rsp+96]     ; /* nbytes = write */
	cmp	eax, ecx
	jbe	L_do_copy           ; /* if (nbytes >= len) */

	sub	eax, ecx         ; /* len -= nbytes */
	rep     movsb
	mov	rsi, rdi
	sub	rsi, r15         ; /* from = out - dist */
	jmp	L_do_copy

ALIGN 4
L_contiguous_in_window:
	mov	rsi, [rsp+56]     ; /* rsi = window */
	add	rsi, rax
	sub	rsi, rcx         ; /* from += write - nbytes */

	mov	eax, r14d        ; /* eax = len */
	cmp	eax, ecx
	jbe	L_do_copy           ; /* if (nbytes >= len) */

	sub	eax, ecx         ; /* len -= nbytes */
	rep     movsb
	mov	rsi, rdi
	sub	rsi, r15         ; /* from = out - dist */
	jmp	L_do_copy           ; /* if (nbytes >= len) */

ALIGN 4
L_do_copy:
	mov	ecx, eax         ; /* ecx = len */
	rep     movsb

	mov	rsi, r8          ; /* move in back to %esi, toss from */
	jmp	L_while_test

L_test_for_end_of_block:
	test	al, 32
	jz	L_invalid_literal_length_code
	mov	dword ptr [rsp+116], 1
	jmp	L_break_loop_with_status

L_invalid_literal_length_code:
	mov	dword ptr [rsp+116], 2
	jmp	L_break_loop_with_status

L_invalid_distance_code:
	mov	dword ptr [rsp+116], 3
	jmp	L_break_loop_with_status

L_invalid_distance_too_far:
	mov	dword ptr [rsp+116], 4
	jmp	L_break_loop_with_status

L_break_loop:
	mov	dword ptr [rsp+116], 0

L_break_loop_with_status:
; /* put in, out, bits, and hold back into ar and pop esp */
	mov	[rsp+16], rsi     ; /* in */
	mov	[rsp+32], rdi     ; /* out */
	mov	[rsp+88], ebx     ; /* bits */
	mov	[rsp+80], rdx     ; /* hold */

	mov	rax, [rsp]       ; /* restore rbp and rsp */
	mov	rbp, [rsp+8]
	mov	rsp, rax



	mov rsi,[rsp-8]
	mov rdi,[rsp-16]
	mov r12,[rsp-24]
	mov r13,[rsp-32]
	mov r14,[rsp-40]
	mov r15,[rsp-48]
	mov rbx,[rsp-56]

    ret 0
;          :
;          : "m" (ar)
;          : "memory", "%rax", "%rbx", "%rcx", "%rdx", "%rsi", "%rdi",
;            "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15"
;    );

inffas8664fnc 	ENDP
;_TEXT	ENDS
END

Deleted compat/zlib/contrib/masmx64/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































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Summary
-------
This directory contains ASM implementations of the functions
longest_match() and inflate_fast(), for 64 bits x86 (both AMD64 and Intel EM64t),
for use with Microsoft Macro Assembler (x64) for AMD64 and Microsoft C++ 64 bits.

gvmat64.asm is written by Gilles Vollant (2005), by using Brian Raiter 686/32 bits
   assembly optimized version from Jean-loup Gailly original longest_match function

inffasx64.asm and inffas8664.c were written by Chris Anderson, by optimizing
   original function from Mark Adler

Use instructions
----------------
Assemble the .asm files using MASM and put the object files into the zlib source
directory.  You can also get object files here:

     http://www.winimage.com/zLibDll/zlib124_masm_obj.zip

define ASMV and ASMINF in your project. Include inffas8664.c in your source tree,
and inffasx64.obj and gvmat64.obj as object to link.


Build instructions
------------------
run bld_64.bat with Microsoft Macro Assembler (x64) for AMD64 (ml64.exe)

ml64.exe is given with Visual Studio 2005, Windows 2003 server DDK

You can get Windows 2003 server DDK with ml64 and cl for AMD64 from
  http://www.microsoft.com/whdc/devtools/ddk/default.mspx for low price)

Deleted compat/zlib/contrib/masmx86/bld_ml32.bat.

1
2


-
-
ml /coff /Zi /c /Flmatch686.lst match686.asm
ml /coff /Zi /c /Flinffas32.lst inffas32.asm

Deleted compat/zlib/contrib/masmx86/inffas32.asm.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
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
























































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
;/* inffas32.asm is a hand tuned assembler version of inffast.c -- fast decoding
; *
; * inffas32.asm is derivated from inffas86.c, with translation of assembly code
; *
; * Copyright (C) 1995-2003 Mark Adler
; * For conditions of distribution and use, see copyright notice in zlib.h
; *
; * Copyright (C) 2003 Chris Anderson <christop@charm.net>
; * Please use the copyright conditions above.
; *
; * Mar-13-2003 -- Most of this is derived from inffast.S which is derived from
; * the gcc -S output of zlib-1.2.0/inffast.c.  Zlib-1.2.0 is in beta release at
; * the moment.  I have successfully compiled and tested this code with gcc2.96,
; * gcc3.2, icc5.0, msvc6.0.  It is very close to the speed of inffast.S
; * compiled with gcc -DNO_MMX, but inffast.S is still faster on the P3 with MMX
; * enabled.  I will attempt to merge the MMX code into this version.  Newer
; * versions of this and inffast.S can be found at
; * http://www.eetbeetee.com/zlib/ and http://www.charm.net/~christop/zlib/
; *
; * 2005 : modification by Gilles Vollant
; */
; For Visual C++ 4.x and higher and ML 6.x and higher
;   ml.exe is in directory \MASM611C of Win95 DDK
;   ml.exe is also distributed in http://www.masm32.com/masmdl.htm
;    and in VC++2003 toolkit at http://msdn.microsoft.com/visualc/vctoolkit2003/
;
;
;   compile with command line option
;   ml  /coff /Zi /c /Flinffas32.lst inffas32.asm

;   if you define NO_GZIP (see inflate.h), compile with
;   ml  /coff /Zi /c /Flinffas32.lst /DNO_GUNZIP inffas32.asm


; zlib122sup is 0 fort zlib 1.2.2.1 and lower
; zlib122sup is 8 fort zlib 1.2.2.2 and more (with addition of dmax and head
;        in inflate_state in inflate.h)
zlib1222sup      equ    8


IFDEF GUNZIP
  INFLATE_MODE_TYPE    equ 11
  INFLATE_MODE_BAD     equ 26
ELSE
  IFNDEF NO_GUNZIP
    INFLATE_MODE_TYPE    equ 11
    INFLATE_MODE_BAD     equ 26
  ELSE
    INFLATE_MODE_TYPE    equ 3
    INFLATE_MODE_BAD     equ 17
  ENDIF
ENDIF


; 75 "inffast.S"
;FILE "inffast.S"

;;;GLOBAL _inflate_fast

;;;SECTION .text



	.586p
	.mmx

	name	inflate_fast_x86
	.MODEL	FLAT

_DATA			segment
inflate_fast_use_mmx:
	dd	1


_TEXT			segment



ALIGN 4
	db	'Fast decoding Code from Chris Anderson'
	db	0

ALIGN 4
invalid_literal_length_code_msg:
	db	'invalid literal/length code'
	db	0

ALIGN 4
invalid_distance_code_msg:
	db	'invalid distance code'
	db	0

ALIGN 4
invalid_distance_too_far_msg:
	db	'invalid distance too far back'
	db	0


ALIGN 4
inflate_fast_mask:
dd	0
dd	1
dd	3
dd	7
dd	15
dd	31
dd	63
dd	127
dd	255
dd	511
dd	1023
dd	2047
dd	4095
dd	8191
dd	16383
dd	32767
dd	65535
dd	131071
dd	262143
dd	524287
dd	1048575
dd	2097151
dd	4194303
dd	8388607
dd	16777215
dd	33554431
dd	67108863
dd	134217727
dd	268435455
dd	536870911
dd	1073741823
dd	2147483647
dd	4294967295


mode_state	 equ	0	;/* state->mode	*/
wsize_state	 equ	(32+zlib1222sup)	;/* state->wsize */
write_state	 equ	(36+4+zlib1222sup)	;/* state->write */
window_state	 equ	(40+4+zlib1222sup)	;/* state->window */
hold_state	 equ	(44+4+zlib1222sup)	;/* state->hold	*/
bits_state	 equ	(48+4+zlib1222sup)	;/* state->bits	*/
lencode_state	 equ	(64+4+zlib1222sup)	;/* state->lencode */
distcode_state	 equ	(68+4+zlib1222sup)	;/* state->distcode */
lenbits_state	 equ	(72+4+zlib1222sup)	;/* state->lenbits */
distbits_state	 equ	(76+4+zlib1222sup)	;/* state->distbits */


;;SECTION .text
; 205 "inffast.S"
;GLOBAL	inflate_fast_use_mmx

;SECTION .data


; GLOBAL inflate_fast_use_mmx:object
;.size inflate_fast_use_mmx, 4
; 226 "inffast.S"
;SECTION .text

ALIGN 4
_inflate_fast proc near
.FPO (16, 4, 0, 0, 1, 0)
	push  edi
	push  esi
	push  ebp
	push  ebx
	pushfd
	sub  esp,64
	cld




	mov  esi, [esp+88]
	mov  edi, [esi+28]







	mov  edx, [esi+4]
	mov  eax, [esi+0]

	add  edx,eax
	sub  edx,11

	mov  [esp+44],eax
	mov  [esp+20],edx

	mov  ebp, [esp+92]
	mov  ecx, [esi+16]
	mov  ebx, [esi+12]

	sub  ebp,ecx
	neg  ebp
	add  ebp,ebx

	sub  ecx,257
	add  ecx,ebx

	mov  [esp+60],ebx
	mov  [esp+40],ebp
	mov  [esp+16],ecx
; 285 "inffast.S"
	mov  eax, [edi+lencode_state]
	mov  ecx, [edi+distcode_state]

	mov  [esp+8],eax
	mov  [esp+12],ecx

	mov  eax,1
	mov  ecx, [edi+lenbits_state]
	shl  eax,cl
	dec  eax
	mov  [esp+0],eax

	mov  eax,1
	mov  ecx, [edi+distbits_state]
	shl  eax,cl
	dec  eax
	mov  [esp+4],eax

	mov  eax, [edi+wsize_state]
	mov  ecx, [edi+write_state]
	mov  edx, [edi+window_state]

	mov  [esp+52],eax
	mov  [esp+48],ecx
	mov  [esp+56],edx

	mov  ebp, [edi+hold_state]
	mov  ebx, [edi+bits_state]
; 321 "inffast.S"
	mov  esi, [esp+44]
	mov  ecx, [esp+20]
	cmp  ecx,esi
	ja   L_align_long

	add  ecx,11
	sub  ecx,esi
	mov  eax,12
	sub  eax,ecx
	lea  edi, [esp+28]
	rep movsb
	mov  ecx,eax
	xor  eax,eax
	rep stosb
	lea  esi, [esp+28]
	mov  [esp+20],esi
	jmp  L_is_aligned


L_align_long:
	test  esi,3
	jz   L_is_aligned
	xor  eax,eax
	mov  al, [esi]
	inc  esi
	mov  ecx,ebx
	add  ebx,8
	shl  eax,cl
	or  ebp,eax
	jmp L_align_long

L_is_aligned:
	mov  edi, [esp+60]
; 366 "inffast.S"
L_check_mmx:
	cmp  dword ptr [inflate_fast_use_mmx],2
	je   L_init_mmx
	ja   L_do_loop

	push  eax
	push  ebx
	push  ecx
	push  edx
	pushfd
	mov  eax, [esp]
	xor  dword ptr [esp],0200000h




	popfd
	pushfd
	pop  edx
	xor  edx,eax
	jz   L_dont_use_mmx
	xor  eax,eax
	cpuid
	cmp  ebx,0756e6547h
	jne  L_dont_use_mmx
	cmp  ecx,06c65746eh
	jne  L_dont_use_mmx
	cmp  edx,049656e69h
	jne  L_dont_use_mmx
	mov  eax,1
	cpuid
	shr  eax,8
	and  eax,15
	cmp  eax,6
	jne  L_dont_use_mmx
	test  edx,0800000h
	jnz  L_use_mmx
	jmp  L_dont_use_mmx
L_use_mmx:
	mov  dword ptr [inflate_fast_use_mmx],2
	jmp  L_check_mmx_pop
L_dont_use_mmx:
	mov  dword ptr [inflate_fast_use_mmx],3
L_check_mmx_pop:
	pop  edx
	pop  ecx
	pop  ebx
	pop  eax
	jmp  L_check_mmx
; 426 "inffast.S"
ALIGN 4
L_do_loop:
; 437 "inffast.S"
	cmp  bl,15
	ja   L_get_length_code

	xor  eax,eax
	lodsw
	mov  cl,bl
	add  bl,16
	shl  eax,cl
	or  ebp,eax

L_get_length_code:
	mov  edx, [esp+0]
	mov  ecx, [esp+8]
	and  edx,ebp
	mov  eax, [ecx+edx*4]

L_dolen:






	mov  cl,ah
	sub  bl,ah
	shr  ebp,cl






	test  al,al
	jnz   L_test_for_length_base

	shr  eax,16
	stosb

L_while_test:


	cmp  [esp+16],edi
	jbe  L_break_loop

	cmp  [esp+20],esi
	ja   L_do_loop
	jmp  L_break_loop

L_test_for_length_base:
; 502 "inffast.S"
	mov  edx,eax
	shr  edx,16
	mov  cl,al

	test  al,16
	jz   L_test_for_second_level_length
	and  cl,15
	jz   L_save_len
	cmp  bl,cl
	jae  L_add_bits_to_len

	mov  ch,cl
	xor  eax,eax
	lodsw
	mov  cl,bl
	add  bl,16
	shl  eax,cl
	or  ebp,eax
	mov  cl,ch

L_add_bits_to_len:
	mov  eax,1
	shl  eax,cl
	dec  eax
	sub  bl,cl
	and  eax,ebp
	shr  ebp,cl
	add  edx,eax

L_save_len:
	mov  [esp+24],edx


L_decode_distance:
; 549 "inffast.S"
	cmp  bl,15
	ja   L_get_distance_code

	xor  eax,eax
	lodsw
	mov  cl,bl
	add  bl,16
	shl  eax,cl
	or  ebp,eax

L_get_distance_code:
	mov  edx, [esp+4]
	mov  ecx, [esp+12]
	and  edx,ebp
	mov  eax, [ecx+edx*4]


L_dodist:
	mov  edx,eax
	shr  edx,16
	mov  cl,ah
	sub  bl,ah
	shr  ebp,cl
; 584 "inffast.S"
	mov  cl,al

	test  al,16
	jz  L_test_for_second_level_dist
	and  cl,15
	jz  L_check_dist_one
	cmp  bl,cl
	jae  L_add_bits_to_dist

	mov  ch,cl
	xor  eax,eax
	lodsw
	mov  cl,bl
	add  bl,16
	shl  eax,cl
	or  ebp,eax
	mov  cl,ch

L_add_bits_to_dist:
	mov  eax,1
	shl  eax,cl
	dec  eax
	sub  bl,cl
	and  eax,ebp
	shr  ebp,cl
	add  edx,eax
	jmp  L_check_window

L_check_window:
; 625 "inffast.S"
	mov  [esp+44],esi
	mov  eax,edi
	sub  eax, [esp+40]

	cmp  eax,edx
	jb   L_clip_window

	mov  ecx, [esp+24]
	mov  esi,edi
	sub  esi,edx

	sub  ecx,3
	mov  al, [esi]
	mov  [edi],al
	mov  al, [esi+1]
	mov  dl, [esi+2]
	add  esi,3
	mov  [edi+1],al
	mov  [edi+2],dl
	add  edi,3
	rep movsb

	mov  esi, [esp+44]
	jmp  L_while_test

ALIGN 4
L_check_dist_one:
	cmp  edx,1
	jne  L_check_window
	cmp  [esp+40],edi
	je  L_check_window

	dec  edi
	mov  ecx, [esp+24]
	mov  al, [edi]
	sub  ecx,3

	mov  [edi+1],al
	mov  [edi+2],al
	mov  [edi+3],al
	add  edi,4
	rep stosb

	jmp  L_while_test

ALIGN 4
L_test_for_second_level_length:




	test  al,64
	jnz   L_test_for_end_of_block

	mov  eax,1
	shl  eax,cl
	dec  eax
	and  eax,ebp
	add  eax,edx
	mov  edx, [esp+8]
	mov  eax, [edx+eax*4]
	jmp  L_dolen

ALIGN 4
L_test_for_second_level_dist:




	test  al,64
	jnz   L_invalid_distance_code

	mov  eax,1
	shl  eax,cl
	dec  eax
	and  eax,ebp
	add  eax,edx
	mov  edx, [esp+12]
	mov  eax, [edx+eax*4]
	jmp  L_dodist

ALIGN 4
L_clip_window:
; 721 "inffast.S"
	mov  ecx,eax
	mov  eax, [esp+52]
	neg  ecx
	mov  esi, [esp+56]

	cmp  eax,edx
	jb   L_invalid_distance_too_far

	add  ecx,edx
	cmp  dword ptr [esp+48],0
	jne  L_wrap_around_window

	sub  eax,ecx
	add  esi,eax
; 749 "inffast.S"
	mov  eax, [esp+24]
	cmp  eax,ecx
	jbe  L_do_copy1

	sub  eax,ecx
	rep movsb
	mov  esi,edi
	sub  esi,edx
	jmp  L_do_copy1

	cmp  eax,ecx
	jbe  L_do_copy1

	sub  eax,ecx
	rep movsb
	mov  esi,edi
	sub  esi,edx
	jmp  L_do_copy1

L_wrap_around_window:
; 793 "inffast.S"
	mov  eax, [esp+48]
	cmp  ecx,eax
	jbe  L_contiguous_in_window

	add  esi, [esp+52]
	add  esi,eax
	sub  esi,ecx
	sub  ecx,eax


	mov  eax, [esp+24]
	cmp  eax,ecx
	jbe  L_do_copy1

	sub  eax,ecx
	rep movsb
	mov  esi, [esp+56]
	mov  ecx, [esp+48]
	cmp  eax,ecx
	jbe  L_do_copy1

	sub  eax,ecx
	rep movsb
	mov  esi,edi
	sub  esi,edx
	jmp  L_do_copy1

L_contiguous_in_window:
; 836 "inffast.S"
	add  esi,eax
	sub  esi,ecx


	mov  eax, [esp+24]
	cmp  eax,ecx
	jbe  L_do_copy1

	sub  eax,ecx
	rep movsb
	mov  esi,edi
	sub  esi,edx

L_do_copy1:
; 862 "inffast.S"
	mov  ecx,eax
	rep movsb

	mov  esi, [esp+44]
	jmp  L_while_test
; 878 "inffast.S"
ALIGN 4
L_init_mmx:
	emms





	movd mm0,ebp
	mov  ebp,ebx
; 896 "inffast.S"
	movd mm4,dword ptr [esp+0]
	movq mm3,mm4
	movd mm5,dword ptr [esp+4]
	movq mm2,mm5
	pxor mm1,mm1
	mov  ebx, [esp+8]
	jmp  L_do_loop_mmx

ALIGN 4
L_do_loop_mmx:
	psrlq mm0,mm1

	cmp  ebp,32
	ja  L_get_length_code_mmx

	movd mm6,ebp
	movd mm7,dword ptr [esi]
	add  esi,4
	psllq mm7,mm6
	add  ebp,32
	por mm0,mm7

L_get_length_code_mmx:
	pand mm4,mm0
	movd eax,mm4
	movq mm4,mm3
	mov  eax, [ebx+eax*4]

L_dolen_mmx:
	movzx  ecx,ah
	movd mm1,ecx
	sub  ebp,ecx

	test  al,al
	jnz L_test_for_length_base_mmx

	shr  eax,16
	stosb

L_while_test_mmx:


	cmp  [esp+16],edi
	jbe L_break_loop

	cmp  [esp+20],esi
	ja L_do_loop_mmx
	jmp L_break_loop

L_test_for_length_base_mmx:

	mov  edx,eax
	shr  edx,16

	test  al,16
	jz  L_test_for_second_level_length_mmx
	and  eax,15
	jz L_decode_distance_mmx

	psrlq mm0,mm1
	movd mm1,eax
	movd ecx,mm0
	sub  ebp,eax
	and  ecx, [inflate_fast_mask+eax*4]
	add  edx,ecx

L_decode_distance_mmx:
	psrlq mm0,mm1

	cmp  ebp,32
	ja L_get_dist_code_mmx

	movd mm6,ebp
	movd mm7,dword ptr [esi]
	add  esi,4
	psllq mm7,mm6
	add  ebp,32
	por mm0,mm7

L_get_dist_code_mmx:
	mov  ebx, [esp+12]
	pand mm5,mm0
	movd eax,mm5
	movq mm5,mm2
	mov  eax, [ebx+eax*4]

L_dodist_mmx:

	movzx  ecx,ah
	mov  ebx,eax
	shr  ebx,16
	sub  ebp,ecx
	movd mm1,ecx

	test  al,16
	jz L_test_for_second_level_dist_mmx
	and  eax,15
	jz L_check_dist_one_mmx

L_add_bits_to_dist_mmx:
	psrlq mm0,mm1
	movd mm1,eax
	movd ecx,mm0
	sub  ebp,eax
	and  ecx, [inflate_fast_mask+eax*4]
	add  ebx,ecx

L_check_window_mmx:
	mov  [esp+44],esi
	mov  eax,edi
	sub  eax, [esp+40]

	cmp  eax,ebx
	jb L_clip_window_mmx

	mov  ecx,edx
	mov  esi,edi
	sub  esi,ebx

	sub  ecx,3
	mov  al, [esi]
	mov  [edi],al
	mov  al, [esi+1]
	mov  dl, [esi+2]
	add  esi,3
	mov  [edi+1],al
	mov  [edi+2],dl
	add  edi,3
	rep movsb

	mov  esi, [esp+44]
	mov  ebx, [esp+8]
	jmp  L_while_test_mmx

ALIGN 4
L_check_dist_one_mmx:
	cmp  ebx,1
	jne  L_check_window_mmx
	cmp  [esp+40],edi
	je   L_check_window_mmx

	dec  edi
	mov  ecx,edx
	mov  al, [edi]
	sub  ecx,3

	mov  [edi+1],al
	mov  [edi+2],al
	mov  [edi+3],al
	add  edi,4
	rep stosb

	mov  ebx, [esp+8]
	jmp  L_while_test_mmx

ALIGN 4
L_test_for_second_level_length_mmx:
	test  al,64
	jnz L_test_for_end_of_block

	and  eax,15
	psrlq mm0,mm1
	movd ecx,mm0
	and  ecx, [inflate_fast_mask+eax*4]
	add  ecx,edx
	mov  eax, [ebx+ecx*4]
	jmp L_dolen_mmx

ALIGN 4
L_test_for_second_level_dist_mmx:
	test  al,64
	jnz L_invalid_distance_code

	and  eax,15
	psrlq mm0,mm1
	movd ecx,mm0
	and  ecx, [inflate_fast_mask+eax*4]
	mov  eax, [esp+12]
	add  ecx,ebx
	mov  eax, [eax+ecx*4]
	jmp  L_dodist_mmx

ALIGN 4
L_clip_window_mmx:

	mov  ecx,eax
	mov  eax, [esp+52]
	neg  ecx
	mov  esi, [esp+56]

	cmp  eax,ebx
	jb  L_invalid_distance_too_far

	add  ecx,ebx
	cmp  dword ptr [esp+48],0
	jne  L_wrap_around_window_mmx

	sub  eax,ecx
	add  esi,eax

	cmp  edx,ecx
	jbe  L_do_copy1_mmx

	sub  edx,ecx
	rep movsb
	mov  esi,edi
	sub  esi,ebx
	jmp  L_do_copy1_mmx

	cmp  edx,ecx
	jbe  L_do_copy1_mmx

	sub  edx,ecx
	rep movsb
	mov  esi,edi
	sub  esi,ebx
	jmp  L_do_copy1_mmx

L_wrap_around_window_mmx:

	mov  eax, [esp+48]
	cmp  ecx,eax
	jbe  L_contiguous_in_window_mmx

	add  esi, [esp+52]
	add  esi,eax
	sub  esi,ecx
	sub  ecx,eax


	cmp  edx,ecx
	jbe  L_do_copy1_mmx

	sub  edx,ecx
	rep movsb
	mov  esi, [esp+56]
	mov  ecx, [esp+48]
	cmp  edx,ecx
	jbe  L_do_copy1_mmx

	sub  edx,ecx
	rep movsb
	mov  esi,edi
	sub  esi,ebx
	jmp  L_do_copy1_mmx

L_contiguous_in_window_mmx:

	add  esi,eax
	sub  esi,ecx


	cmp  edx,ecx
	jbe  L_do_copy1_mmx

	sub  edx,ecx
	rep movsb
	mov  esi,edi
	sub  esi,ebx

L_do_copy1_mmx:


	mov  ecx,edx
	rep movsb

	mov  esi, [esp+44]
	mov  ebx, [esp+8]
	jmp  L_while_test_mmx
; 1174 "inffast.S"
L_invalid_distance_code:





	mov  ecx, invalid_distance_code_msg
	mov  edx,INFLATE_MODE_BAD
	jmp  L_update_stream_state

L_test_for_end_of_block:





	test  al,32
	jz  L_invalid_literal_length_code

	mov  ecx,0
	mov  edx,INFLATE_MODE_TYPE
	jmp  L_update_stream_state

L_invalid_literal_length_code:





	mov  ecx, invalid_literal_length_code_msg
	mov  edx,INFLATE_MODE_BAD
	jmp  L_update_stream_state

L_invalid_distance_too_far:



	mov  esi, [esp+44]
	mov  ecx, invalid_distance_too_far_msg
	mov  edx,INFLATE_MODE_BAD
	jmp  L_update_stream_state

L_update_stream_state:

	mov  eax, [esp+88]
	test  ecx,ecx
	jz  L_skip_msg
	mov  [eax+24],ecx
L_skip_msg:
	mov  eax, [eax+28]
	mov  [eax+mode_state],edx
	jmp  L_break_loop

ALIGN 4
L_break_loop:
; 1243 "inffast.S"
	cmp  dword ptr [inflate_fast_use_mmx],2
	jne  L_update_next_in



	mov  ebx,ebp

L_update_next_in:
; 1266 "inffast.S"
	mov  eax, [esp+88]
	mov  ecx,ebx
	mov  edx, [eax+28]
	shr  ecx,3
	sub  esi,ecx
	shl  ecx,3
	sub  ebx,ecx
	mov  [eax+12],edi
	mov  [edx+bits_state],ebx
	mov  ecx,ebx

	lea  ebx, [esp+28]
	cmp  [esp+20],ebx
	jne  L_buf_not_used

	sub  esi,ebx
	mov  ebx, [eax+0]
	mov  [esp+20],ebx
	add  esi,ebx
	mov  ebx, [eax+4]
	sub  ebx,11
	add  [esp+20],ebx

L_buf_not_used:
	mov  [eax+0],esi

	mov  ebx,1
	shl  ebx,cl
	dec  ebx





	cmp  dword ptr [inflate_fast_use_mmx],2
	jne  L_update_hold



	psrlq mm0,mm1
	movd ebp,mm0

	emms

L_update_hold:



	and  ebp,ebx
	mov  [edx+hold_state],ebp




	mov  ebx, [esp+20]
	cmp  ebx,esi
	jbe  L_last_is_smaller

	sub  ebx,esi
	add  ebx,11
	mov  [eax+4],ebx
	jmp  L_fixup_out
L_last_is_smaller:
	sub  esi,ebx
	neg  esi
	add  esi,11
	mov  [eax+4],esi




L_fixup_out:

	mov  ebx, [esp+16]
	cmp  ebx,edi
	jbe  L_end_is_smaller

	sub  ebx,edi
	add  ebx,257
	mov  [eax+16],ebx
	jmp  L_done
L_end_is_smaller:
	sub  edi,ebx
	neg  edi
	add  edi,257
	mov  [eax+16],edi





L_done:
	add  esp,64
	popfd
	pop  ebx
	pop  ebp
	pop  esi
	pop  edi
	ret
_inflate_fast endp

_TEXT	ends
end

Deleted compat/zlib/contrib/masmx86/match686.asm.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
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































































































































































































































































































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
; match686.asm -- Asm portion of the optimized longest_match for 32 bits x86
; Copyright (C) 1995-1996 Jean-loup Gailly, Brian Raiter and Gilles Vollant.
; File written by Gilles Vollant, by converting match686.S from Brian Raiter
; for MASM. This is as assembly version of longest_match
;  from Jean-loup Gailly in deflate.c
;
;         http://www.zlib.net
;         http://www.winimage.com/zLibDll
;         http://www.muppetlabs.com/~breadbox/software/assembly.html
;
; For Visual C++ 4.x and higher and ML 6.x and higher
;   ml.exe is distributed in
;  http://www.microsoft.com/downloads/details.aspx?FamilyID=7a1c9da0-0510-44a2-b042-7ef370530c64
;
; this file contain two implementation of longest_match
;
;  this longest_match was written by Brian raiter (1998), optimized for Pentium Pro
;   (and the faster known version of match_init on modern Core 2 Duo and AMD Phenom)
;
;  for using an assembly version of longest_match, you need define ASMV in project
;
;    compile the asm file running
;           ml /coff /Zi /c /Flmatch686.lst match686.asm
;    and do not include match686.obj in your project
;
; note: contrib of zLib 1.2.3 and earlier contained both a deprecated version for
;  Pentium (prior Pentium Pro) and this version for Pentium Pro and modern processor
;  with autoselect (with cpu detection code)
;  if you want support the old pentium optimization, you can still use these version
;
; this file is not optimized for old pentium, but it compatible with all x86 32 bits
; processor (starting 80386)
;
;
; see below : zlib1222add must be adjuster if you use a zlib version < 1.2.2.2

;uInt longest_match(s, cur_match)
;    deflate_state *s;
;    IPos cur_match;                             /* current match */

    NbStack         equ     76
    cur_match       equ     dword ptr[esp+NbStack-0]
    str_s           equ     dword ptr[esp+NbStack-4]
; 5 dword on top (ret,ebp,esi,edi,ebx)
    adrret          equ     dword ptr[esp+NbStack-8]
    pushebp         equ     dword ptr[esp+NbStack-12]
    pushedi         equ     dword ptr[esp+NbStack-16]
    pushesi         equ     dword ptr[esp+NbStack-20]
    pushebx         equ     dword ptr[esp+NbStack-24]

    chain_length    equ     dword ptr [esp+NbStack-28]
    limit           equ     dword ptr [esp+NbStack-32]
    best_len        equ     dword ptr [esp+NbStack-36]
    window          equ     dword ptr [esp+NbStack-40]
    prev            equ     dword ptr [esp+NbStack-44]
    scan_start      equ      word ptr [esp+NbStack-48]
    wmask           equ     dword ptr [esp+NbStack-52]
    match_start_ptr equ     dword ptr [esp+NbStack-56]
    nice_match      equ     dword ptr [esp+NbStack-60]
    scan            equ     dword ptr [esp+NbStack-64]

    windowlen       equ     dword ptr [esp+NbStack-68]
    match_start     equ     dword ptr [esp+NbStack-72]
    strend          equ     dword ptr [esp+NbStack-76]
    NbStackAdd      equ     (NbStack-24)

    .386p

    name    gvmatch
    .MODEL  FLAT



;  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").

    zlib1222add         equ     8

;  Note : these value are good with a 8 bytes boundary pack structure
    dep_chain_length    equ     74h+zlib1222add
    dep_window          equ     30h+zlib1222add
    dep_strstart        equ     64h+zlib1222add
    dep_prev_length     equ     70h+zlib1222add
    dep_nice_match      equ     88h+zlib1222add
    dep_w_size          equ     24h+zlib1222add
    dep_prev            equ     38h+zlib1222add
    dep_w_mask          equ     2ch+zlib1222add
    dep_good_match      equ     84h+zlib1222add
    dep_match_start     equ     68h+zlib1222add
    dep_lookahead       equ     6ch+zlib1222add


_TEXT                   segment

IFDEF NOUNDERLINE
            public  longest_match
            public  match_init
ELSE
            public  _longest_match
            public  _match_init
ENDIF

    MAX_MATCH           equ     258
    MIN_MATCH           equ     3
    MIN_LOOKAHEAD       equ     (MAX_MATCH+MIN_MATCH+1)



MAX_MATCH       equ     258
MIN_MATCH       equ     3
MIN_LOOKAHEAD   equ     (MAX_MATCH + MIN_MATCH + 1)
MAX_MATCH_8_     equ     ((MAX_MATCH + 7) AND 0FFF0h)


;;; stack frame offsets

chainlenwmask   equ  esp + 0    ; high word: current chain len
                    ; low word: s->wmask
window      equ  esp + 4    ; local copy of s->window
windowbestlen   equ  esp + 8    ; s->window + bestlen
scanstart   equ  esp + 16   ; first two bytes of string
scanend     equ  esp + 12   ; last two bytes of string
scanalign   equ  esp + 20   ; dword-misalignment of string
nicematch   equ  esp + 24   ; a good enough match size
bestlen     equ  esp + 28   ; size of best match so far
scan        equ  esp + 32   ; ptr to string wanting match

LocalVarsSize   equ 36
;   saved ebx   byte esp + 36
;   saved edi   byte esp + 40
;   saved esi   byte esp + 44
;   saved ebp   byte esp + 48
;   return address  byte esp + 52
deflatestate    equ  esp + 56   ; the function arguments
curmatch    equ  esp + 60

;;; 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.)

dsWSize     equ 36+zlib1222add
dsWMask     equ 44+zlib1222add
dsWindow    equ 48+zlib1222add
dsPrev      equ 56+zlib1222add
dsMatchLen  equ 88+zlib1222add
dsPrevMatch equ 92+zlib1222add
dsStrStart  equ 100+zlib1222add
dsMatchStart    equ 104+zlib1222add
dsLookahead equ 108+zlib1222add
dsPrevLen   equ 112+zlib1222add
dsMaxChainLen   equ 116+zlib1222add
dsGoodMatch equ 132+zlib1222add
dsNiceMatch equ 136+zlib1222add


;;; match686.asm -- Pentium-Pro-optimized version of longest_match()
;;; Written for zlib 1.1.2
;;; Copyright (C) 1998 Brian Raiter <breadbox@muppetlabs.com>
;;; You can look at http://www.muppetlabs.com/~breadbox/software/assembly.html
;;;
;;
;;  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.
;;

;GLOBAL _longest_match, _match_init


;SECTION    .text

;;; uInt longest_match(deflate_state *deflatestate, IPos curmatch)

;_longest_match:
    IFDEF NOUNDERLINE
    longest_match       proc near
    ELSE
    _longest_match      proc near
    ENDIF
.FPO (9, 4, 0, 0, 1, 0)

;;; Save registers that the compiler may be using, and adjust esp to
;;; make room for our stack frame.

        push    ebp
        push    edi
        push    esi
        push    ebx
        sub esp, LocalVarsSize

;;; Retrieve the function arguments. ecx 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.

        mov edx, [deflatestate]
        mov ecx, [curmatch]

;;; uInt wmask = s->w_mask;
;;; unsigned chain_length = s->max_chain_length;
;;; if (s->prev_length >= s->good_match) {
;;;     chain_length >>= 2;
;;; }

        mov eax, [edx + dsPrevLen]
        mov ebx, [edx + dsGoodMatch]
        cmp eax, ebx
        mov eax, [edx + dsWMask]
        mov ebx, [edx + dsMaxChainLen]
        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
        mov [chainlenwmask], ebx

;;; if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead;

        mov eax, [edx + dsNiceMatch]
        mov ebx, [edx + dsLookahead]
        cmp ebx, eax
        jl  LookaheadLess
        mov ebx, eax
LookaheadLess:  mov [nicematch], ebx

;;; register Bytef *scan = s->window + s->strstart;

        mov esi, [edx + dsWindow]
        mov [window], esi
        mov ebp, [edx + dsStrStart]
        lea edi, [esi + ebp]
        mov [scan], edi

;;; Determine how many bytes the scan ptr is off from being
;;; dword-aligned.

        mov eax, edi
        neg eax
        and eax, 3
        mov [scanalign], eax

;;; IPos limit = s->strstart > (IPos)MAX_DIST(s) ?
;;;     s->strstart - (IPos)MAX_DIST(s) : NIL;

        mov eax, [edx + dsWSize]
        sub eax, MIN_LOOKAHEAD
        sub ebp, eax
        jg  LimitPositive
        xor ebp, ebp
LimitPositive:

;;; int best_len = s->prev_length;

        mov eax, [edx + dsPrevLen]
        mov [bestlen], eax

;;; Store the sum of s->window + best_len in esi locally, and in esi.

        add esi, eax
        mov [windowbestlen], esi

;;; register ush scan_start = *(ushf*)scan;
;;; register ush scan_end   = *(ushf*)(scan+best_len-1);
;;; Posf *prev = s->prev;

        movzx   ebx, word ptr [edi]
        mov [scanstart], ebx
        movzx   ebx, word ptr [edi + eax - 1]
        mov [scanend], ebx
        mov edi, [edx + dsPrev]

;;; Jump into the main loop.

        mov edx, [chainlenwmask]
        jmp short LoopEntry

align 4

;;; 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
;;; ecx = curmatch
;;; edx = chainlenwmask - i.e., ((chainlen << 16) | wmask)
;;; esi = windowbestlen - i.e., (window + bestlen)
;;; edi = prev
;;; ebp = limit

LookupLoop:
        and ecx, edx
        movzx   ecx, word ptr [edi + ecx*2]
        cmp ecx, ebp
        jbe LeaveNow
        sub edx, 00010000h
        js  LeaveNow
LoopEntry:  movzx   eax, word ptr [esi + ecx - 1]
        cmp eax, ebx
        jnz LookupLoop
        mov eax, [window]
        movzx   eax, word ptr [eax + ecx]
        cmp eax, [scanstart]
        jnz LookupLoop

;;; 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).

        mov esi, [window]
        mov edi, [scan]
        add esi, ecx
        mov eax, [scanalign]
        mov edx, 0fffffef8h; -(MAX_MATCH_8)
        lea edi, [edi + eax + 0108h] ;MAX_MATCH_8]
        lea esi, [esi + eax + 0108h] ;MAX_MATCH_8]

;;; Test the strings for equality, 8 bytes at a time. At the end,
;;; adjust edx 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. (esi 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 eax, [esi + edx]
        xor eax, [edi + edx]
        jnz LeaveLoopCmps
        mov eax, [esi + edx + 4]
        xor eax, [edi + edx + 4]
        jnz LeaveLoopCmps4
        add edx, 8
        jnz LoopCmps
        jmp short LenMaximum
LeaveLoopCmps4: add edx, 4
LeaveLoopCmps:  test    eax, 0000FFFFh
        jnz LenLower
        add edx,  2
        shr eax, 16
LenLower:   sub al, 1
        adc edx, 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 eax, [edi + edx]
        mov edi, [scan]
        sub eax, edi
        cmp eax, MAX_MATCH
        jge LenMaximum

;;; 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.

        mov edx, [deflatestate]
        mov ebx, [bestlen]
        cmp eax, ebx
        jg  LongerMatch
        mov esi, [windowbestlen]
        mov edi, [edx + dsPrev]
        mov ebx, [scanend]
        mov edx, [chainlenwmask]
        jmp LookupLoop

;;;         s->match_start = cur_match;
;;;         best_len = len;
;;;         if (len >= nice_match) break;
;;;         scan_end = *(ushf*)(scan+best_len-1);

LongerMatch:    mov ebx, [nicematch]
        mov [bestlen], eax
        mov [edx + dsMatchStart], ecx
        cmp eax, ebx
        jge LeaveNow
        mov esi, [window]
        add esi, eax
        mov [windowbestlen], esi
        movzx   ebx, word ptr [edi + eax - 1]
        mov edi, [edx + dsPrev]
        mov [scanend], ebx
        mov edx, [chainlenwmask]
        jmp LookupLoop

;;; Accept the current string, with the maximum possible length.

LenMaximum: mov edx, [deflatestate]
        mov dword ptr [bestlen], MAX_MATCH
        mov [edx + dsMatchStart], ecx

;;; if ((uInt)best_len <= s->lookahead) return (uInt)best_len;
;;; return s->lookahead;

LeaveNow:
        mov edx, [deflatestate]
        mov ebx, [bestlen]
        mov eax, [edx + dsLookahead]
        cmp ebx, eax
        jg  LookaheadRet
        mov eax, ebx
LookaheadRet:

;;; Restore the stack and return from whence we came.

        add esp, LocalVarsSize
        pop ebx
        pop esi
        pop edi
        pop ebp

        ret
; please don't remove this string !
; Your can freely use match686 in any free or commercial app if you don't remove the string in the binary!
    db     0dh,0ah,"asm686 with masm, optimised assembly code from Brian Raiter, written 1998",0dh,0ah


    IFDEF NOUNDERLINE
    longest_match       endp
    ELSE
    _longest_match      endp
    ENDIF

    IFDEF NOUNDERLINE
    match_init      proc near
                    ret
    match_init      endp
    ELSE
    _match_init     proc near
                    ret
    _match_init     endp
    ENDIF


_TEXT   ends
end

Deleted compat/zlib/contrib/masmx86/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



























-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

Summary
-------
This directory contains ASM implementations of the functions
longest_match() and inflate_fast().


Use instructions
----------------
Assemble using MASM, and copy the object files into the zlib source
directory, then run the appropriate makefile, as suggested below.  You can
donwload MASM from here:

    http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=7a1c9da0-0510-44a2-b042-7ef370530c64

You can also get objects files here:

    http://www.winimage.com/zLibDll/zlib124_masm_obj.zip

Build instructions
------------------
* With Microsoft C and MASM:
nmake -f win32/Makefile.msc LOC="-DASMV -DASMINF" OBJA="match686.obj inffas32.obj"

* With Borland C and TASM:
make -f win32/Makefile.bor LOCAL_ZLIB="-DASMV -DASMINF" OBJA="match686.obj inffas32.obj" OBJPA="+match686c.obj+match686.obj+inffas32.obj"

Changes to 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



1
2
3
4
5
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=-O -I../..
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 readme.txt
	./minizip test test.txt
	./miniunz -l test.zip
	mv readme.txt readme.old
	@mv test.txt test.old
	./miniunz test.zip
	@cmp test.txt test.old
	@rm -f test.*

clean:
	/bin/rm -f *.o *~ minizip miniunz
	/bin/rm -f *.o *~ minizip miniunz test.*

Changes to compat/zlib/contrib/minizip/MiniZip64_Changes.txt.

1
2

3
4
5
6
1

2
3
4
5
6

-
+





MiniZip 1.1 was derrived from MiniZip at version 1.01f
MiniZip 1.1 was derived from MiniZip at version 1.01f

Change in 1.0 (Okt 2009)
 - **TODO - Add history**

Changes to compat/zlib/contrib/minizip/configure.ac.

1
2
3
4

5
6
7
8
9
10
11
1
2
3

4
5
6
7
8
9
10
11



-
+







#                                               -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.

AC_INIT([minizip], [1.2.11], [bugzilla.redhat.com])
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])

Changes to compat/zlib/contrib/minizip/crypt.h.

28
29
30
31
32
33
34
35

36
37
38
39
40

41
42
43
44
45
46
47
48

49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65

66
67
68
69
70
71
72
73
74
75
76
77
78
79
80

81
82
83
84
85
86
87

88
89
90
91
92
93
94
95






96
97

98
99
100
101
102
103
104
28
29
30
31
32
33
34

35

36
37
38
39
40
41
42
43
44
45
46
47

48

49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

64

65
66
67
68
69
70
71
72
73
74
75
76
77

78
79
80
81
82
83
84

85
86
87






88
89
90
91
92
93


94
95
96
97
98
99
100
101







-
+
-




+







-
+
-















-
+
-













-
+






-
+


-
-
-
-
-
-
+
+
+
+
+
+
-
-
+







*/

#define 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)
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)
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)
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), t^(c))
    (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 */
#    define ZCR_SEED2 3141592654UL      /* use PI as default pattern */
#  endif

static int 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)
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) {
{
    int n;                       /* index in random header */
    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;

Changes to compat/zlib/contrib/minizip/ioapi.c.

10
11
12
13
14
15
16
17

18
19
20
21
22
23
24
25
26
27
28
29
30
31

32
33
34
35
36
37
38
39
40
41

42
43
44
45
46
47
48
49
50
51
52
53
54
55

56
57
58
59
60
61

62
63
64
65
66
67
68
69

70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95

96
97
98

99
100
101
102
103
104
105
106
107
108
109
110
111
112
113

114
115
116

117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132

133
134

135
136
137
138
139

140
141

142
143
144
145
146

147
148

149
150
151
152
153
154

155
156

157

158
159
160
161

162
163
164

165
166
167
168
169
170
171
172
173
174
175
176
177
178
179

180
181
182
183
184

185
186
187

188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203

204
205
206
207
208
209
210

211
212

213
214
215
216
217

218
219

220
221
222
223
224
225

226
227
228
229
230
231
232
233
234
235
236
237

238
239
240
241
242
243
244
245
246
247
10
11
12
13
14
15
16

17
18
19
20
21
22
23
24
25
26
27
28
29
30

31

32
33
34
35
36
37
38
39

40

41
42
43
44
45
46
47
48
49
50
51
52

53

54
55
56
57

58
59
60
61
62
63
64
65

66

67
68

69
70
71
72
73
74
75
76
77
78
79
80
81









82

83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99

100

101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118

119

120
121
122
123
124
125

126

127
128
129
130
131
132

133

134
135
136
137
138
139
140

141

142
143

144
145
146
147

148

149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165

166
167
168
169
170

171

172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189

190
191
192
193
194
195
196

197

198
199
200
201
202
203

204

205
206
207
208
209
210


211

212
213
214
215
216
217
218
219
220
221

222

223
224
225
226
227
228
229
230
231







-
+













-
+
-








-
+
-












-
+
-




-
+







-
+
-


-













-
-
-
-
-
-
-
-
-
+
-


+














-
+
-


+















-
+
-

+




-
+
-

+




-
+
-

+





-
+
-

+
-
+



-
+
-


+














-
+




-
+
-


+















-
+






-
+
-

+




-
+
-

+




-
-
+
-










-
+
-










*/

#if defined(_WIN32) && (!(defined(_CRT_SECURE_NO_WARNINGS)))
        #define _CRT_SECURE_NO_WARNINGS
#endif

#if defined(__APPLE__) || defined(IOAPI_NO_64)
#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)
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)
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)
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 = (*(pfilefunc->ztell32_file))(pfilefunc->zfile_func64.opaque,filestream);
        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)
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.zerror_file = p_filefunc32->zerror_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 OF((voidpf opaque, const char* filename, int mode));
static uLong   ZCALLBACK fread_file_func OF((voidpf opaque, voidpf stream, void* buf, uLong size));
static uLong   ZCALLBACK fwrite_file_func OF((voidpf opaque, voidpf stream, const void* buf,uLong size));
static ZPOS64_T ZCALLBACK ftell64_file_func OF((voidpf opaque, voidpf stream));
static long    ZCALLBACK fseek64_file_func OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin));
static int     ZCALLBACK fclose_file_func OF((voidpf opaque, voidpf stream));
static int     ZCALLBACK ferror_file_func OF((voidpf opaque, voidpf stream));

static voidpf ZCALLBACK fopen_file_func (voidpf opaque, const char* filename, int mode)
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)
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)
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)
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)
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)
static ZPOS64_T ZCALLBACK ftell64_file_func(voidpf opaque, voidpf stream) {
{
    ZPOS64_T ret;
    (void)opaque;
    ret = FTELLO_FUNC((FILE *)stream);
    ret = (ZPOS64_T)FTELLO_FUNC((FILE *)stream);
    return ret;
}

static long ZCALLBACK fseek_file_func (voidpf  opaque, voidpf stream, uLong offset, int origin)
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, offset, fseek_origin) != 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)
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, offset, fseek_origin) != 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)
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)
static int ZCALLBACK ferror_file_func(voidpf opaque, voidpf stream) {
{
    int ret;
    (void)opaque;
    ret = ferror((FILE *)stream);
    return ret;
}

void fill_fopen_filefunc (pzlib_filefunc_def)
  zlib_filefunc_def* pzlib_filefunc_def;
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)
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;
}

Changes to compat/zlib/contrib/minizip/ioapi.h.

46
47
48
49
50
51
52
53

54
55
56
57
58
59
60
46
47
48
49
50
51
52

53
54
55
56
57
58
59
60







-
+







#include "zlib.h"

#if defined(USE_FILE32API)
#define fopen64 fopen
#define ftello64 ftell
#define fseeko64 fseek
#else
#ifdef __FreeBSD__
#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)))
78
79
80
81
82
83
84
85

86
87
88
89
90
91
92
93
94

95
96
97
98
99
100
101
102
103
104
105




106
107
108
109
110
111
112
78
79
80
81
82
83
84

85
86
87
88
89
90
91
92
93

94

95
96
97
98
99
100
101
102
103

104
105
106
107
108
109
110
111
112
113
114







-
+








-
+
-









-
+
+
+
+







#endif
*/

#ifdef HAVE_MINIZIP64_CONF_H
#include "mz64conf.h"
#endif

/* a type choosen by DEFINE */
/* 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

/* Maximum unsigned 32-bit value used as placeholder for zip64 */

#define MAXU32 0xffffffff

#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)
128
129
130
131
132
133
134
135
136
137
138
139





140
141
142


143
144
145

146
147
148
149
150
151
152
153
154
155
156
157
158
159
160



161
162
163
164
165
166
167
168
169
170
171
172
173
174
175


176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196



197
198

199
200
201
202
203
204
205
206
207
208
130
131
132
133
134
135
136





137
138
139
140
141
142


143
144
145
146

147
148
149
150
151
152
153
154
155
156
157
158
159



160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175


176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195



196
197
198
199

200
201
202
203
204
205
206
207
208
209
210







-
-
-
-
-
+
+
+
+
+

-
-
+
+


-
+












-
-
-
+
+
+













-
-
+
+


















-
-
-
+
+
+

-
+










   #define ZCALLBACK
 #endif
#endif




typedef voidpf   (ZCALLBACK *open_file_func)      OF((voidpf opaque, const char* filename, int mode));
typedef uLong    (ZCALLBACK *read_file_func)      OF((voidpf opaque, voidpf stream, void* buf, uLong size));
typedef uLong    (ZCALLBACK *write_file_func)     OF((voidpf opaque, voidpf stream, const void* buf, uLong size));
typedef int      (ZCALLBACK *close_file_func)     OF((voidpf opaque, voidpf stream));
typedef int      (ZCALLBACK *testerror_file_func) OF((voidpf opaque, voidpf stream));
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)      OF((voidpf opaque, voidpf stream));
typedef long     (ZCALLBACK *seek_file_func)      OF((voidpf opaque, voidpf stream, uLong offset, int origin));
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 structure */
/* 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)    OF((voidpf opaque, voidpf stream));
typedef long     (ZCALLBACK *seek64_file_func)    OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin));
typedef voidpf   (ZCALLBACK *open64_file_func)    OF((voidpf opaque, const void* filename, int mode));
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 OF((zlib_filefunc64_def* pzlib_filefunc_def));
void fill_fopen_filefunc OF((zlib_filefunc_def* pzlib_filefunc_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 OF((const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode));
long    call_zseek64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin));
ZPOS64_T call_ztell64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf 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);
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

Changes to compat/zlib/contrib/minizip/iowin32.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
52
53
54
55

56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77

78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96

97
98
99
100
101
102
103
104
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40








41
42
43
44
45
46
47
48
49
50
51

52

53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72

73

74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90

91

92
93
94
95
96
97
98







+
+
+
+
+





-
-
-
-
-
-
-
-











-
+
-




















-
+
-

















-
+
-







#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

voidpf  ZCALLBACK win32_open_file_func  OF((voidpf opaque, const char* filename, int mode));
uLong   ZCALLBACK win32_read_file_func  OF((voidpf opaque, voidpf stream, void* buf, uLong size));
uLong   ZCALLBACK win32_write_file_func OF((voidpf opaque, voidpf stream, const void* buf, uLong size));
ZPOS64_T ZCALLBACK win32_tell64_file_func  OF((voidpf opaque, voidpf stream));
long    ZCALLBACK win32_seek64_file_func  OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin));
int     ZCALLBACK win32_close_file_func OF((voidpf opaque, voidpf stream));
int     ZCALLBACK win32_error_file_func OF((voidpf opaque, voidpf stream));

typedef struct
{
    HANDLE hf;
    int error;
} WIN32FILE_IOWIN;


static void win32_translate_open_mode(int mode,
                                      DWORD* lpdwDesiredAccess,
                                      DWORD* lpdwCreationDisposition,
                                      DWORD* lpdwShareMode,
                                      DWORD* lpdwFlagsAndAttributes)
                                      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)
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)
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
118
119
120
121
122
123
124
125

126
127
128
129
130
131
132
133
112
113
114
115
116
117
118

119

120
121
122
123
124
125
126







-
+
-







        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)
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
142
143
144
145
146
147
148
149

150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169

170
171
172
173
174
175
176
177
135
136
137
138
139
140
141

142

143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160

161

162
163
164
165
166
167
168







-
+
-


















-
+
-







        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)
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)
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
191
192
193
194
195
196
197
198

199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220

221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241

242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261

262
263
264
265
266
267
268
269
182
183
184
185
186
187
188

189

190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209

210

211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229

230

231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248

249

250
251
252
253
254
255
256







-
+
-




















-
+
-



















-
+
-


















-
+
-







        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 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 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)
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 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;
277
278
279
280
281
282
283
284

285
286
287
288
289
290
291
292
264
265
266
267
268
269
270

271

272
273
274
275
276
277
278







-
+
-







        }
        else
            ret=(long)pos.LowPart;
    }
    return ret;
}

ZPOS64_T ZCALLBACK win32_tell64_file_func (voidpf opaque, voidpf stream)
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)
    {
302
303
304
305
306
307
308
309

310
311
312
313
314
315
316
317
288
289
290
291
292
293
294

295

296
297
298
299
300
301
302







-
+
-







        else
            ret=pos.QuadPart;
    }
    return ret;
}


long ZCALLBACK win32_seek_file_func (voidpf opaque,voidpf stream,uLong offset,int origin)
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)
340
341
342
343
344
345
346
347

348
349
350
351
352
353
354
355
325
326
327
328
329
330
331

332

333
334
335
336
337
338
339







-
+
-







        }
        else
            ret=0;
    }
    return ret;
}

long ZCALLBACK win32_seek64_file_func (voidpf opaque, voidpf stream,ZPOS64_T offset,int origin)
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;

379
380
381
382
383
384
385
386

387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404

405
406
407
408
409
410
411
412
413
414

415
416
417
418
419
420
421
422
423
424
425
426

427
428
429
430
431
432
433
434
435
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
363
364
365
366
367
368
369

370

371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386

387

388
389
390
391
392
393
394
395

396

397
398
399
400
401
402
403
404
405
406

407

408
409
410
411
412
413
414
415
416
417
418

419

420
421
422
423
424
425
426
427
428
429
430

431

432
433
434
435
436
437
438
439
440







-
+
-
















-
+
-








-
+
-










-
+
-











-
+
-











-
+
-









        }
        else
            ret=0;
    }
    return ret;
}

int ZCALLBACK win32_close_file_func (voidpf opaque, voidpf stream)
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 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)
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)
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)
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)
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;
}

Changes to compat/zlib/contrib/minizip/iowin32.h.

14
15
16
17
18
19
20
21
22
23
24




25
26
27
28
14
15
16
17
18
19
20




21
22
23
24
25
26
27
28







-
-
-
-
+
+
+
+




#include <windows.h>


#ifdef __cplusplus
extern "C" {
#endif

void fill_win32_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def));
void fill_win32_filefunc64 OF((zlib_filefunc64_def* pzlib_filefunc_def));
void fill_win32_filefunc64A OF((zlib_filefunc64_def* pzlib_filefunc_def));
void fill_win32_filefunc64W OF((zlib_filefunc64_def* pzlib_filefunc_def));
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

Changes to compat/zlib/contrib/minizip/miniunz.c.

23
24
25
26
27
28
29
30

31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

48
49
50
51
52
53
54
23
24
25
26
27
28
29

30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55







-
+

















+







                #define _LARGEFILE64_SOURCE
        #endif
        #ifndef _FILE_OFFSET_BIT
                #define _FILE_OFFSET_BIT 64
        #endif
#endif

#ifdef __APPLE__
#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>
74
75
76
77
78
79
80
81

82
83
84

85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100


101
102
103
104
105
106
107
108
109
110
111
112
113
114
115




116
117
118
119
120
121
122
123
124
125

126
127
128
129
130
131
132
133


134
135
136
137
138
139

140
141
142
143

144
145

146
147
148
149
150
151
152
75
76
77
78
79
80
81

82
83


84



85
86
87
88
89
90
91
92
93
94
95
96

97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125


126

127
128
129
130
131
132
133
134
135
136
137
138
139


140

141
142

143
144

145
146
147
148
149
150
151
152







-
+

-
-
+
-
-
-












-
+
+















+
+
+
+








-
-
+
-







+
+




-
-
+
-


-
+

-
+







  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)
    dosdate : the new date at the MSDOS format (4 bytes)
    tmu_date : the SAME new date at the tm_unz format */
void change_file_date(filename,dosdate,tmu_date)
    const char *filename;
static void change_file_date(const char *filename, uLong dosdate, tm_unz tmu_date) {
    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
#ifdef unix || __APPLE__
#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 */

int mymkdir(dirname)
    const char* dirname;
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;
}

int makedir (newdir)
    char *newdir;
static int makedir(const char *newdir) {
{
  char *buffer ;
  char *p;
  int  len = (int)strlen(newdir);
  size_t len = strlen(newdir);

  if (len <= 0)
  if (len == 0)
    return 0;

  buffer = (char*)malloc(len+1);
        if (buffer==NULL)
        {
                printf("Error allocating memory\n");
                return UNZ_INTERNALERROR;
181
182
183
184
185
186
187
188

189
190

191
192
193
194

195
196
197
198
199
200
201
202
203

204
205
206

207
208
209
210
211
212
213
214
181
182
183
184
185
186
187

188


189
190
191
192

193

194
195
196
197
198
199
200

201
202
203

204

205
206
207
208
209
210
211







-
+
-
-
+



-
+
-







-
+


-
+
-







        break;
      *p++ = hold;
    }
  free(buffer);
  return 1;
}

void do_banner()
static void do_banner(void) {
{
    printf("MiniUnz 1.01b, demo of zLib + Unz package written by Gilles Vollant\n");
    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");
}

void do_help()
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 crypted file using password\n\n");
           "  -p  extract encrypted file using password\n\n");
}

void Display64BitsSize(ZPOS64_T n, int size_char)
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');
227
228
229
230
231
232
233
234

235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251

252
253
254
255
256
257
258
259
260
261
262

263
264
265
266
267
268
269
224
225
226
227
228
229
230

231


232
233
234
235
236
237
238
239
240
241
242
243
244
245

246
247
248
249
250
251
252
253
254
255
256

257
258
259
260
261
262
263
264







-
+
-
-














-
+










-
+







          printf(" ");
      }
  }

  printf("%s",&number[pos_string]);
}

int do_list(uf)
static int do_list(unzFile uf) {
    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;
        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 crypted */
        /* 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)
305
306
307
308
309
310
311
312
313
314

315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
300
301
302
303
304
305
306



307



308
309
310
311
312
313
314
315
316

317
318
319
320
321
322
323







-
-
-
+
-
-
-









-







        }
    }

    return 0;
}


int do_extract_currentfile(uf,popt_extract_without_path,popt_overwrite,password)
    unzFile uf;
    const int* popt_extract_without_path;
static int do_extract_currentfile(unzFile uf, const int* popt_extract_without_path, int* popt_overwrite, const char* password) {
    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;
    uLong ratio=0;
    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;
    }
362
363
364
365
366
367
368














369
370
371
372
373
374
375
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378







+
+
+
+
+
+
+
+
+
+
+
+
+
+







        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);
        }

435
436
437
438
439
440
441
442

443
444
445
446
447
448
449
438
439
440
441
442
443
444

445
446
447
448
449
450
451
452







-
+







                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,err,1,fout)!=1)
                    if (fwrite(buf,(unsigned)err,1,fout)!=1)
                    {
                        printf("error in writing extracted file\n");
                        err=UNZ_ERRNO;
                        break;
                    }
            }
            while (err>0);
468
469
470
471
472
473
474
475
476
477

478
479
480
481
482
483
484
485
486
487
488
489
490
491
471
472
473
474
475
476
477



478



479
480
481

482
483
484
485
486
487
488







-
-
-
+
-
-
-



-







    }

    free(buf);
    return err;
}


int do_extract(uf,opt_extract_without_path,opt_overwrite,password)
    unzFile uf;
    int opt_extract_without_path;
static int do_extract(unzFile uf, int opt_extract_without_path, int opt_overwrite, const char* password) {
    int opt_overwrite;
    const char* password;
{
    uLong i;
    unz_global_info64 gi;
    int err;
    FILE* fout=NULL;

    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++)
    {
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
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







-
-
-
-
+
-
-
-
-















-
-
-
+
-







            }
        }
    }

    return 0;
}

int do_extract_onefile(uf,filename,opt_extract_without_path,opt_overwrite,password)
    unzFile uf;
    const char* filename;
    int opt_extract_without_path;
static int do_extract_onefile(unzFile uf, const char* filename, int opt_extract_without_path, int opt_overwrite, const char* password) {
    int opt_overwrite;
    const char* password;
{
    int err = UNZ_OK;
    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(argc,argv)
    int argc;
    char *argv[];
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;
561
562
563
564
565
566
567
568

569
570
571
572
573
574
575
548
549
550
551
552
553
554

555
556
557
558
559
560
561
562







-
+







        {
            if ((*argv[i])=='-')
            {
                const char *p=argv[i]+1;

                while ((*p)!='\0')
                {
                    char c=*(p++);;
                    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'))
603
604
605
606
607
608
609
610

611
612
613
614
615
616
617
590
591
592
593
594
595
596

597
598
599
600
601
602
603
604







-
+







    {

#        ifdef USEWIN32IOAPI
        zlib_filefunc64_def ffunc;
#        endif

        strncpy(filename_try, zipfilename,MAXFILENAME-1);
        /* strncpy doesnt append the trailing NULL, of the string is too long. */
        /* 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);

Changes to compat/zlib/contrib/minizip/minizip.c.

24
25
26
27
28
29
30
31

32
33
34
35
36
37
38
24
25
26
27
28
29
30

31
32
33
34
35
36
37
38







-
+







                #define _LARGEFILE64_SOURCE
        #endif
        #ifndef _FILE_OFFSET_BIT
                #define _FILE_OFFSET_BIT 64
        #endif
#endif

#ifdef __APPLE__
#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)
67
68
69
70
71
72
73
74
75
76
77



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97

98
99
100
101



102

103
104
105
106
107
108
109
110
111

112
113
114
115
116

117
118
119
120
121
122
123
67
68
69
70
71
72
73




74
75
76

77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94

95




96
97
98

99
100
101
102
103
104
105
106
107

108
109
110
111
112

113
114
115
116
117
118
119
120







-
-
-
-
+
+
+
-


















-
+
-
-
-
-
+
+
+
-
+








-
+




-
+










#define WRITEBUFFERSIZE (16384)
#define MAXFILENAME (256)

#ifdef _WIN32
uLong filetime(f, tmzip, dt)
    char *f;                /* name of file to get info on */
    tm_zip *tmzip;             /* return value: access, modific. and creation times */
    uLong *dt;             /* dostime */
/* 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
#ifdef unix || __APPLE__
#if defined(unix) || defined(__APPLE__)
uLong filetime(f, tmzip, dt)
    char *f;               /* name of file to get info on */
    tm_zip *tmzip;         /* return value: access, modific. and creation times */
    uLong *dt;             /* dostime */
/* 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];
    int len = strlen(f);
    size_t len = strlen(f);
    if (len > MAXFILENAME)
      len = MAXFILENAME;

    strncpy(name, f,MAXFILENAME-1);
    /* strncpy doesnt append the trailing NULL, of the string is too long. */
    /* 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)
    {
133
134
135
136
137
138
139
140
141
142
143



144



145
146
147
148
149
150
151
152
153
154

155
156
157
158
159
160
161
162
163
164
165
166

167
168
169
170
171
172

173
174
175
176
177
178
179
180
181
182
183
184
185

186
187
188
189
190
191
192

193
194
195
196
197
198
199
200
201
202

203
204
205
206
207
208
209
210
211
212


213
214
215
216
217
218
219
220
221
222
223
224

225
226
227
228
229
230
231
232
233


234
235

236
237
238
239
240
241
242
243
244
245
246
247
248

249
250
251
252
253
254
255
256
257
258

259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279

280
281
282
283
284
285
286
130
131
132
133
134
135
136




137
138
139

140
141
142
143
144
145
146
147
148
149
150


151

152
153
154
155
156
157
158
159
160
161

162

163
164
165
166

167

168
169
170
171
172
173
174
175
176
177
178

179

180
181
182
183
184

185
186
187
188
189
190
191
192
193
194

195
196
197
198
199
200
201
202
203


204
205
206
207
208
209
210
211
212
213
214
215
216

217

218
219
220
221
222
223


224
225
226

227
228
229
230
231
232
233
234
235
236
237



238

239
240
241
242
243
244
245
246

247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267

268
269
270
271
272
273
274
275







-
-
-
-
+
+
+
-
+
+
+








-
-
+
-










-
+
-




-
+
-











-
+
-





-
+









-
+








-
-
+
+











-
+
-






-
-
+
+

-
+










-
-
-
+
-








-
+




















-
+







  tmzip->tm_mday = filedate->tm_mday;
  tmzip->tm_mon  = filedate->tm_mon ;
  tmzip->tm_year = filedate->tm_year;

  return ret;
}
#else
uLong filetime(f, tmzip, dt)
    char *f;                /* name of file to get info on */
    tm_zip *tmzip;             /* return value: access, modific. and creation times */
    uLong *dt;             /* dostime */
/* 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




int check_exist_file(filename)
    const char* filename;
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;
}

void do_banner()
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");
}

void do_help()
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 */
int getFileCrc(const char* filenameinzip,void*buf,unsigned long size_buf,unsigned long* result_crc)
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;
   /* unsigned long total_read = 0; */
   if (fin==NULL)
   {
       err = ZIP_ERRNO;
   }

    if (err == ZIP_OK)
        do
        {
            err = ZIP_OK;
            size_read = (int)fread(buf,1,size_buf,fin);
            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(calculate_crc,buf,size_read);
            total_read += size_read;
                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;
}

int isLargeFile(const char* filename)
static int isLargeFile(const char* filename) {
{
  int largeFile = 0;
  ZPOS64_T pos = 0;
  FILE* pFile = FOPEN_FUNC(filename, "rb");

  if(pFile != NULL)
  {
    int n = FSEEKO_FUNC(pFile, 0, SEEK_END);
    pos = FTELLO_FUNC(pFile);
    FSEEKO_FUNC(pFile, 0, SEEK_END);
    pos = (ZPOS64_T)FTELLO_FUNC(pFile);

                printf("File : %s is %lld bytes\n", filename, pos);
                printf("File : %s is %llu bytes\n", filename, pos);

    if(pos >= 0xffffffff)
     largeFile = 1;

                fclose(pFile);
  }

 return largeFile;
}

int main(argc,argv)
    int argc;
    char *argv[];
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;
    int size_buf=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++);;
                    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'))
318
319
320
321
322
323
324
325

326
327
328
329
330
331
332
307
308
309
310
311
312
313

314
315
316
317
318
319
320
321







-
+







    else
    {
        int i,len;
        int dot_found=0;

        zipok = 1 ;
        strncpy(filename_try, argv[zipfilenamearg],MAXFILENAME-1);
        /* strncpy doesnt append the trailing NULL, of the string is too long. */
        /* 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;

388
389
390
391
392
393
394
395

396
397
398
399


400
401
402
403
404
405
406
377
378
379
380
381
382
383

384
385
386


387
388
389
390
391
392
393
394
395







-
+


-
-
+
+








        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'))) &&
                   ((argv[i][1]>='0') && (argv[i][1]<='9'))) &&
                  (strlen(argv[i]) == 2)))
            {
                FILE * fin;
                int size_read;
                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 =
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
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







-
+









-
+







                    }
                }

                if (err == ZIP_OK)
                    do
                    {
                        err = ZIP_OK;
                        size_read = (int)fread(buf,1,size_buf,fin);
                        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,size_read);
                            err = zipWriteInFileInZip (zf,buf,(unsigned)size_read);
                            if (err<0)
                            {
                                printf("error in writing %s in the zipfile\n",
                                                 filenameinzip);
                            }

                        }

Changes to compat/zlib/contrib/minizip/mztools.c.

23
24
25
26
27
28
29
30

31
32
33
34
35
36
37
38
39
40
41
42
43
23
24
25
26
27
28
29

30






31
32
33
34
35
36
37







-
+
-
-
-
-
-
-







  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(file, fileOut, fileOutTmp, nRecovered, bytesRecovered)
extern int ZEXPORT unzRepair(const char* file, const char* fileOut, const char* fileOutTmp, uLong* nRecovered, uLong* bytesRecovered) {
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;

Changes to compat/zlib/contrib/minizip/unzip.c.

45
46
47
48
49
50
51
52

53
54
55
56
57


58
59
60
61
62
63
64
45
46
47
48
49
50
51

52
53
54
55


56
57
58
59
60
61
62
63
64







-
+



-
-
+
+







  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 - 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 paches recived from Gilles Vollant
        Oct-2009 - Mathias Svensson - Applied support to unzip files with compression mathod BZIP2 (bzip2 lib is required)
  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

*/
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
73
74
75
76
77
78
79


80
81
82
83
84
85
86







-
-







#endif

#include "zlib.h"
#include "unzip.h"

#ifdef STDC
#  include <stddef.h>
#  include <string.h>
#  include <stdlib.h>
#endif
#ifdef NO_ERRNO_H
    extern int errno;
#else
#   include <errno.h>
#endif

107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125

126
127
128
129
130
131
132
105
106
107
108
109
110
111



112
113
114
115
116
117
118
119

120
121
122
123
124
125
126
127







-
-
-








-
+







#ifndef UNZ_MAXFILENAMEINZIP
#define UNZ_MAXFILENAMEINZIP (256)
#endif

#ifndef ALLOC
# define ALLOC(size) (malloc(size))
#endif
#ifndef TRYFREE
# define TRYFREE(p) {if (p) free(p);}
#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_info_interntal contain internal info about a file in zipfile*/
/* 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,
149
150
151
152
153
154
155
156

157
158
159
160
161
162
163
164
165
166
167
168
169

170
171
172
173
174
175
176
144
145
146
147
148
149
150

151
152
153
154
155
156
157
158
159
160
161
162
163

164
165
166
167
168
169
170
171







-
+












-
+







    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 structore of the zipfile */
    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 structore of the zipfile */
    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*/

192
193
194
195
196
197
198
199

200
201

202
203
204
205






206
207
208
209
210
211
212
213
214
215
216









































217
218


219
220
221
222

223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349

350
351
352
353
354
355
356
357
187
188
189
190
191
192
193
194
195
196

197


198
199
200
201
202
203
204
205











206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247

248
249
250
251
252
253
254
255
256
257
258
259
260
261






















































































































262

263

264
265
266
267
268
269
270








+

-
+
-
-


+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+
+




+







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

-
+
-







#    endif
} unz64_s;


#ifndef NOUNCRYPT
#include "crypt.h"
#endif


/* ===========================================================================
     Read a byte from a gz_stream; update next_in and avail_in. Return EOF
   Reads a long in LSB order from the given gz_stream. Sets
   for end of file.
   IN assertion: the stream s has been successfully opened for reading.
*/

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)

local int unz64local_getByte OF((
    const zlib_filefunc64_32_def* pzlib_filefunc_def,
    voidpf filestream,
    int *pi));

local int unz64local_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)
    {
        *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)
    {
        *pi = (int)c;
        *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;
    }
}


/* ===========================================================================
   Reads a long in LSB order from the given gz_stream. Sets
*/
local int unz64local_getShort OF((
    const zlib_filefunc64_32_def* pzlib_filefunc_def,
    voidpf filestream,
    uLong *pX));

local int unz64local_getShort (const zlib_filefunc64_32_def* pzlib_filefunc_def,
                             voidpf filestream,
                             uLong *pX)
{
    uLong x ;
    int i = 0;
    int err;

    err = unz64local_getByte(pzlib_filefunc_def,filestream,&i);
    x = (uLong)i;

    if (err==UNZ_OK)
        err = unz64local_getByte(pzlib_filefunc_def,filestream,&i);
    x |= ((uLong)i)<<8;

    if (err==UNZ_OK)
        *pX = x;
    else
        *pX = 0;
    return err;
}

local int unz64local_getLong OF((
    const zlib_filefunc64_32_def* pzlib_filefunc_def,
    voidpf filestream,
    uLong *pX));

local int unz64local_getLong (const zlib_filefunc64_32_def* pzlib_filefunc_def,
                            voidpf filestream,
                            uLong *pX)
{
    uLong x ;
    int i = 0;
    int err;

    err = unz64local_getByte(pzlib_filefunc_def,filestream,&i);
    x = (uLong)i;

    if (err==UNZ_OK)
        err = unz64local_getByte(pzlib_filefunc_def,filestream,&i);
    x |= ((uLong)i)<<8;

    if (err==UNZ_OK)
        err = unz64local_getByte(pzlib_filefunc_def,filestream,&i);
    x |= ((uLong)i)<<16;

    if (err==UNZ_OK)
        err = unz64local_getByte(pzlib_filefunc_def,filestream,&i);
    x += ((uLong)i)<<24;

    if (err==UNZ_OK)
        *pX = x;
    else
        *pX = 0;
    return err;
}

local int unz64local_getLong64 OF((
    const zlib_filefunc64_32_def* pzlib_filefunc_def,
    voidpf filestream,
    ZPOS64_T *pX));


local int unz64local_getLong64 (const zlib_filefunc64_32_def* pzlib_filefunc_def,
                            voidpf filestream,
                            ZPOS64_T *pX)
{
    ZPOS64_T x ;
    int i = 0;
    int err;

    err = unz64local_getByte(pzlib_filefunc_def,filestream,&i);
    x = (ZPOS64_T)i;

    if (err==UNZ_OK)
        err = unz64local_getByte(pzlib_filefunc_def,filestream,&i);
    x |= ((ZPOS64_T)i)<<8;

    if (err==UNZ_OK)
        err = unz64local_getByte(pzlib_filefunc_def,filestream,&i);
    x |= ((ZPOS64_T)i)<<16;

    if (err==UNZ_OK)
        err = unz64local_getByte(pzlib_filefunc_def,filestream,&i);
    x |= ((ZPOS64_T)i)<<24;

    if (err==UNZ_OK)
        err = unz64local_getByte(pzlib_filefunc_def,filestream,&i);
    x |= ((ZPOS64_T)i)<<32;

    if (err==UNZ_OK)
        err = unz64local_getByte(pzlib_filefunc_def,filestream,&i);
    x |= ((ZPOS64_T)i)<<40;

    if (err==UNZ_OK)
        err = unz64local_getByte(pzlib_filefunc_def,filestream,&i);
    x |= ((ZPOS64_T)i)<<48;

    if (err==UNZ_OK)
        err = unz64local_getByte(pzlib_filefunc_def,filestream,&i);
    x |= ((ZPOS64_T)i)<<56;

    if (err==UNZ_OK)
        *pX = x;
    else
        *pX = 0;
    return err;
}

/* My own strcmpi / strcasecmp */
local int strcmpcasenosensitive_internal (const char* fileName1, const char* fileName2)
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'))
375
376
377
378
379
380
381
382
383
384



385
386

387
388
389
390
391
392


393
394
395
396
397
398
399
400
401
402
403
404
405
406
407




408
409
410
411
412
413

414
415
416
417
418
419

420
421
422

423
424
425
426
427
428
429
430
431
432

433
434
435
436
437
438
439
288
289
290
291
292
293
294



295
296
297
298

299
300
301
302
303


304
305


306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326


327

328
329
330
331

332
333
334

335
336
337
338
339
340
341
342
343
344

345
346
347
348
349
350
351
352







-
-
-
+
+
+

-
+




-
-
+
+
-
-













+
+
+
+




-
-
+
-




-
+


-
+









-
+







#endif

#ifndef STRCMPCASENOSENTIVEFUNCTION
#define STRCMPCASENOSENTIVEFUNCTION strcmpcasenosensitive_internal
#endif

/*
   Compare two filename (fileName1,fileName2).
   If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp)
   If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi
   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 iCaseSenisivity = 0, case sensitivity is defaut of your operating system
   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)
                                             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 OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream));
local ZPOS64_T unz64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)
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=0;
    ZPOS64_T uPosFound=CENTRALDIRINVALID;

    if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0)
        return 0;
        return CENTRALDIRINVALID;


    uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream);

    if (uMaxBack>uSizeFile)
        uMaxBack = uSizeFile;

    buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4);
    if (buf==NULL)
        return 0;
        return CENTRALDIRINVALID;

    uBackRead = 4;
    while (uBackRead<uMaxBack)
    {
        uLong uReadSize;
        ZPOS64_T uReadPos ;
        int i;
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
364
365
366
367
368
369
370

371
372
373
374

375
376
377

378
379
380
381
382
383
384
385
386




387

388

389
390
391
392

393
394
395
396
397

398
399
400
401
402
403
404
405
406
407

408
409
410
411
412
413
414
415







-
+



-
+


-
+








-
-
-
-

-
+
-




-
+




-
+









-
+







        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+i;
                uPosFound = uReadPos+(unsigned)i;
                break;
            }

        if (uPosFound!=0)
        if (uPosFound!=CENTRALDIRINVALID)
            break;
    }
    TRYFREE(buf);
    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 OF((
    const zlib_filefunc64_32_def* pzlib_filefunc_def,
    voidpf filestream));

local ZPOS64_T unz64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib_filefunc_def,
                                      voidpf filestream)
                                             voidpf filestream) {
{
    unsigned char* buf;
    ZPOS64_T uSizeFile;
    ZPOS64_T uBackRead;
    ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */
    ZPOS64_T uPosFound=0;
    ZPOS64_T uPosFound=CENTRALDIRINVALID;
    uLong uL;
                ZPOS64_T relativeOffset;

    if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0)
        return 0;
        return CENTRALDIRINVALID;


    uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream);

    if (uMaxBack>uSizeFile)
        uMaxBack = uSizeFile;

    buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4);
    if (buf==NULL)
        return 0;
        return CENTRALDIRINVALID;

    uBackRead = 4;
    while (uBackRead<uMaxBack)
    {
        uLong uReadSize;
        ZPOS64_T uReadPos;
        int i;
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
427
428
429
430
431
432
433

434
435
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







-
+



-
+


-
-
-
+
+
+



-
+



-
+

-
+

-
+

-
+



-
+



-
+

-
+



-
+



-
+


-
+













-
-
-
+
+
+
-





-
-
-
-
+
+
+
+







        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+i;
                uPosFound = uReadPos+(unsigned)i;
                break;
            }

        if (uPosFound!=0)
        if (uPosFound!=CENTRALDIRINVALID)
            break;
    }
    TRYFREE(buf);
    if (uPosFound == 0)
        return 0;
    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 0;
        return CENTRALDIRINVALID;

    /* the signature, already checked */
    if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK)
        return 0;
        return CENTRALDIRINVALID;

    /* number of the disk with the start of the zip64 end of  central directory */
    /* 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 0;
        return CENTRALDIRINVALID;
    if (uL != 0)
        return 0;
        return CENTRALDIRINVALID;

    /* relative offset of the zip64 end of central directory record */
    if (unz64local_getLong64(pzlib_filefunc_def,filestream,&relativeOffset)!=UNZ_OK)
        return 0;
        return CENTRALDIRINVALID;

    /* total number of disks */
    if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK)
        return 0;
        return CENTRALDIRINVALID;
    if (uL != 1)
        return 0;
        return CENTRALDIRINVALID;

    /* Goto end of central directory record */
    if (ZSEEK64(*pzlib_filefunc_def,filestream, relativeOffset,ZLIB_FILEFUNC_SEEK_SET)!=0)
        return 0;
        return CENTRALDIRINVALID;

     /* the signature */
    if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK)
        return 0;
        return CENTRALDIRINVALID;

    if (uL != 0x06064b50)
        return 0;
        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)
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 dist, used for
                                   spaning ZIP, unsupported, always 0*/
    uLong number_disk_with_CD;  /* number the the disk with central dir, used
                                   for spaning ZIP, unsupported, always 0*/
    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]!=' ')
617
618
619
620
621
622
623
624

625
626
627
628
629
630
631
524
525
526
527
528
529
530

531
532
533
534
535
536
537
538







-
+







                                                 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)
    if (central_pos!=CENTRALDIRINVALID)
    {
        uLong uS;
        ZPOS64_T uL64;

        us.isZip64 = 1;

        if (ZSEEK64(us.z_filefunc, us.filestream,
679
680
681
682
683
684
685
686

687
688
689
690
691
692
693
586
587
588
589
590
591
592

593
594
595
596
597
598
599
600







-
+







            err=UNZ_ERRNO;

        us.gi.size_comment = 0;
    }
    else
    {
        central_pos = unz64local_SearchCentralDir(&us.z_filefunc,us.filestream);
        if (central_pos==0)
        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;
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
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







-
-
+
+
-










-
-
+
+
-












-
+
-



-
+
-








-
+
-









-
+








-
+
-








-
+
-










-
+

-
+
-


-
-
-
+
+
+

-
-
-
+
+
+





-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
-







        *s=us;
        unzGoToFirstFile((unzFile)s);
    }
    return (unzFile)s;
}


extern unzFile ZEXPORT unzOpen2 (const char *path,
                                        zlib_filefunc_def* pzlib_filefunc32_def)
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)
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)
extern unzFile ZEXPORT unzOpen(const char *path) {
{
    return unzOpenInternal(path, NULL, 0);
}

extern unzFile ZEXPORT unzOpen64 (const void *path)
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)
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);
    TRYFREE(s);
    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)
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)
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 easilty)
   Translate date/time from Dos format to tm_unz (readable more easily)
*/
local void unz64local_DosDateToTmuDate (ZPOS64_T ulDosDate, tm_unz* ptm)
local void unz64local_DosDateToTmuDate(ZPOS64_T ulDosDate, tm_unz* ptm) {
{
    ZPOS64_T uDate;
    uDate = (ZPOS64_T)(ulDosDate>>16);
    ptm->tm_mday = (uInt)(uDate&0x1f) ;
    ptm->tm_mon =  (uInt)((((uDate)&0x1E0)/0x20)-1) ;
    ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ;
    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 = (uInt) ((ulDosDate &0xF800)/0x800);
    ptm->tm_min =  (uInt) ((ulDosDate&0x7E0)/0x20) ;
    ptm->tm_sec =  (uInt) (2*(ulDosDate&0x1f)) ;
    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 OF((unzFile file,
                                                  unz_file_info64 *pfile_info,
                                                  unz_file_info64_internal
                                                  *pfile_info_internal,
                                                  char *szFileName,
                                                  uLong fileNameBufferSize,
                                                  void *extraField,
                                                  uLong extraFieldBufferSize,
                                                  char *szComment,
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));

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)
                                                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;
989
990
991
992
993
994
995
996

997
998
999
1000
1001
1002
1003
876
877
878
879
880
881
882

883
884
885
886
887
888
889
890







-
+







        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,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==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)
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
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







-
+



















-
-
-
-
-
-
-
+
+
+
+
+

-
-
-
-
-
+
+
+
+
+

-
-
-
-
-
-
+
+
+
+
+
+

-
-
-
-
-
-
+
+
+
+
+
+







                                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,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==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)
            {
                                                        uLong uL;

                                                                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.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.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_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 == MAXU32)
                                                                {
                                                                        /* Disk Start Number */
                                                                        if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != 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;
            }
1086
1087
1088
1089
1090
1091
1092
1093

1094
1095
1096
1097
1098
1099
1100
971
972
973
974
975
976
977

978
979
980
981
982
983
984
985







-
+







            uSizeRead = file_info.size_file_comment;
        }
        else
            uSizeRead = commentBufferSize;

        if (lSeek!=0)
        {
            if (ZSEEK64(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==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)
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
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







-
-
-
-
-
+
+
+
+
+
-

-
-
-
+
+
+


-
-
-
-
-
+
+
+
+
+
-









/*
  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)
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);
                                                 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)
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))
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
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







-
+












-
+
-



















-
+
-







        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->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)
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)
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)
1225
1226
1227
1228
1229
1230
1231
1232

1233
1234
1235
1236
1237
1238
1239
1240
1106
1107
1108
1109
1110
1111
1112

1113

1114
1115
1116
1117
1118
1119
1120







-
+
-







  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)
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;
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
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







-
+
-














-
+
-
-
-










-
+
-




















-
+
-
-
-







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)
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(
extern int ZEXPORT unzGetFilePos(unzFile file, unz_file_pos* file_pos) {
    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)
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(
extern int ZEXPORT unzGoToFilePos(unzFile file, unz_file_pos* file_pos) {
    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);
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387



1388
1389
1390
1391
1392
1393
1394
1395
1250
1251
1252
1253
1254
1255
1256



1257
1258
1259

1260
1261
1262
1263
1264
1265
1266







-
-
-
+
+
+
-







/*
  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)
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;
1465
1466
1467
1468
1469
1470
1471
1472
1473


1474
1475
1476
1477
1478
1479
1480
1481
1336
1337
1338
1339
1340
1341
1342


1343
1344

1345
1346
1347
1348
1349
1350
1351







-
-
+
+
-







    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)
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
1505
1506
1507
1508
1509
1510
1511
1512

1513
1514
1515
1516
1517
1518
1519
1375
1376
1377
1378
1379
1380
1381

1382
1383
1384
1385
1386
1387
1388
1389







-
+







    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)
    {
        TRYFREE(pfile_in_zip_read_info);
        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;
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
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







+
-
+



















+
-
+







      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);
        TRYFREE(pfile_in_zip_read_info);
        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);
        TRYFREE(pfile_in_zip_read_info);
        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
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
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







-
+
-



-
+
-



-
+
-





-
+
-



















-
+




-
+
-







    }
#    endif


    return UNZ_OK;
}

extern int ZEXPORT unzOpenCurrentFile (unzFile file)
extern int ZEXPORT unzOpenCurrentFile(unzFile file) {
{
    return unzOpenCurrentFile3(file, NULL, NULL, 0, NULL);
}

extern int ZEXPORT unzOpenCurrentFilePassword (unzFile file, const char*  password)
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)
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)
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 somes bytes are copied
  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)
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;
1763
1764
1765
1766
1767
1768
1769
1770

1771
1772
1773
1774
1775
1776
1777
1630
1631
1632
1633
1634
1635
1636

1637
1638
1639
1640
1641
1642
1643
1644







-
+








        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 : iRead;
                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 ;

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
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







+
+
+














-
+






-
+







-
+
-













-
+
-


















-
+
-







            */
            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 : iRead;
                return (iRead==0) ? UNZ_EOF : (int)iRead;
            if (err!=Z_OK)
                break;
        }
    }

    if (err==Z_OK)
        return iRead;
        return (int)iRead;
    return err;
}


/*
  Give the current position in uncompressed data
*/
extern z_off_t ZEXPORT unztell (unzFile file)
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)
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)
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;

1949
1950
1951
1952
1953
1954
1955
1956

1957
1958
1959
1960
1961
1962
1963
1964
1816
1817
1818
1819
1820
1821
1822

1823

1824
1825
1826
1827
1828
1829
1830







-
+
-







  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)
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;
1997
1998
1999
2000
2001
2002
2003
2004

2005
2006
2007
2008
2009
2010
2011
2012
1863
1864
1865
1866
1867
1868
1869

1870

1871
1872
1873
1874
1875
1876
1877







-
+
-







    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)
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;
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
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







-
+










-
+












-
+
-







        (!pfile_in_zip_read_info->raw))
    {
        if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait)
            err=UNZ_CRCERROR;
    }


    TRYFREE(pfile_in_zip_read_info->read_buffer);
    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;
    TRYFREE(pfile_in_zip_read_info);
    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)
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;
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
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







-
+
-













-
+
-








-
+
-
















-
+
-



    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)
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)
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)
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)
extern int ZEXPORT unzSetOffset (unzFile file, uLong pos) {
{
    return unzSetOffset64(file,pos);
}

Changes to compat/zlib/contrib/minizip/unzip.h.

79
80
81
82
83
84
85
86
87
88
89
90
91






92
93
94
95
96
97
98
79
80
81
82
83
84
85






86
87
88
89
90
91
92
93
94
95
96
97
98







-
-
-
-
-
-
+
+
+
+
+
+







#define UNZ_BADZIPFILE                  (-103)
#define UNZ_INTERNALERROR               (-104)
#define UNZ_CRCERROR                    (-105)

/* tm_unz contain date/time info */
typedef struct tm_unz_s
{
    uInt tm_sec;            /* seconds after the minute - [0,59] */
    uInt tm_min;            /* minutes after the hour - [0,59] */
    uInt tm_hour;           /* hours since midnight - [0,23] */
    uInt tm_mday;           /* day of the month - [1,31] */
    uInt tm_mon;            /* months since January - [0,11] */
    uInt tm_year;           /* years - [1980..2044] */
    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
146
147
148
149
150
151
152
153
154
155



156
157
158
159



160
161

162
163
164
165
166
167


168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185


186
187
188
189
190
191
192


193
194
195
196
197
198

199
200
201
202
203
204
205
206


207
208
209


210
211
212
213
214
215
216
217
218



219
220
221
222
223
224
225
226
227
228
229

230
231
232
233
234
235

236
237
238
239
240
241
242
243
244



245
246
247
248
249
250
251
146
147
148
149
150
151
152



153
154
155
156



157
158
159
160

161
162
163
164
165


166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183


184
185
186
187
188
189
190


191
192
193
194
195
196
197

198
199
200
201
202
203
204


205
206
207


208
209
210
211
212
213
214
215



216
217
218
219
220
221
222
223
224
225
226
227
228

229
230
231
232
233
234

235
236
237
238
239
240
241



242
243
244
245
246
247
248
249
250
251







-
-
-
+
+
+

-
-
-
+
+
+

-
+




-
-
+
+
















-
-
+
+





-
-
+
+





-
+






-
-
+
+

-
-
+
+






-
-
-
+
+
+










-
+





-
+






-
-
-
+
+
+







    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 OF ((const char* fileName1,
                                                 const char* fileName2,
                                                 int iCaseSensitivity));
extern int ZEXPORT unzStringFileNameCompare(const char* fileName1,
                                            const char* fileName2,
                                            int iCaseSensitivity);
/*
   Compare two filename (fileName1,fileName2).
   If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp)
   If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi
   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 iCaseSenisivity = 0, case sensitivity is defaut of your operating system
   If iCaseSensitivity = 0, case sensitivity is default of your operating system
    (like 1 on Unix, 2 on Windows)
*/


extern unzFile ZEXPORT unzOpen OF((const char *path));
extern unzFile ZEXPORT unzOpen64 OF((const void *path));
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 OF((const char *path,
                                    zlib_filefunc_def* pzlib_filefunc_def));
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 OF((const void *path,
                                    zlib_filefunc64_def* pzlib_filefunc_def));
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 OF((unzFile file));
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 OF((unzFile file,
                                        unz_global_info *pglobal_info));
extern int ZEXPORT unzGetGlobalInfo(unzFile file,
                                    unz_global_info *pglobal_info);

extern int ZEXPORT unzGetGlobalInfo64 OF((unzFile file,
                                        unz_global_info64 *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 OF((unzFile file,
                                           char *szComment,
                                           uLong uSizeBuf));
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 OF((unzFile file));
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 OF((unzFile file));
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 OF((unzFile file,
                     const char *szFileName,
                     int iCaseSensitivity));
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
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295








296
297
298
299
300
301
302
303
304








305
306
307

308
309

310
311
312
313
314
315
316
317
318
319
320
321

322
323
324
325
326
327
328
329
330
331

332
333
334
335
336
337
338


339
340
341
342
343
344
345
346
347
348




349
350
351
352
353
354
355
356
357
358
359
360
361
362





363
364
365
366
367
368
369
370
371
372
373

374
375
376
377
378
379
380
381



382
383
384
385
386
387

388
389
390
391
392
393

394
395

396
397
398
399
400

401
402
403
404
405
406
407



408
409
410
411
412
413
414
281
282
283
284
285
286
287








288
289
290
291
292
293
294
295
296








297
298
299
300
301
302
303
304
305
306

307
308

309
310
311
312
313
314
315
316
317
318
319
320

321
322
323
324
325
326
327
328
329
330

331
332
333
334
335
336


337
338
339
340
341
342
343
344




345
346
347
348
349
350
351
352
353
354
355
356
357





358
359
360
361
362
363
364
365
366
367
368
369
370
371
372

373
374
375
376
377
378



379
380
381
382
383
384
385
386

387
388
389
390
391
392

393
394

395
396
397
398
399

400
401
402
403
404



405
406
407
408
409
410
411
412
413
414







-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+

-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+


-
+

-
+











-
+









-
+





-
-
+
+






-
-
-
-
+
+
+
+









-
-
-
-
-
+
+
+
+
+










-
+





-
-
-
+
+
+





-
+





-
+

-
+




-
+




-
-
-
+
+
+








extern int ZEXPORT unzGoToFilePos64(
    unzFile file,
    const unz64_file_pos* file_pos);

/* ****************************************** */

extern int ZEXPORT unzGetCurrentFileInfo64 OF((unzFile file,
                         unz_file_info64 *pfile_info,
                         char *szFileName,
                         uLong fileNameBufferSize,
                         void *extraField,
                         uLong extraFieldBufferSize,
                         char *szComment,
                         uLong commentBufferSize));
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 OF((unzFile file,
                         unz_file_info *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 somes info about
  if pfile_info!=NULL, the *pfile_info structure will contain some info about
        the current file
  if szFileName!=NULL, the filemane string will be copied in szFileName
  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 OF((unzFile file));
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 OF((unzFile 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 OF((unzFile file,
                                                  const char* password));
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 OF((unzFile file,
                                           int* method,
                                           int* level,
                                           int raw));
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 OF((unzFile file,
                                           int* method,
                                           int* level,
                                           int raw,
                                           const char* password));
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 OF((unzFile file));
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 OF((unzFile file,
                      voidp buf,
                      unsigned len));
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 somes bytes are copied
  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 OF((unzFile file));
extern z_off_t ZEXPORT unztell(unzFile file);

extern ZPOS64_T ZEXPORT unztell64 OF((unzFile file));
extern ZPOS64_T ZEXPORT unztell64(unzFile file);
/*
  Give the current position in uncompressed data
*/

extern int ZEXPORT unzeof OF((unzFile file));
extern int ZEXPORT unzeof(unzFile file);
/*
  return 1 if the end of file was reached, 0 elsewhere
*/

extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file,
                                             voidp buf,
                                             unsigned len));
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

Changes to compat/zlib/contrib/minizip/zip.c.

10
11
12
13
14
15
16
17

18
19
20
21
22
23
24
25
26
27

28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
10
11
12
13
14
15
16

17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34


35
36
37
38
39
40
41
42
43
44
45
46
47
48

49
50
51
52
53
54
55
56
57
58
59
60
61
62



63
64
65
66
67
68
69







-
+










+






-
-














-
+













-
-
-







         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 recreting zip archive with RAW when deleting items from a zip.
                                 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>
#  include <string.h>
#  include <stdlib.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 depedent */
# 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
#ifndef TRYFREE
# define TRYFREE(p) {if (p) free(p);}
#endif

/*
#define SIZECENTRALDIRITEM (0x2e)
#define SIZEZIPLOCALHEADER (0x1e)
*/

/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */
134
135
136
137
138
139
140
141

142
143
144
145
146
147
148

149
150
151
152
153
154

155
156
157
158
159
160
161

162
163
164
165
166
167
168

169
170
171

172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189

190
191
192
193
194
195
196
197
198
199
200
201
202
203

204
205
206
207
208

209
210
211
212
213

214
215
216
217
218

219
220
221
222
223
224
225

226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241

242
243
244
245
246
247
248
130
131
132
133
134
135
136

137
138
139
140
141
142
143

144
145
146
147
148
149

150
151
152
153
154
155
156

157
158
159
160
161
162
163

164
165
166

167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184

185

186
187
188
189
190
191
192
193
194
195
196
197

198

199
200
201

202
203
204
205
206

207

208
209
210

211

212
213
214
215
216

217

218
219
220
221
222
223
224
225
226
227
228
229
230
231

232
233
234
235
236
237
238
239







-
+






-
+





-
+






-
+






-
+


-
+

















-
+
-












-
+
-



-
+




-
+
-



-
+
-





-
+
-














-
+







    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
                                     currenty writing */
                                     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 currenty wr.*/
    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 extened information in the extra field */
    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;
    int crypt_header_size;
    unsigned crypt_header_size;
#endif
} curfile64_info;

typedef struct
{
    zlib_filefunc64_32_def z_filefunc;
    voidpf filestream;        /* io structore of the zipfile */
    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 curretly writing */
    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()
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)
local void free_datablock(linkedlist_datablock_internal* ldi) {
{
    while (ldi!=NULL)
    {
        linkedlist_datablock_internal* ldinext = ldi->next_datablock;
        TRYFREE(ldi);
        free(ldi);
        ldi = ldinext;
    }
}

local void init_linkedlist(linkedlist_data* ll)
local void init_linkedlist(linkedlist_data* ll) {
{
    ll->first_block = ll->last_block = NULL;
}

local void free_linkedlist(linkedlist_data* ll)
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)
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 = (unsigned char*)buf;
    from_copy = (const unsigned char*)buf;

    while (len>0)
    {
        uInt copy_this;
        uInt i;
        unsigned char* to_copy;

279
280
281
282
283
284
285
286
287

288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304

305
306
307
308
309
310
311

312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332

333
334
335
336
337
338
339
340
341


342
343
344
345
346
347
348
349

350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373

374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395

396
397
398
399
400
401
402
403
270
271
272
273
274
275
276


277

278
279
280
281
282
283
284
285
286
287
288
289
290
291
292

293
294
295
296
297
298


299

300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318

319

320
321
322
323
324
325


326
327
328
329
330
331
332



333

334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353



354

355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372



373

374
375
376
377
378
379
380







-
-
+
-















-
+





-
-
+
-



















-
+
-






-
-
+
+





-
-
-
+
-




















-
-
-
+
-


















-
-
-
+
-








#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 OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T x, int nbByte));
local int zip64local_putValue (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T x, int nbByte)
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,nbByte)!=(uLong)nbByte)
    if (ZWRITE64(*pzlib_filefunc_def,filestream,buf,(uLong)nbByte)!=(uLong)nbByte)
        return ZIP_ERRNO;
    else
        return ZIP_OK;
}

local void zip64local_putValue_inmemory OF((void* dest, ZPOS64_T x, int nbByte));
local void zip64local_putValue_inmemory (void* dest, ZPOS64_T x, int nbByte)
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)
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) (((ptm->tm_mday) + (32 * (ptm->tm_mon+1)) + (512 * year)) << 16) |
        ((ptm->tm_sec/2) + (32* ptm->tm_min) + (2048 * (uLong)ptm->tm_hour));
      (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 OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, int *pi));

local int zip64local_getByte(const zlib_filefunc64_32_def* pzlib_filefunc_def,voidpf filestream,int* pi)
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 OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong *pX));

local int zip64local_getShort (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong* pX)
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 OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong *pX));

local int zip64local_getLong (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong* pX)
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;

416
417
418
419
420
421
422
423
424
425
426

427
428
429
430
431
432
433
434
393
394
395
396
397
398
399

400


401

402
403
404
405
406
407
408







-

-
-
+
-







    if (err==ZIP_OK)
        *pX = x;
    else
        *pX = 0;
    return err;
}

local int zip64local_getLong64 OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T *pX));


local int zip64local_getLong64 (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T *pX)
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;

471
472
473
474
475
476
477
478
479
480

481
482
483
484
485
486
487
488
445
446
447
448
449
450
451



452

453
454
455
456
457
458
459







-
-
-
+
-







#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 OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream));

local ZPOS64_T zip64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)
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)
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
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







-
+



-
-
+
+

-
+







-
-
-
+
-







    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+i;
        uPosFound = uReadPos+(unsigned)i;
        break;
      }

      if (uPosFound!=0)
        break;
    if (uPosFound!=0)
      break;
  }
  TRYFREE(buf);
  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 OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream));

local ZPOS64_T zip64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)
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;
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
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







-
+








-
+











-
+







      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+i;
        uPosFound = uReadPos+(unsigned)i;
        break;
      }
    }

      if (uPosFound!=0)
        break;
  }

  TRYFREE(buf);
  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 */
  /* 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)
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
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







-
+
-








-
-
-
-
+
+
+
+








  if (uL != 0x06064b50) // signature of 'Zip64 end of central directory'
    return 0;

  return relativeOffset;
}

int LoadCentralDirectoryRecord(zip64_internal* pziinit)
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 dist, used for
                              spaning ZIP, unsupported, always 0*/
  uLong number_disk_with_CD;  /* number the the disk with central dir, used
                              for spaning ZIP, unsupported, always 0*/
  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;
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
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







-
+















-
+
-







        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;
    }
    TRYFREE(buf_read);
    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)
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)
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
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







-
+

-
+









-
+
-










-
+
-














-
+
-



-
+
-



-
+
-







      *globalcomment = ziinit.globalcomment;
    }
#    endif /* !NO_ADDFILEINEXISTINGZIP*/

    if (err != ZIP_OK)
    {
#    ifndef NO_ADDFILEINEXISTINGZIP
        TRYFREE(ziinit.globalcomment);
        free(ziinit.globalcomment);
#    endif /* !NO_ADDFILEINEXISTINGZIP*/
        TRYFREE(zi);
        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)
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)
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)
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)
extern zipFile ZEXPORT zipOpen64(const void* pathname, int append) {
{
    return zipOpen3(pathname,append,NULL,NULL);
}

int Write_LocalFileHeader(zip64_internal* zi, const char* filename, uInt size_extrafield_local, const void* extrafield_local)
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);

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
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







-
-
+
+
















-
-
-
-
-
-
-
+
+
+
+
+
+
+
-







      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, (short)HeaderID,2);
      err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (short)DataSize,2);
      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)
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
1078
1079
1080
1081
1082
1083
1084











1085
1086
1087
1088
1089
1090
1091
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







+
+
+
+
+
+
+
+
+
+
+







#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)
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
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







-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+


-
-
-
-
-
-
+
+
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+







-
+
-
-
-
-
-
-
-
+
+
+
+
+
+





-
+
-
-
-
-
-
-
-
+
+
+
+
+
+



-
-
-
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+


-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+


-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+


-
+
-







#    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)
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);
    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)
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);
    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)
                                         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);
    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)
                                        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);
    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)
                                           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);
    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)
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);
    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)
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);
    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)
local int zip64FlushWriteBuffer(zip64_internal* zi) {
{
    int err=ZIP_OK;

    if (zi->ci.encrypt != 0)
    {
#ifndef NOCRYPT
        uInt i;
        int t;
1395
1396
1397
1398
1399
1400
1401
1402

1403
1404
1405
1406
1407
1408
1409
1410
1358
1359
1360
1361
1362
1363
1364

1365

1366
1367
1368
1369
1370
1371
1372







-
+
-









    zi->ci.pos_in_buffered_data = 0;

    return err;
}

extern int ZEXPORT zipWriteInFileInZip (zipFile file,const void* buf,unsigned int len)
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;

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
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







-
+




















-
-
-
-
-








      if(err == BZ_RUN_OK)
        err = ZIP_OK;
    }
    else
#endif
    {
      zi->ci.stream.next_in = (Bytef*)buf;
      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);
              if(uTotalOutBefore > zi->ci.stream.total_out)
              {
                int bBreak = 0;
                bBreak++;
              }

              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)
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
1459
1460
1461
1462
1463
1464
1465

1466

1467
1468
1469

1470

1471
1472
1473

1474
1475
1476
1477
1478
1479
1480
1481







-
+
-



-
+
-



-
+







          }
      }// while(...)
    }

    return err;
}

extern int ZEXPORT zipCloseFileInZipRaw (zipFile file, uLong uncompressed_size, uLong crc32)
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)
extern int ZEXPORT zipCloseFileInZipRaw64(zipFile file, ZPOS64_T uncompressed_size, uLong crc32) {
{
    zip64_internal* zi;
    ZPOS64_T compressed_size;
    uLong invalidValue = 0xffffffff;
    short datasize = 0;
    unsigned datasize = 0;
    int err=ZIP_OK;

    if (file == NULL)
        return ZIP_PARAMERROR;
    zi = (zip64_internal*)file;

    if (zi->in_opened_file_inzip == 0)
1649
1650
1651
1652
1653
1654
1655
1656

1657
1658
1659
1660
1661
1662
1663
1604
1605
1606
1607
1608
1609
1610

1611
1612
1613
1614
1615
1616
1617
1618







-
+








    if(datasize > 0)
    {
      char* p = NULL;

      if((uLong)(datasize + 4) > zi->ci.size_centralExtraFree)
      {
        // we can not write more data to the buffer that we have room for.
        // 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
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
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







-
+
-



-
+
-




















-
+
-








    zi->number_entry ++;
    zi->in_opened_file_inzip = 0;

    return err;
}

extern int ZEXPORT zipCloseFileInZip (zipFile file)
extern int ZEXPORT zipCloseFileInZip(zipFile file) {
{
    return zipCloseFileInZipRaw (file,0,0);
}

int Write_Zip64EndOfCentralDirectoryLocator(zip64_internal* zi, ZPOS64_T zip64eocd_pos_inzip)
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;
}

int Write_Zip64EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip)
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' */
1809
1810
1811
1812
1813
1814
1815
1816
1817


1818
1819
1820
1821
1822
1823
1824
1761
1762
1763
1764
1765
1766
1767


1768
1769
1770
1771
1772
1773
1774
1775
1776







-
-
+
+







  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;
}
int Write_EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip)
{

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);
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
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







-
+
-
















-
+
-







    else
      err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (uLong)(centraldir_pos_inzip - zi->add_position_when_writing_offset),4);
  }

   return err;
}

int Write_GlobalComment(zip64_internal* zi, const char* global_comment)
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)
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)
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
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







-
+


















-
+

-
+




-
+
-









-
+


-
+







            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)
    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
    TRYFREE(zi->globalcomment);
    free(zi->globalcomment);
#endif
    TRYFREE(zi);
    free(zi);

    return err;
}

extern int ZEXPORT zipRemoveExtraInfoBlock (char* pData, int* dataLen, short sHeader)
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 < 4)
  if(pData == NULL || dataLen == NULL || *dataLen < 4)
    return ZIP_PARAMERROR;

  pNewHeader = (char*)ALLOC(*dataLen);
  pNewHeader = (char*)ALLOC((unsigned)*dataLen);
  pTmp = pNewHeader;

  while(p < (pData + *dataLen))
  {
    header = *(short*)p;
    dataSize = *(((short*)p)+1);

1997
1998
1999
2000
2001
2002
2003
2004

2005
2006
2007
1946
1947
1948
1949
1950
1951
1952

1953
1954
1955
1956







-
+



    *dataLen = size;

    retVal = ZIP_OK;
  }
  else
    retVal = ZIP_ERRNO;

  TRYFREE(pNewHeader);
  free(pNewHeader);

  return retVal;
}

Changes to compat/zlib/contrib/minizip/zip.h.

84
85
86
87
88
89
90
91
92
93
94
95
96






97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117


118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134

135
136
137
138
139
140




141
142

143
144
145

146





147
148
149
150
151
152
153
154
155
156










157
158
159
160
161
162
163
164
165
166
167
168











169
170
171
172
173
174
175

176
177

178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197











198
199
200
201
202
203
204
205
206
207
208
209
210
211












212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
















232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249

















250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276


















277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298



















299
300
301
302
303
304
305
306
307
308
309



310
311
312
313
314

315
316
317
318
319
320
321



322
323
324
325



326
327
328
329
330
331
332
333
334


335
336
337
338
339
340

341
342
343
344
345
346
347
84
85
86
87
88
89
90






91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115


116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133

134
135
136




137
138
139
140
141

142
143
144

145
146
147
148
149
150
151










152
153
154
155
156
157
158
159
160
161
162











163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179

180
181

182
183
184
185
186
187
188
189
190
191











192
193
194
195
196
197
198
199
200
201
202
203
204












205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
















221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237

















238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254

255
256
257
258
259
260
261
262


















263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280

281
282



















283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301

302
303
304
305
306
307
308



309
310
311
312
313
314
315

316
317
318
319
320



321
322
323
324



325
326
327
328
329
330
331
332
333
334


335
336
337
338
339
340
341

342
343
344
345
346
347
348
349







-
-
-
-
-
-
+
+
+
+
+
+



















-
-
+
+
















-
+


-
-
-
-
+
+
+
+

-
+


-
+

+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+

-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+






-
+

-
+









-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+


-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+




-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-








-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-


-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-







-
-
-
+
+
+




-
+




-
-
-
+
+
+

-
-
-
+
+
+







-
-
+
+





-
+







#  endif
#endif
/* default memLevel */

/* tm_zip contain date/time info */
typedef struct tm_zip_s
{
    uInt tm_sec;            /* seconds after the minute - [0,59] */
    uInt tm_min;            /* minutes after the hour - [0,59] */
    uInt tm_hour;           /* hours since midnight - [0,23] */
    uInt tm_mday;           /* day of the month - [1,31] */
    uInt tm_mon;            /* months since January - [0,11] */
    uInt tm_year;           /* years - [1980..2044] */
    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 OF((const char *pathname, int append));
extern zipFile ZEXPORT zipOpen64 OF((const void *pathname, int append));
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 couse, you can use RAW reading and writing to copy the file you did not want delte
   Of course, you can use RAW reading and writing to copy the file you did not want delete
*/

extern zipFile ZEXPORT zipOpen2 OF((const char *pathname,
                                   int append,
                                   zipcharpc* globalcomment,
                                   zlib_filefunc_def* pzlib_filefunc_def));
extern zipFile ZEXPORT zipOpen2(const char *pathname,
                                int append,
                                zipcharpc* globalcomment,
                                zlib_filefunc_def* pzlib_filefunc_def);

extern zipFile ZEXPORT zipOpen2_64 OF((const void *pathname,
extern zipFile ZEXPORT zipOpen2_64(const void *pathname,
                                   int append,
                                   zipcharpc* globalcomment,
                                   zlib_filefunc64_def* pzlib_filefunc_def));
                                   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 OF((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 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 OF((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));
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 the the local header
    contains the extrafield data for the local header
  if extrafield_global!=NULL and size_extrafield_global>0, extrafield_global
    contains the extrafield data the the local header
    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 OF((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(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 OF((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));
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 OF((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(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 OF((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
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 OF((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(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 OF((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
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 OF((zipFile file,
                       const void* buf,
                       unsigned len));
extern int ZEXPORT zipWriteInFileInZip(zipFile file,
                                       const void* buf,
                                       unsigned len);
/*
  Write data in the zipfile
*/

extern int ZEXPORT zipCloseFileInZip OF((zipFile file));
extern int ZEXPORT zipCloseFileInZip(zipFile file);
/*
  Close the current file in the zipfile
*/

extern int ZEXPORT zipCloseFileInZipRaw OF((zipFile file,
                                            uLong uncompressed_size,
                                            uLong crc32));
extern int ZEXPORT zipCloseFileInZipRaw(zipFile file,
                                        uLong uncompressed_size,
                                        uLong crc32);

extern int ZEXPORT zipCloseFileInZipRaw64 OF((zipFile file,
                                            ZPOS64_T 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 OF((zipFile file,
                const char* global_comment));
extern int ZEXPORT zipClose(zipFile file,
                            const char* global_comment);
/*
  Close the zipfile
*/


extern int ZEXPORT zipRemoveExtraInfoBlock OF((char* pData, int* dataLen, short sHeader));
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.

Changes to 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
1
2
3
4
5
6
7
8
9
10
11
12

13
14
15
16
17
18
19
20












-
+







(* 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.2.11';
  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;

Changes to compat/zlib/contrib/puff/README.

34
35
36
37
38
39
40
41

42
43
44
45
46
47
48
34
35
36
37
38
39
40

41
42
43
44
45
46
47
48







-
+







         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 readabilty.  puff.c
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.

Changes to compat/zlib/contrib/puff/puff.c.

39
40
41
42
43
44
45
46

47
48
49
50
51
52
53
39
40
41
42
43
44
45

46
47
48
49
50
51
52
53







-
+







 *                      - 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 readabilty
 *                        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
589
590
591
592
593
594
595
596
597
598
599




600
601
602
603
604
605
606
589
590
591
592
593
594
595




596
597
598
599
600
601
602
603
604
605
606







-
-
-
-
+
+
+
+







 *   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
 *   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.
 * - 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
620
621
622
623
624
625
626
627

628
629
630
631
632
633
634
620
621
622
623
624
625
626

627
628
629
630
631
632
633
634







-
+







 * - 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 follwed by extra bits to define the length of
 *   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).

Changes to compat/zlib/contrib/puff/pufftest.c.

139
140
141
142
143
144
145
146

147
148
149
150
151
152
153
139
140
141
142
143
144
145

146
147
148
149
150
151
152
153







-
+







        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 decompressd data to stdout */
    /* 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);

Changes to compat/zlib/contrib/testzlib/testzlib.c.

165
166
167
168
169
170
171
172

173
174
175
176
177
178
179
165
166
167
168
169
170
171

172
173
174
175
176
177
178
179







-
+







    }

    if (ReadFileMemory(argv[1],&lFileSize,&FilePtr)==0)
    {
        printf("error reading %s\n",argv[1]);
        return 1;
    }
    else printf("file %s read, %u bytes\n",argv[1],lFileSize);
    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]);

Changes to 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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

33


34
35




36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55






+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+










-
+
-
-


-
-
-
-
+










+
+







/*
 * 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 unix
#ifdef _WIN32
#  include <unistd.h>
#else
#  include <direct.h>
#  include <io.h>
#endif

#ifdef WIN32
#include <windows.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 */
98
99
100
101
102
103
104
105

106
107
108
109
110
111
112
113

114
115

116
117
118

119
120
121

122
123
124

125
126
127
128
129
130
131
132
111
112
113
114
115
116
117

118

119






120


121



122



123



124

125
126
127
128
129
130
131







-
+
-

-
-
-
-
-
-
+
-
-
+
-
-
-
+
-
-
-
+
-
-
-
+
-







  char              *fname;
  int                mode;
  time_t             time;
};

enum { TGZ_EXTRACT, TGZ_LIST, TGZ_INVALID };

char *TGZfname          OF((const char *));
char *prog;
void TGZnotfound        OF((const char *));

int getoct              OF((char *, int));
char *strtime           OF((time_t *));
int setfiletime         OF((char *, time_t));
void push_attr          OF((struct attr_item **, char *, int, time_t));
void restore_attr       OF((struct attr_item **));

void error(const char *msg)
int ExprMatch           OF((char *, char *));

{
int makedir             OF((char *));
int matchname           OF((int, int, char **, char *));

  fprintf(stderr, "%s: %s\n", prog, msg);
void error              OF((const char *));
int tar                 OF((gzFile, int, int, int, char **));

  exit(1);
void help               OF((int));
int main                OF((int, char **));

}
char *prog;

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)
201
202
203
204
205
206
207
208

209
210
211
212
213
214
215
200
201
202
203
204
205
206

207
208
209
210
211
212
213
214







-
+







}


/* set file time */

int setfiletime (char *fname,time_t ftime)
{
#ifdef WIN32
#ifdef _WIN32
  static int isWinNT = -1;
  SYSTEMTIME st;
  FILETIME locft, modft;
  struct tm *loctm;
  HANDLE hFile;
  int result;

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
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







-
-
-
-
-
-












-
+







  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);
}

void error(const char *msg)
{
  fprintf(stderr, "%s: %s\n", prog, msg);
  exit(1);
}


/* ============================================================ */

#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;
    gzFile      f;

    prog = strrchr(argv[0],'\\');
    if (prog == NULL)
      {
        prog = strrchr(argv[0],'/');
        if (prog == NULL)
          {

Changes to 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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19



20
21
22
23
24
25
26
-
+


















-
-
-







Building instructions for the DLL versions of Zlib 1.2.11
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
- Compile assembly code (with Visual Studio Command Prompt) by running:
   bld_ml64.bat (in contrib\masmx64)
   bld_ml32.bat (in contrib\masmx86)
- 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
38
39
40
41
42
43
44






45
46
47
48
49
50
51
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54







+
+
+
+
+
+







- 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.

70
71
72
73
74
75
76
77

78
73
74
75
76
77
78
79

80
81







-
+

  has a slightly different effect. To avoid compatibility problems, do
  not define it here.


Gilles Vollant
info@winimage.com

Visual Studio 2013 and 2015 Projects from Sean Hunt
Visual Studio 2013, 2015, and 2022 Projects from Sean Hunt
seandhunt_7@yahoo.com

Changes to compat/zlib/contrib/vstudio/vc10/miniunz.vcxproj.filters.

1
2
3
4
5
6

7
8
9
10
11
12
13
1
2
3
4
5

6
7
8
9
10
11
12
13





-
+







<?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;asm</Extensions>
      <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>

Changes to compat/zlib/contrib/vstudio/vc10/minizip.vcxproj.filters.

1
2
3
4
5
6

7
8
9
10
11
12
13
1
2
3
4
5

6
7
8
9
10
11
12
13





-
+







<?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;asm</Extensions>
      <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>

Changes to compat/zlib/contrib/vstudio/vc10/testzlib.vcxproj.

177
178
179
180
181
182
183
184

185
186
187
188
189
190
191
192
193
194
195
196
197

198
199
200
201
202
203
204
177
178
179
180
181
182
183

184
185
186
187
188
189
190
191
192
193
194
195
196

197
198
199
200
201
202
203
204







-
+












-
+







    <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>ASMV;ASMINF;WIN32;ZLIB_WINAPI;_DEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <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>..\..\masmx86\match686.obj;..\..\masmx86\inffas32.obj;%(AdditionalDependencies)</AdditionalDependencies>
      <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
      <OutputFile>$(OutDir)testzlib.exe</OutputFile>
      <GenerateDebugInformation>true</GenerateDebugInformation>
      <ProgramDatabaseFile>$(OutDir)testzlib.pdb</ProgramDatabaseFile>
      <SubSystem>Console</SubSystem>
      <RandomizedBaseAddress>false</RandomizedBaseAddress>
      <DataExecutionPrevention>
      </DataExecutionPrevention>
237
238
239
240
241
242
243
244

245
246
247
248
249
250
251
252
253
254
255
256
257

258
259
260
261
262
263
264
265
266
267
268
269
270
271
272

273
274
275
276
277
278
279

280
281
282
283
284
285
286
237
238
239
240
241
242
243

244
245
246
247
248
249
250
251
252
253
254
255
256

257
258
259
260
261
262
263
264
265
266
267
268
269
270
271

272
273
274
275
276
277
278

279
280
281
282
283
284
285
286







-
+












-
+














-
+






-
+







  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
    <ClCompile>
      <Optimization>MaxSpeed</Optimization>
      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
      <OmitFramePointers>true</OmitFramePointers>
      <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <PreprocessorDefinitions>ASMV;ASMINF;WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <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>..\..\masmx86\match686.obj;..\..\masmx86\inffas32.obj;%(AdditionalDependencies)</AdditionalDependencies>
      <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>ASMV;ASMINF;WIN32;ZLIB_WINAPI;_DEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <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>..\..\masmx64\gvmat64.obj;..\..\masmx64\inffasx64.obj;%(AdditionalDependencies)</AdditionalDependencies>
      <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
    </Link>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">
    <Midl>
      <TargetEnvironment>Itanium</TargetEnvironment>
    </Midl>
    <ClCompile>
348
349
350
351
352
353
354
355

356
357
358
359
360
361
362

363
364
365
366
367
368
369
348
349
350
351
352
353
354

355
356
357
358
359
360
361

362
363
364
365
366
367
368
369







-
+






-
+







      <EnableCOMDATFolding>true</EnableCOMDATFolding>
      <TargetMachine>MachineIA64</TargetMachine>
    </Link>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
    <ClCompile>
      <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <PreprocessorDefinitions>ASMV;ASMINF;WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <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>..\..\masmx64\gvmat64.obj;..\..\masmx64\inffasx64.obj;%(AdditionalDependencies)</AdditionalDependencies>
      <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
    </Link>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">
    <Midl>
      <TargetEnvironment>Itanium</TargetEnvironment>
    </Midl>
    <ClCompile>
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
394
395
396
397
398
399
400








401
402
403
404
405
406
407
408
409
410
411
412







-
-
-
-
-
-
-
-












  </ItemDefinitionGroup>
  <ItemGroup>
    <ClCompile Include="..\..\..\adler32.c" />
    <ClCompile Include="..\..\..\compress.c" />
    <ClCompile Include="..\..\..\crc32.c" />
    <ClCompile Include="..\..\..\deflate.c" />
    <ClCompile Include="..\..\..\infback.c" />
    <ClCompile Include="..\..\masmx64\inffas8664.c">
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
    </ClCompile>
    <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>

Changes to compat/zlib/contrib/vstudio/vc10/testzlib.vcxproj.filters.

1
2
3
4
5
6

7
8
9
10
11
12
13
1
2
3
4
5

6
7
8
9
10
11
12
13





-
+







<?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;asm</Extensions>
      <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>
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
26
27
28
29
30
31
32



33
34
35
36
37
38
39







-
-
-







    </ClCompile>
    <ClCompile Include="..\..\..\deflate.c">
      <Filter>Source Files</Filter>
    </ClCompile>
    <ClCompile Include="..\..\..\infback.c">
      <Filter>Source Files</Filter>
    </ClCompile>
    <ClCompile Include="..\..\masmx64\inffas8664.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">

Changes to compat/zlib/contrib/vstudio/vc10/testzlibdll.vcxproj.filters.

1
2
3
4
5
6

7
8
9
10
11
12
13
1
2
3
4
5

6
7
8
9
10
11
12
13





-
+







<?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;asm</Extensions>
      <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>

Changes to 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
1
2
3
4


5
6
7
8
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, 2, 11, 0
  PRODUCTVERSION 1, 2, 11, 0
  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.2.11\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-2017 Jean-loup Gailly & Mark Adler\0"
      VALUE "LegalCopyright", "(C) 1995-2024 Jean-loup Gailly & Mark Adler\0"
    END
  END
  BLOCK "VarFileInfo"
  BEGIN
    VALUE "Translation", 0x0409, 1252
  END
END

Changes to compat/zlib/contrib/vstudio/vc10/zlibstat.vcxproj.

156
157
158
159
160
161
162
163

164
165
166
167
168
169
170
156
157
158
159
160
161
162

163
164
165
166
167
168
169
170







-
+







    <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>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <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>
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194


195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213

214
215
216
217
218
219
220
221
222
223
224
225

226
227
228
229
230
231
232
178
179
180
181
182
183
184




185
186
187
188


189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208

209
210
211
212




213
214
215
216

217
218
219
220
221
222
223
224







-
-
-
-




-
-
+
+


















-
+



-
-
-
-




-
+







      <Culture>0x040c</Culture>
    </ResourceCompile>
    <Lib>
      <AdditionalOptions>/MACHINE:X86 /NODEFAULTLIB %(AdditionalOptions)</AdditionalOptions>
      <OutputFile>$(OutDir)zlibstat.lib</OutputFile>
      <SuppressStartupBanner>true</SuppressStartupBanner>
    </Lib>
    <PreBuildEvent>
      <Command>cd ..\..\masmx86
bld_ml32.bat</Command>
    </PreBuildEvent>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
    <ClCompile>
      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <PreprocessorDefinitions>WIN32;ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ASMV;ASMINF;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <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>..\..\masmx86\match686.obj;..\..\masmx86\inffas32.obj;%(AdditionalDependencies)</AdditionalDependencies>
      <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
      <OutputFile>$(OutDir)zlibstat.lib</OutputFile>
      <SuppressStartupBanner>true</SuppressStartupBanner>
    </Lib>
    <PreBuildEvent>
      <Command>cd ..\..\masmx86
bld_ml32.bat</Command>
    </PreBuildEvent>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">
    <ClCompile>
      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <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>
248
249
250
251
252
253
254
255

256
257
258
259
260
261
262
240
241
242
243
244
245
246

247
248
249
250
251
252
253
254







-
+







  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
    <Midl>
      <TargetEnvironment>X64</TargetEnvironment>
    </Midl>
    <ClCompile>
      <Optimization>Disabled</Optimization>
      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <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>
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288

289
290
291
292
293
294
295
262
263
264
265
266
267
268




269
270
271
272
273
274
275

276
277
278
279
280
281
282
283







-
-
-
-







-
+







      <Culture>0x040c</Culture>
    </ResourceCompile>
    <Lib>
      <AdditionalOptions>/MACHINE:AMD64 /NODEFAULTLIB %(AdditionalOptions)</AdditionalOptions>
      <OutputFile>$(OutDir)zlibstat.lib</OutputFile>
      <SuppressStartupBanner>true</SuppressStartupBanner>
    </Lib>
    <PreBuildEvent>
      <Command>cd ..\..\masmx64
bld_ml64.bat</Command>
    </PreBuildEvent>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">
    <Midl>
      <TargetEnvironment>Itanium</TargetEnvironment>
    </Midl>
    <ClCompile>
      <Optimization>Disabled</Optimization>
      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <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>
310
311
312
313
314
315
316
317
318


319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337

338
339
340
341
342
343
344
345
346
347
348
349
350
351
352

353
354
355
356
357
358
359
298
299
300
301
302
303
304


305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324

325
326
327
328




329
330
331
332
333
334
335

336
337
338
339
340
341
342
343







-
-
+
+


















-
+



-
-
-
-







-
+







  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
    <Midl>
      <TargetEnvironment>X64</TargetEnvironment>
    </Midl>
    <ClCompile>
      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <PreprocessorDefinitions>ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ASMV;ASMINF;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <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>..\..\masmx64\gvmat64.obj;..\..\masmx64\inffasx64.obj;%(AdditionalDependencies)</AdditionalDependencies>
      <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
      <OutputFile>$(OutDir)zlibstat.lib</OutputFile>
      <SuppressStartupBanner>true</SuppressStartupBanner>
    </Lib>
    <PreBuildEvent>
      <Command>cd ..\..\masmx64
bld_ml64.bat</Command>
    </PreBuildEvent>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">
    <Midl>
      <TargetEnvironment>Itanium</TargetEnvironment>
    </Midl>
    <ClCompile>
      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <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>
375
376
377
378
379
380
381
382

383
384
385
386
387
388
389
359
360
361
362
363
364
365

366
367
368
369
370
371
372
373







-
+







  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">
    <Midl>
      <TargetEnvironment>X64</TargetEnvironment>
    </Midl>
    <ClCompile>
      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <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>
405
406
407
408
409
410
411
412

413
414
415
416
417
418
419
389
390
391
392
393
394
395

396
397
398
399
400
401
402
403







-
+







  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">
    <Midl>
      <TargetEnvironment>Itanium</TargetEnvironment>
    </Midl>
    <ClCompile>
      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <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>
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
423
424
425
426
427
428
429








430
431
432
433
434
435
436







-
-
-
-
-
-
-
-







    <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="..\..\masmx64\inffas8664.c">
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
    </ClCompile>
    <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" />

Changes to compat/zlib/contrib/vstudio/vc10/zlibstat.vcxproj.filters.

29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
29
30
31
32
33
34
35



36
37
38
39
40
41
42







-
-
-







    </ClCompile>
    <ClCompile Include="..\..\..\gzwrite.c">
      <Filter>Source Files</Filter>
    </ClCompile>
    <ClCompile Include="..\..\..\infback.c">
      <Filter>Source Files</Filter>
    </ClCompile>
    <ClCompile Include="..\..\masmx64\inffas8664.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">

Changes to compat/zlib/contrib/vstudio/vc10/zlibvc.def.

1
2
3
4

5
6
7
8
9
10
11
1
2
3

4
5
6
7
8
9
10
11



-
+







LIBRARY
; zlib data compression and ZIP file I/O library

VERSION		1.2
VERSION		1.3.1

EXPORTS
        adler32                                  @1
        compress                                 @2
        crc32                                    @3
        deflate                                  @4
        deflateCopy                              @5
147
148
149
150
151
152
153





147
148
149
150
151
152
153
154
155
156
157
158







+
+
+
+
+
        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

Changes to compat/zlib/contrib/vstudio/vc10/zlibvc.vcxproj.

193
194
195
196
197
198
199
200
201


202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222

223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247

248
249
250
251
252
253
254
193
194
195
196
197
198
199


200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221

222
223
224
225
226
227
228
229
230
231




232
233
234
235
236
237
238
239
240
241
242

243
244
245
246
247
248
249
250







-
-
+
+




















-
+









-
-
-
-











-
+







      <MkTypLibCompatible>true</MkTypLibCompatible>
      <SuppressStartupBanner>true</SuppressStartupBanner>
      <TargetEnvironment>Win32</TargetEnvironment>
      <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName>
    </Midl>
    <ClCompile>
      <Optimization>Disabled</Optimization>
      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;ASMV;ASMINF;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <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>..\..\masmx86\match686.obj;..\..\masmx86\inffas32.obj;%(AdditionalDependencies)</AdditionalDependencies>
      <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>
    <PreBuildEvent>
      <Command>cd ..\..\masmx86
bld_ml32.bat</Command>
    </PreBuildEvent>
  </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>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <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>
284
285
286
287
288
289
290
291
292


293
294
295
296
297
298
299
280
281
282
283
284
285
286


287
288
289
290
291
292
293
294
295







-
-
+
+







      <MkTypLibCompatible>true</MkTypLibCompatible>
      <SuppressStartupBanner>true</SuppressStartupBanner>
      <TargetEnvironment>Win32</TargetEnvironment>
      <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName>
    </Midl>
    <ClCompile>
      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;ASMV;ASMINF;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <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>
308
309
310
311
312
313
314
315

316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341


342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361

362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384

385
386
387
388
389
390
391
304
305
306
307
308
309
310

311
312
313
314
315
316
317
318
319
320




321
322
323
324
325
326
327
328
329
330
331


332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352

353
354
355
356
357
358
359
360




361
362
363
364
365
366
367
368
369
370
371

372
373
374
375
376
377
378
379







-
+









-
-
-
-











-
-
+
+



















-
+







-
-
-
-











-
+







    </ClCompile>
    <ResourceCompile>
      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <Culture>0x040c</Culture>
    </ResourceCompile>
    <Link>
      <AdditionalOptions>/MACHINE:I386 %(AdditionalOptions)</AdditionalOptions>
      <AdditionalDependencies>..\..\masmx86\match686.obj;..\..\masmx86\inffas32.obj;%(AdditionalDependencies)</AdditionalDependencies>
      <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>
    <PreBuildEvent>
      <Command>cd ..\..\masmx86
bld_ml32.bat</Command>
    </PreBuildEvent>
  </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>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;ASMV;ASMINF;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <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>..\..\masmx64\gvmat64.obj;..\..\masmx64\inffasx64.obj;%(AdditionalDependencies)</AdditionalDependencies>
      <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
      <SuppressStartupBanner>true</SuppressStartupBanner>
      <ModuleDefinitionFile>.\zlibvc.def</ModuleDefinitionFile>
      <GenerateDebugInformation>true</GenerateDebugInformation>
      <GenerateMapFile>true</GenerateMapFile>
      <SubSystem>Windows</SubSystem>
      <TargetMachine>MachineX64</TargetMachine>
    </Link>
    <PreBuildEvent>
      <Command>cd ..\..\masmx64
bld_ml64.bat</Command>
    </PreBuildEvent>
  </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>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <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>
420
421
422
423
424
425
426
427

428
429
430
431
432
433
434
408
409
410
411
412
413
414

415
416
417
418
419
420
421
422







-
+







      <MkTypLibCompatible>true</MkTypLibCompatible>
      <SuppressStartupBanner>true</SuppressStartupBanner>
      <TargetEnvironment>X64</TargetEnvironment>
      <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName>
    </Midl>
    <ClCompile>
      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <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>
461
462
463
464
465
466
467
468

469
470
471
472
473
474
475
449
450
451
452
453
454
455

456
457
458
459
460
461
462
463







-
+







      <MkTypLibCompatible>true</MkTypLibCompatible>
      <SuppressStartupBanner>true</SuppressStartupBanner>
      <TargetEnvironment>Itanium</TargetEnvironment>
      <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName>
    </Midl>
    <ClCompile>
      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <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>
506
507
508
509
510
511
512
513
514


515
516
517
518
519
520
521
494
495
496
497
498
499
500


501
502
503
504
505
506
507
508
509







-
-
+
+







      <MkTypLibCompatible>true</MkTypLibCompatible>
      <SuppressStartupBanner>true</SuppressStartupBanner>
      <TargetEnvironment>X64</TargetEnvironment>
      <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName>
    </Midl>
    <ClCompile>
      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;ASMV;ASMINF;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <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>
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
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







-
+







-
-
-
-











-
+







      <SuppressStartupBanner>true</SuppressStartupBanner>
    </ClCompile>
    <ResourceCompile>
      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <Culture>0x040c</Culture>
    </ResourceCompile>
    <Link>
      <AdditionalDependencies>..\..\masmx64\gvmat64.obj;..\..\masmx64\inffasx64.obj;%(AdditionalDependencies)</AdditionalDependencies>
      <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
      <SuppressStartupBanner>true</SuppressStartupBanner>
      <IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
      <ModuleDefinitionFile>.\zlibvc.def</ModuleDefinitionFile>
      <GenerateMapFile>true</GenerateMapFile>
      <SubSystem>Windows</SubSystem>
      <TargetMachine>MachineX64</TargetMachine>
    </Link>
    <PreBuildEvent>
      <Command>cd ..\..\masmx64
bld_ml64.bat</Command>
    </PreBuildEvent>
  </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>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <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>
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
581
582
583
584
585
586
587








588
589
590
591
592
593
594







-
-
-
-
-
-
-
-







    <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="..\..\masmx64\inffas8664.c">
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
    </ClCompile>
    <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" />

Changes to compat/zlib/contrib/vstudio/vc10/zlibvc.vcxproj.filters.

38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
38
39
40
41
42
43
44



45
46
47
48
49
50
51







-
-
-







    </ClCompile>
    <ClCompile Include="..\..\..\gzwrite.c">
      <Filter>Source Files</Filter>
    </ClCompile>
    <ClCompile Include="..\..\..\infback.c">
      <Filter>Source Files</Filter>
    </ClCompile>
    <ClCompile Include="..\..\masmx64\inffas8664.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">

Changes to compat/zlib/contrib/vstudio/vc11/testzlib.vcxproj.

183
184
185
186
187
188
189
190

191
192
193
194
195
196
197
198
199
200
201
202
203

204
205
206
207
208
209
210
183
184
185
186
187
188
189

190
191
192
193
194
195
196
197
198
199
200
201
202

203
204
205
206
207
208
209
210







-
+












-
+







    <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>ASMV;ASMINF;WIN32;ZLIB_WINAPI;_DEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <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>..\..\masmx86\match686.obj;..\..\masmx86\inffas32.obj;%(AdditionalDependencies)</AdditionalDependencies>
      <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
      <OutputFile>$(OutDir)testzlib.exe</OutputFile>
      <GenerateDebugInformation>true</GenerateDebugInformation>
      <ProgramDatabaseFile>$(OutDir)testzlib.pdb</ProgramDatabaseFile>
      <SubSystem>Console</SubSystem>
      <RandomizedBaseAddress>false</RandomizedBaseAddress>
      <DataExecutionPrevention>
      </DataExecutionPrevention>
243
244
245
246
247
248
249
250

251
252
253
254
255
256
257
258
259
260
261
262
263

264
265
266
267
268
269
270
271
272
273
274
275
276
277
278

279
280
281
282
283
284
285

286
287
288
289
290
291
292
243
244
245
246
247
248
249

250
251
252
253
254
255
256
257
258
259
260
261
262

263
264
265
266
267
268
269
270
271
272
273
274
275
276
277

278
279
280
281
282
283
284

285
286
287
288
289
290
291
292







-
+












-
+














-
+






-
+







  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
    <ClCompile>
      <Optimization>MaxSpeed</Optimization>
      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
      <OmitFramePointers>true</OmitFramePointers>
      <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <PreprocessorDefinitions>ASMV;ASMINF;WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <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>..\..\masmx86\match686.obj;..\..\masmx86\inffas32.obj;%(AdditionalDependencies)</AdditionalDependencies>
      <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>ASMV;ASMINF;WIN32;ZLIB_WINAPI;_DEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <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>..\..\masmx64\gvmat64.obj;..\..\masmx64\inffasx64.obj;%(AdditionalDependencies)</AdditionalDependencies>
      <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
    </Link>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">
    <Midl>
      <TargetEnvironment>Itanium</TargetEnvironment>
    </Midl>
    <ClCompile>
354
355
356
357
358
359
360
361

362
363
364
365
366
367
368

369
370
371
372
373
374
375
354
355
356
357
358
359
360

361
362
363
364
365
366
367

368
369
370
371
372
373
374
375







-
+






-
+







      <EnableCOMDATFolding>true</EnableCOMDATFolding>
      <TargetMachine>MachineIA64</TargetMachine>
    </Link>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
    <ClCompile>
      <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <PreprocessorDefinitions>ASMV;ASMINF;WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <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>..\..\masmx64\gvmat64.obj;..\..\masmx64\inffasx64.obj;%(AdditionalDependencies)</AdditionalDependencies>
      <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
    </Link>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">
    <Midl>
      <TargetEnvironment>Itanium</TargetEnvironment>
    </Midl>
    <ClCompile>
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
400
401
402
403
404
405
406








407
408
409
410
411
412
413
414
415
416
417
418







-
-
-
-
-
-
-
-












  </ItemDefinitionGroup>
  <ItemGroup>
    <ClCompile Include="..\..\..\adler32.c" />
    <ClCompile Include="..\..\..\compress.c" />
    <ClCompile Include="..\..\..\crc32.c" />
    <ClCompile Include="..\..\..\deflate.c" />
    <ClCompile Include="..\..\..\infback.c" />
    <ClCompile Include="..\..\masmx64\inffas8664.c">
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
    </ClCompile>
    <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>

Changes to 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
1
2
3
4


5
6
7
8
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, 2, 11, 0
  PRODUCTVERSION 1, 2, 11, 0
  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.2.11\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-2017 Jean-loup Gailly & Mark Adler\0"
      VALUE "LegalCopyright", "(C) 1995-2024 Jean-loup Gailly & Mark Adler\0"
    END
  END
  BLOCK "VarFileInfo"
  BEGIN
    VALUE "Translation", 0x0409, 1252
  END
END

Changes to compat/zlib/contrib/vstudio/vc11/zlibstat.vcxproj.

163
164
165
166
167
168
169
170

171
172
173
174
175
176
177
163
164
165
166
167
168
169

170
171
172
173
174
175
176
177







-
+







    <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>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <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>
189
190
191
192
193
194
195
196
197


198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216

217
218
219
220
221
222
223
224

225
226
227
228
229
230
231
189
190
191
192
193
194
195


196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215

216
217
218
219
220
221
222
223

224
225
226
227
228
229
230
231







-
-
+
+


















-
+







-
+







      <OutputFile>$(OutDir)zlibstat.lib</OutputFile>
      <SuppressStartupBanner>true</SuppressStartupBanner>
    </Lib>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
    <ClCompile>
      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <PreprocessorDefinitions>WIN32;ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ASMV;ASMINF;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <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>..\..\masmx86\match686.obj;..\..\masmx86\inffas32.obj;%(AdditionalDependencies)</AdditionalDependencies>
      <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
      <OutputFile>$(OutDir)zlibstat.lib</OutputFile>
      <SuppressStartupBanner>true</SuppressStartupBanner>
    </Lib>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">
    <ClCompile>
      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <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>
247
248
249
250
251
252
253
254

255
256
257
258
259
260
261
247
248
249
250
251
252
253

254
255
256
257
258
259
260
261







-
+







  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
    <Midl>
      <TargetEnvironment>X64</TargetEnvironment>
    </Midl>
    <ClCompile>
      <Optimization>Disabled</Optimization>
      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <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>
276
277
278
279
280
281
282
283

284
285
286
287
288
289
290
276
277
278
279
280
281
282

283
284
285
286
287
288
289
290







-
+







  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">
    <Midl>
      <TargetEnvironment>Itanium</TargetEnvironment>
    </Midl>
    <ClCompile>
      <Optimization>Disabled</Optimization>
      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <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>
305
306
307
308
309
310
311
312
313


314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332

333
334
335
336
337
338
339
340
341
342
343

344
345
346
347
348
349
350
305
306
307
308
309
310
311


312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331

332
333
334
335
336
337
338
339
340
341
342

343
344
345
346
347
348
349
350







-
-
+
+


















-
+










-
+







  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
    <Midl>
      <TargetEnvironment>X64</TargetEnvironment>
    </Midl>
    <ClCompile>
      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <PreprocessorDefinitions>ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ASMV;ASMINF;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <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>..\..\masmx64\gvmat64.obj;..\..\masmx64\inffasx64.obj;%(AdditionalDependencies)</AdditionalDependencies>
      <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>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <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>
366
367
368
369
370
371
372
373

374
375
376
377
378
379
380
366
367
368
369
370
371
372

373
374
375
376
377
378
379
380







-
+







  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">
    <Midl>
      <TargetEnvironment>X64</TargetEnvironment>
    </Midl>
    <ClCompile>
      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <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>
396
397
398
399
400
401
402
403

404
405
406
407
408
409
410
396
397
398
399
400
401
402

403
404
405
406
407
408
409
410







-
+







  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">
    <Midl>
      <TargetEnvironment>Itanium</TargetEnvironment>
    </Midl>
    <ClCompile>
      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <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>
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
430
431
432
433
434
435
436








437
438
439
440
441
442
443







-
-
-
-
-
-
-
-







    <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="..\..\masmx64\inffas8664.c">
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
    </ClCompile>
    <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" />

Changes to compat/zlib/contrib/vstudio/vc11/zlibvc.def.

1
2
3
4

5
6
7
8
9
10
11
1
2
3

4
5
6
7
8
9
10
11



-
+







LIBRARY
; zlib data compression and ZIP file I/O library

VERSION		1.2
VERSION		1.3.1

EXPORTS
        adler32                                  @1
        compress                                 @2
        crc32                                    @3
        deflate                                  @4
        deflateCopy                              @5
147
148
149
150
151
152
153





147
148
149
150
151
152
153
154
155
156
157
158







+
+
+
+
+
        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

Changes to compat/zlib/contrib/vstudio/vc11/zlibvc.vcxproj.

200
201
202
203
204
205
206
207
208


209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229

230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258

259
260
261
262
263
264
265
200
201
202
203
204
205
206


207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228

229
230
231
232
233
234
235
236
237
238
239
240
241
242




243
244
245
246
247
248
249
250
251
252
253

254
255
256
257
258
259
260
261







-
-
+
+




















-
+













-
-
-
-











-
+







      <MkTypLibCompatible>true</MkTypLibCompatible>
      <SuppressStartupBanner>true</SuppressStartupBanner>
      <TargetEnvironment>Win32</TargetEnvironment>
      <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName>
    </Midl>
    <ClCompile>
      <Optimization>Disabled</Optimization>
      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;ASMV;ASMINF;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <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>..\..\masmx86\match686.obj;..\..\masmx86\inffas32.obj;%(AdditionalDependencies)</AdditionalDependencies>
      <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>
    <PreBuildEvent>
      <Command>cd ..\..\masmx86
bld_ml32.bat</Command>
    </PreBuildEvent>
  </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>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <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>
299
300
301
302
303
304
305
306
307


308
309
310
311
312
313
314
295
296
297
298
299
300
301


302
303
304
305
306
307
308
309
310







-
-
+
+







      <MkTypLibCompatible>true</MkTypLibCompatible>
      <SuppressStartupBanner>true</SuppressStartupBanner>
      <TargetEnvironment>Win32</TargetEnvironment>
      <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName>
    </Midl>
    <ClCompile>
      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;ASMV;ASMINF;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <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>
323
324
325
326
327
328
329
330

331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360


361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380

381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407

408
409
410
411
412
413
414
319
320
321
322
323
324
325

326
327
328
329
330
331
332
333
334
335
336
337
338
339




340
341
342
343
344
345
346
347
348
349
350


351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371

372
373
374
375
376
377
378
379
380
381
382
383




384
385
386
387
388
389
390
391
392
393
394

395
396
397
398
399
400
401
402







-
+













-
-
-
-











-
-
+
+



















-
+











-
-
-
-











-
+







    </ClCompile>
    <ResourceCompile>
      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <Culture>0x040c</Culture>
    </ResourceCompile>
    <Link>
      <AdditionalOptions>/MACHINE:I386 %(AdditionalOptions)</AdditionalOptions>
      <AdditionalDependencies>..\..\masmx86\match686.obj;..\..\masmx86\inffas32.obj;%(AdditionalDependencies)</AdditionalDependencies>
      <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>
    <PreBuildEvent>
      <Command>cd ..\..\masmx86
bld_ml32.bat</Command>
    </PreBuildEvent>
  </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>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;ASMV;ASMINF;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <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>..\..\masmx64\gvmat64.obj;..\..\masmx64\inffasx64.obj;%(AdditionalDependencies)</AdditionalDependencies>
      <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>
    <PreBuildEvent>
      <Command>cd ..\..\contrib\masmx64
bld_ml64.bat</Command>
    </PreBuildEvent>
  </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>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <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>
443
444
445
446
447
448
449
450

451
452
453
454
455
456
457
431
432
433
434
435
436
437

438
439
440
441
442
443
444
445







-
+







      <MkTypLibCompatible>true</MkTypLibCompatible>
      <SuppressStartupBanner>true</SuppressStartupBanner>
      <TargetEnvironment>X64</TargetEnvironment>
      <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName>
    </Midl>
    <ClCompile>
      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <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>
488
489
490
491
492
493
494
495

496
497
498
499
500
501
502
476
477
478
479
480
481
482

483
484
485
486
487
488
489
490







-
+







      <MkTypLibCompatible>true</MkTypLibCompatible>
      <SuppressStartupBanner>true</SuppressStartupBanner>
      <TargetEnvironment>Itanium</TargetEnvironment>
      <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName>
    </Midl>
    <ClCompile>
      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <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>
533
534
535
536
537
538
539
540
541


542
543
544
545
546
547
548
521
522
523
524
525
526
527


528
529
530
531
532
533
534
535
536







-
-
+
+







      <MkTypLibCompatible>true</MkTypLibCompatible>
      <SuppressStartupBanner>true</SuppressStartupBanner>
      <TargetEnvironment>X64</TargetEnvironment>
      <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName>
    </Midl>
    <ClCompile>
      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;ASMV;ASMINF;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <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>
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
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







-
+











-
-
-
-











-
+







      <SuppressStartupBanner>true</SuppressStartupBanner>
    </ClCompile>
    <ResourceCompile>
      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <Culture>0x040c</Culture>
    </ResourceCompile>
    <Link>
      <AdditionalDependencies>..\..\masmx64\gvmat64.obj;..\..\masmx64\inffasx64.obj;%(AdditionalDependencies)</AdditionalDependencies>
      <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>
    <PreBuildEvent>
      <Command>cd ..\..\masmx64
bld_ml64.bat</Command>
    </PreBuildEvent>
  </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>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <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>
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
612
613
614
615
616
617
618








619
620
621
622
623
624
625







-
-
-
-
-
-
-
-







    <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="..\..\masmx64\inffas8664.c">
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
    </ClCompile>
    <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" />

Changes to compat/zlib/contrib/vstudio/vc12/testzlib.vcxproj.

186
187
188
189
190
191
192
193

194
195
196
197
198
199
200
201
202
203
204
205
206

207
208
209
210
211
212
213
186
187
188
189
190
191
192

193
194
195
196
197
198
199
200
201
202
203
204
205

206
207
208
209
210
211
212
213







-
+












-
+







    <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>ASMV;ASMINF;WIN32;ZLIB_WINAPI;_DEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <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>..\..\masmx86\match686.obj;..\..\masmx86\inffas32.obj;%(AdditionalDependencies)</AdditionalDependencies>
      <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
      <OutputFile>$(OutDir)testzlib.exe</OutputFile>
      <GenerateDebugInformation>true</GenerateDebugInformation>
      <ProgramDatabaseFile>$(OutDir)testzlib.pdb</ProgramDatabaseFile>
      <SubSystem>Console</SubSystem>
      <RandomizedBaseAddress>false</RandomizedBaseAddress>
      <DataExecutionPrevention>
      </DataExecutionPrevention>
246
247
248
249
250
251
252
253

254
255
256
257
258
259
260
261
262
263
264
265
266

267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282

283
284
285
286
287
288
289

290
291
292
293
294
295
296
246
247
248
249
250
251
252

253
254
255
256
257
258
259
260
261
262
263
264
265

266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281

282
283
284
285
286
287
288

289
290
291
292
293
294
295
296







-
+












-
+















-
+






-
+







  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
    <ClCompile>
      <Optimization>MaxSpeed</Optimization>
      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
      <OmitFramePointers>true</OmitFramePointers>
      <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <PreprocessorDefinitions>ASMV;ASMINF;WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <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>..\..\masmx86\match686.obj;..\..\masmx86\inffas32.obj;%(AdditionalDependencies)</AdditionalDependencies>
      <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>ASMV;ASMINF;WIN32;ZLIB_WINAPI;_DEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <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>..\..\masmx64\gvmat64.obj;..\..\masmx64\inffasx64.obj;%(AdditionalDependencies)</AdditionalDependencies>
      <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
    </Link>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">
    <Midl>
      <TargetEnvironment>Itanium</TargetEnvironment>
    </Midl>
    <ClCompile>
358
359
360
361
362
363
364
365

366
367
368
369
370
371
372

373
374
375
376
377
378
379
358
359
360
361
362
363
364

365
366
367
368
369
370
371

372
373
374
375
376
377
378
379







-
+






-
+







      <EnableCOMDATFolding>true</EnableCOMDATFolding>
      <TargetMachine>MachineIA64</TargetMachine>
    </Link>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
    <ClCompile>
      <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <PreprocessorDefinitions>ASMV;ASMINF;WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <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>..\..\masmx64\gvmat64.obj;..\..\masmx64\inffasx64.obj;%(AdditionalDependencies)</AdditionalDependencies>
      <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
    </Link>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">
    <Midl>
      <TargetEnvironment>Itanium</TargetEnvironment>
    </Midl>
    <ClCompile>
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
404
405
406
407
408
409
410








411
412
413
414
415
416
417
418
419
420
421
422







-
-
-
-
-
-
-
-












  </ItemDefinitionGroup>
  <ItemGroup>
    <ClCompile Include="..\..\..\adler32.c" />
    <ClCompile Include="..\..\..\compress.c" />
    <ClCompile Include="..\..\..\crc32.c" />
    <ClCompile Include="..\..\..\deflate.c" />
    <ClCompile Include="..\..\..\infback.c" />
    <ClCompile Include="..\..\masmx64\inffas8664.c">
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
    </ClCompile>
    <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>

Changes to 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
1
2
3
4


5
6
7
8
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, 2, 11, 0
  PRODUCTVERSION 1, 2, 11, 0
  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.2.11\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-2017 Jean-loup Gailly & Mark Adler\0"
      VALUE "LegalCopyright", "(C) 1995-2024 Jean-loup Gailly & Mark Adler\0"
    END
  END
  BLOCK "VarFileInfo"
  BEGIN
    VALUE "Translation", 0x0409, 1252
  END
END

Changes to compat/zlib/contrib/vstudio/vc12/zlibstat.vcxproj.

166
167
168
169
170
171
172
173

174
175
176
177
178
179
180
166
167
168
169
170
171
172

173
174
175
176
177
178
179
180







-
+







    <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>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <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>
192
193
194
195
196
197
198
199
200


201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219

220
221
222
223
224
225
226
227

228
229
230
231
232
233
234
192
193
194
195
196
197
198


199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218

219
220
221
222
223
224
225
226

227
228
229
230
231
232
233
234







-
-
+
+


















-
+







-
+







      <OutputFile>$(OutDir)zlibstat.lib</OutputFile>
      <SuppressStartupBanner>true</SuppressStartupBanner>
    </Lib>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
    <ClCompile>
      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <PreprocessorDefinitions>WIN32;ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ASMV;ASMINF;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <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>..\..\masmx86\match686.obj;..\..\masmx86\inffas32.obj;%(AdditionalDependencies)</AdditionalDependencies>
      <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
      <OutputFile>$(OutDir)zlibstat.lib</OutputFile>
      <SuppressStartupBanner>true</SuppressStartupBanner>
    </Lib>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">
    <ClCompile>
      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <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>
250
251
252
253
254
255
256
257

258
259
260
261
262
263
264
250
251
252
253
254
255
256

257
258
259
260
261
262
263
264







-
+







  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
    <Midl>
      <TargetEnvironment>X64</TargetEnvironment>
    </Midl>
    <ClCompile>
      <Optimization>Disabled</Optimization>
      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <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>
279
280
281
282
283
284
285
286

287
288
289
290
291
292
293
279
280
281
282
283
284
285

286
287
288
289
290
291
292
293







-
+







  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">
    <Midl>
      <TargetEnvironment>Itanium</TargetEnvironment>
    </Midl>
    <ClCompile>
      <Optimization>Disabled</Optimization>
      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <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>
308
309
310
311
312
313
314
315
316


317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335

336
337
338
339
340
341
342
343
344
345
346

347
348
349
350
351
352
353
308
309
310
311
312
313
314


315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334

335
336
337
338
339
340
341
342
343
344
345

346
347
348
349
350
351
352
353







-
-
+
+


















-
+










-
+







  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
    <Midl>
      <TargetEnvironment>X64</TargetEnvironment>
    </Midl>
    <ClCompile>
      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <PreprocessorDefinitions>ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ASMV;ASMINF;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <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>..\..\masmx64\gvmat64.obj;..\..\masmx64\inffasx64.obj;%(AdditionalDependencies)</AdditionalDependencies>
      <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>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <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>
369
370
371
372
373
374
375
376

377
378
379
380
381
382
383
369
370
371
372
373
374
375

376
377
378
379
380
381
382
383







-
+







  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">
    <Midl>
      <TargetEnvironment>X64</TargetEnvironment>
    </Midl>
    <ClCompile>
      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <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>
399
400
401
402
403
404
405
406

407
408
409
410
411
412
413
399
400
401
402
403
404
405

406
407
408
409
410
411
412
413







-
+







  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">
    <Midl>
      <TargetEnvironment>Itanium</TargetEnvironment>
    </Midl>
    <ClCompile>
      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <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>
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
433
434
435
436
437
438
439








440
441
442
443
444
445
446







-
-
-
-
-
-
-
-







    <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="..\..\masmx64\inffas8664.c">
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
    </ClCompile>
    <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" />

Changes to compat/zlib/contrib/vstudio/vc12/zlibvc.def.

1
2
3
4

5
6
7
8
9
10
11
1
2
3

4
5
6
7
8
9
10
11



-
+







LIBRARY
; zlib data compression and ZIP file I/O library

VERSION		1.2
VERSION		1.3.1

EXPORTS
        adler32                                  @1
        compress                                 @2
        crc32                                    @3
        deflate                                  @4
        deflateCopy                              @5
147
148
149
150
151
152
153





147
148
149
150
151
152
153
154
155
156
157
158







+
+
+
+
+
        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

Changes to compat/zlib/contrib/vstudio/vc12/zlibvc.vcxproj.

203
204
205
206
207
208
209
210
211


212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232

233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261

262
263
264
265
266
267
268
203
204
205
206
207
208
209


210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231

232
233
234
235
236
237
238
239
240
241
242
243
244
245




246
247
248
249
250
251
252
253
254
255
256

257
258
259
260
261
262
263
264







-
-
+
+




















-
+













-
-
-
-











-
+







      <MkTypLibCompatible>true</MkTypLibCompatible>
      <SuppressStartupBanner>true</SuppressStartupBanner>
      <TargetEnvironment>Win32</TargetEnvironment>
      <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName>
    </Midl>
    <ClCompile>
      <Optimization>Disabled</Optimization>
      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;ASMV;ASMINF;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <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>..\..\masmx86\match686.obj;..\..\masmx86\inffas32.obj;%(AdditionalDependencies)</AdditionalDependencies>
      <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>
    <PreBuildEvent>
      <Command>cd ..\..\masmx86
bld_ml32.bat</Command>
    </PreBuildEvent>
  </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>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <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>
302
303
304
305
306
307
308
309
310


311
312
313
314
315
316
317
298
299
300
301
302
303
304


305
306
307
308
309
310
311
312
313







-
-
+
+







      <MkTypLibCompatible>true</MkTypLibCompatible>
      <SuppressStartupBanner>true</SuppressStartupBanner>
      <TargetEnvironment>Win32</TargetEnvironment>
      <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName>
    </Midl>
    <ClCompile>
      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;ASMV;ASMINF;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <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>
326
327
328
329
330
331
332
333

334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364


365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384

385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411

412
413
414
415
416
417
418
322
323
324
325
326
327
328

329
330
331
332
333
334
335
336
337
338
339
340
341
342
343




344
345
346
347
348
349
350
351
352
353
354


355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375

376
377
378
379
380
381
382
383
384
385
386
387




388
389
390
391
392
393
394
395
396
397
398

399
400
401
402
403
404
405
406







-
+














-
-
-
-











-
-
+
+



















-
+











-
-
-
-











-
+







    </ClCompile>
    <ResourceCompile>
      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <Culture>0x040c</Culture>
    </ResourceCompile>
    <Link>
      <AdditionalOptions>/MACHINE:I386 %(AdditionalOptions)</AdditionalOptions>
      <AdditionalDependencies>..\..\masmx86\match686.obj;..\..\masmx86\inffas32.obj;%(AdditionalDependencies)</AdditionalDependencies>
      <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>
    <PreBuildEvent>
      <Command>cd ..\..\masmx86
bld_ml32.bat</Command>
    </PreBuildEvent>
  </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>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;ASMV;ASMINF;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <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>..\..\masmx64\gvmat64.obj;..\..\masmx64\inffasx64.obj;%(AdditionalDependencies)</AdditionalDependencies>
      <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>
    <PreBuildEvent>
      <Command>cd ..\..\contrib\masmx64
bld_ml64.bat</Command>
    </PreBuildEvent>
  </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>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <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>
447
448
449
450
451
452
453
454

455
456
457
458
459
460
461
435
436
437
438
439
440
441

442
443
444
445
446
447
448
449







-
+







      <MkTypLibCompatible>true</MkTypLibCompatible>
      <SuppressStartupBanner>true</SuppressStartupBanner>
      <TargetEnvironment>X64</TargetEnvironment>
      <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName>
    </Midl>
    <ClCompile>
      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <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>
492
493
494
495
496
497
498
499

500
501
502
503
504
505
506
480
481
482
483
484
485
486

487
488
489
490
491
492
493
494







-
+







      <MkTypLibCompatible>true</MkTypLibCompatible>
      <SuppressStartupBanner>true</SuppressStartupBanner>
      <TargetEnvironment>Itanium</TargetEnvironment>
      <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName>
    </Midl>
    <ClCompile>
      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <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>
537
538
539
540
541
542
543
544
545


546
547
548
549
550
551
552
525
526
527
528
529
530
531


532
533
534
535
536
537
538
539
540







-
-
+
+







      <MkTypLibCompatible>true</MkTypLibCompatible>
      <SuppressStartupBanner>true</SuppressStartupBanner>
      <TargetEnvironment>X64</TargetEnvironment>
      <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName>
    </Midl>
    <ClCompile>
      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;ASMV;ASMINF;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <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>
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
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







-
+











-
-
-
-











-
+







      <SuppressStartupBanner>true</SuppressStartupBanner>
    </ClCompile>
    <ResourceCompile>
      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <Culture>0x040c</Culture>
    </ResourceCompile>
    <Link>
      <AdditionalDependencies>..\..\masmx64\gvmat64.obj;..\..\masmx64\inffasx64.obj;%(AdditionalDependencies)</AdditionalDependencies>
      <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>
    <PreBuildEvent>
      <Command>cd ..\..\masmx64
bld_ml64.bat</Command>
    </PreBuildEvent>
  </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>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <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>
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
616
617
618
619
620
621
622








623
624
625
626
627
628
629







-
-
-
-
-
-
-
-







    <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="..\..\masmx64\inffas8664.c">
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
    </ClCompile>
    <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" />

Changes to compat/zlib/contrib/vstudio/vc14/testzlib.vcxproj.

186
187
188
189
190
191
192
193

194
195
196
197
198
199
200
201
202
203
204
205
206

207
208
209
210
211
212
213
186
187
188
189
190
191
192

193
194
195
196
197
198
199
200
201
202
203
204
205

206
207
208
209
210
211
212
213







-
+












-
+







    <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>ASMV;ASMINF;WIN32;ZLIB_WINAPI;_DEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <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>..\..\masmx86\match686.obj;..\..\masmx86\inffas32.obj;%(AdditionalDependencies)</AdditionalDependencies>
      <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
      <OutputFile>$(OutDir)testzlib.exe</OutputFile>
      <GenerateDebugInformation>true</GenerateDebugInformation>
      <ProgramDatabaseFile>$(OutDir)testzlib.pdb</ProgramDatabaseFile>
      <SubSystem>Console</SubSystem>
      <RandomizedBaseAddress>false</RandomizedBaseAddress>
      <DataExecutionPrevention>
      </DataExecutionPrevention>
246
247
248
249
250
251
252
253

254
255
256
257
258
259
260
261
262
263
264
265
266

267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282

283
284
285
286
287
288
289

290
291
292
293
294
295
296
246
247
248
249
250
251
252

253
254
255
256
257
258
259
260
261
262
263
264
265

266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281

282
283
284
285
286
287
288

289
290
291
292
293
294
295
296







-
+












-
+















-
+






-
+







  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
    <ClCompile>
      <Optimization>MaxSpeed</Optimization>
      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
      <OmitFramePointers>true</OmitFramePointers>
      <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <PreprocessorDefinitions>ASMV;ASMINF;WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <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>..\..\masmx86\match686.obj;..\..\masmx86\inffas32.obj;%(AdditionalDependencies)</AdditionalDependencies>
      <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>ASMV;ASMINF;WIN32;ZLIB_WINAPI;_DEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <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>..\..\masmx64\gvmat64.obj;..\..\masmx64\inffasx64.obj;%(AdditionalDependencies)</AdditionalDependencies>
      <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
    </Link>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">
    <Midl>
      <TargetEnvironment>Itanium</TargetEnvironment>
    </Midl>
    <ClCompile>
358
359
360
361
362
363
364
365

366
367
368
369
370
371
372

373
374
375
376
377
378
379
358
359
360
361
362
363
364

365
366
367
368
369
370
371

372
373
374
375
376
377
378
379







-
+






-
+







      <EnableCOMDATFolding>true</EnableCOMDATFolding>
      <TargetMachine>MachineIA64</TargetMachine>
    </Link>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
    <ClCompile>
      <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <PreprocessorDefinitions>ASMV;ASMINF;WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <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>..\..\masmx64\gvmat64.obj;..\..\masmx64\inffasx64.obj;%(AdditionalDependencies)</AdditionalDependencies>
      <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
    </Link>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">
    <Midl>
      <TargetEnvironment>Itanium</TargetEnvironment>
    </Midl>
    <ClCompile>
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
404
405
406
407
408
409
410








411
412
413
414
415
416
417
418
419
420
421
422







-
-
-
-
-
-
-
-












  </ItemDefinitionGroup>
  <ItemGroup>
    <ClCompile Include="..\..\..\adler32.c" />
    <ClCompile Include="..\..\..\compress.c" />
    <ClCompile Include="..\..\..\crc32.c" />
    <ClCompile Include="..\..\..\deflate.c" />
    <ClCompile Include="..\..\..\infback.c" />
    <ClCompile Include="..\..\masmx64\inffas8664.c">
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
    </ClCompile>
    <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>

Changes to 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
1
2
3
4


5
6
7
8
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, 2, 11, 0
  PRODUCTVERSION 1, 2, 11, 0
  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.2.11\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-2017 Jean-loup Gailly & Mark Adler\0"
      VALUE "LegalCopyright", "(C) 1995-2024 Jean-loup Gailly & Mark Adler\0"
    END
  END
  BLOCK "VarFileInfo"
  BEGIN
    VALUE "Translation", 0x0409, 1252
  END
END

Changes to compat/zlib/contrib/vstudio/vc14/zlibstat.vcxproj.

166
167
168
169
170
171
172
173

174
175
176
177
178
179
180
166
167
168
169
170
171
172

173
174
175
176
177
178
179
180







-
+







    <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>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <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>
192
193
194
195
196
197
198
199
200


201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219

220
221
222
223
224
225
226
227

228
229
230
231
232
233
234
192
193
194
195
196
197
198


199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218

219
220
221
222
223
224
225
226

227
228
229
230
231
232
233
234







-
-
+
+


















-
+







-
+







      <OutputFile>$(OutDir)zlibstat.lib</OutputFile>
      <SuppressStartupBanner>true</SuppressStartupBanner>
    </Lib>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
    <ClCompile>
      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <PreprocessorDefinitions>WIN32;ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ASMV;ASMINF;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <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>..\..\masmx86\match686.obj;..\..\masmx86\inffas32.obj;%(AdditionalDependencies)</AdditionalDependencies>
      <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
      <OutputFile>$(OutDir)zlibstat.lib</OutputFile>
      <SuppressStartupBanner>true</SuppressStartupBanner>
    </Lib>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">
    <ClCompile>
      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <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>
250
251
252
253
254
255
256
257

258
259
260
261
262
263
264
250
251
252
253
254
255
256

257
258
259
260
261
262
263
264







-
+







  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
    <Midl>
      <TargetEnvironment>X64</TargetEnvironment>
    </Midl>
    <ClCompile>
      <Optimization>Disabled</Optimization>
      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <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>
279
280
281
282
283
284
285
286

287
288
289
290
291
292
293
279
280
281
282
283
284
285

286
287
288
289
290
291
292
293







-
+







  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">
    <Midl>
      <TargetEnvironment>Itanium</TargetEnvironment>
    </Midl>
    <ClCompile>
      <Optimization>Disabled</Optimization>
      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <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>
308
309
310
311
312
313
314
315
316


317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335

336
337
338
339
340
341
342
343
344
345
346

347
348
349
350
351
352
353
308
309
310
311
312
313
314


315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334

335
336
337
338
339
340
341
342
343
344
345

346
347
348
349
350
351
352
353







-
-
+
+


















-
+










-
+







  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
    <Midl>
      <TargetEnvironment>X64</TargetEnvironment>
    </Midl>
    <ClCompile>
      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <PreprocessorDefinitions>ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ASMV;ASMINF;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <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>..\..\masmx64\gvmat64.obj;..\..\masmx64\inffasx64.obj;%(AdditionalDependencies)</AdditionalDependencies>
      <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>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <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>
369
370
371
372
373
374
375
376

377
378
379
380
381
382
383
369
370
371
372
373
374
375

376
377
378
379
380
381
382
383







-
+







  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">
    <Midl>
      <TargetEnvironment>X64</TargetEnvironment>
    </Midl>
    <ClCompile>
      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <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>
399
400
401
402
403
404
405
406

407
408
409
410
411
412
413
399
400
401
402
403
404
405

406
407
408
409
410
411
412
413







-
+







  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">
    <Midl>
      <TargetEnvironment>Itanium</TargetEnvironment>
    </Midl>
    <ClCompile>
      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <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>
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
433
434
435
436
437
438
439








440
441
442
443
444
445
446







-
-
-
-
-
-
-
-







    <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="..\..\masmx64\inffas8664.c">
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
    </ClCompile>
    <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" />

Changes to compat/zlib/contrib/vstudio/vc14/zlibvc.def.

1
2
3
4

5
6
7
8
9
10
11
1
2
3

4
5
6
7
8
9
10
11



-
+







LIBRARY
; zlib data compression and ZIP file I/O library

VERSION		1.2
VERSION		1.3.1

EXPORTS
        adler32                                  @1
        compress                                 @2
        crc32                                    @3
        deflate                                  @4
        deflateCopy                              @5
147
148
149
150
151
152
153





147
148
149
150
151
152
153
154
155
156
157
158







+
+
+
+
+
        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

Changes to compat/zlib/contrib/vstudio/vc14/zlibvc.vcxproj.

203
204
205
206
207
208
209
210
211


212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232

233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261

262
263
264
265
266
267
268
203
204
205
206
207
208
209


210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231

232
233
234
235
236
237
238
239
240
241
242
243
244
245




246
247
248
249
250
251
252
253
254
255
256

257
258
259
260
261
262
263
264







-
-
+
+




















-
+













-
-
-
-











-
+







      <MkTypLibCompatible>true</MkTypLibCompatible>
      <SuppressStartupBanner>true</SuppressStartupBanner>
      <TargetEnvironment>Win32</TargetEnvironment>
      <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName>
    </Midl>
    <ClCompile>
      <Optimization>Disabled</Optimization>
      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;ASMV;ASMINF;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <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>..\..\masmx86\match686.obj;..\..\masmx86\inffas32.obj;%(AdditionalDependencies)</AdditionalDependencies>
      <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>
    <PreBuildEvent>
      <Command>cd ..\..\masmx86
bld_ml32.bat</Command>
    </PreBuildEvent>
  </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>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <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>
302
303
304
305
306
307
308
309
310


311
312
313
314
315
316
317
298
299
300
301
302
303
304


305
306
307
308
309
310
311
312
313







-
-
+
+







      <MkTypLibCompatible>true</MkTypLibCompatible>
      <SuppressStartupBanner>true</SuppressStartupBanner>
      <TargetEnvironment>Win32</TargetEnvironment>
      <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName>
    </Midl>
    <ClCompile>
      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;ASMV;ASMINF;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <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>
326
327
328
329
330
331
332
333

334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364


365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384

385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411

412
413
414
415
416
417
418
322
323
324
325
326
327
328

329
330
331
332
333
334
335
336
337
338
339
340
341
342
343




344
345
346
347
348
349
350
351
352
353
354


355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375

376
377
378
379
380
381
382
383
384
385
386
387




388
389
390
391
392
393
394
395
396
397
398

399
400
401
402
403
404
405
406







-
+














-
-
-
-











-
-
+
+



















-
+











-
-
-
-











-
+







    </ClCompile>
    <ResourceCompile>
      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <Culture>0x040c</Culture>
    </ResourceCompile>
    <Link>
      <AdditionalOptions>/MACHINE:I386 %(AdditionalOptions)</AdditionalOptions>
      <AdditionalDependencies>..\..\masmx86\match686.obj;..\..\masmx86\inffas32.obj;%(AdditionalDependencies)</AdditionalDependencies>
      <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>
    <PreBuildEvent>
      <Command>cd ..\..\masmx86
bld_ml32.bat</Command>
    </PreBuildEvent>
  </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>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;ASMV;ASMINF;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <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>..\..\masmx64\gvmat64.obj;..\..\masmx64\inffasx64.obj;%(AdditionalDependencies)</AdditionalDependencies>
      <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>
    <PreBuildEvent>
      <Command>cd ..\..\contrib\masmx64
bld_ml64.bat</Command>
    </PreBuildEvent>
  </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>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <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>
447
448
449
450
451
452
453
454

455
456
457
458
459
460
461
435
436
437
438
439
440
441

442
443
444
445
446
447
448
449







-
+







      <MkTypLibCompatible>true</MkTypLibCompatible>
      <SuppressStartupBanner>true</SuppressStartupBanner>
      <TargetEnvironment>X64</TargetEnvironment>
      <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName>
    </Midl>
    <ClCompile>
      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <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>
492
493
494
495
496
497
498
499

500
501
502
503
504
505
506
480
481
482
483
484
485
486

487
488
489
490
491
492
493
494







-
+







      <MkTypLibCompatible>true</MkTypLibCompatible>
      <SuppressStartupBanner>true</SuppressStartupBanner>
      <TargetEnvironment>Itanium</TargetEnvironment>
      <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName>
    </Midl>
    <ClCompile>
      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <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>
537
538
539
540
541
542
543
544
545


546
547
548
549
550
551
552
525
526
527
528
529
530
531


532
533
534
535
536
537
538
539
540







-
-
+
+







      <MkTypLibCompatible>true</MkTypLibCompatible>
      <SuppressStartupBanner>true</SuppressStartupBanner>
      <TargetEnvironment>X64</TargetEnvironment>
      <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName>
    </Midl>
    <ClCompile>
      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
      <AdditionalIncludeDirectories>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;ASMV;ASMINF;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <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>
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
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







-
+











-
-
-
-











-
+







      <SuppressStartupBanner>true</SuppressStartupBanner>
    </ClCompile>
    <ResourceCompile>
      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <Culture>0x040c</Culture>
    </ResourceCompile>
    <Link>
      <AdditionalDependencies>..\..\masmx64\gvmat64.obj;..\..\masmx64\inffasx64.obj;%(AdditionalDependencies)</AdditionalDependencies>
      <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>
    <PreBuildEvent>
      <Command>cd ..\..\masmx64
bld_ml64.bat</Command>
    </PreBuildEvent>
  </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>..\..\..;..\..\masmx86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
      <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>
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
616
617
618
619
620
621
622








623
624
625
626
627
628
629







-
-
-
-
-
-
-
-







    <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="..\..\masmx64\inffas8664.c">
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">true</ExcludedFromBuild>
      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
    </ClCompile>
    <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" />

Changes to compat/zlib/contrib/vstudio/vc9/miniunz.vcproj.

538
539
540
541
542
543
544
545

546
547
548
549
550
551
552
538
539
540
541
542
543
544

545
546
547
548
549
550
551
552







-
+







		</Configuration>
	</Configurations>
	<References>
	</References>
	<Files>
		<Filter
			Name="Source Files"
			Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm"
			Filter="cpp;c;cxx;def;odl;idl;hpj;bat"
			>
			<File
				RelativePath="..\..\minizip\miniunz.c"
				>
			</File>
		</Filter>
		<Filter

Changes to compat/zlib/contrib/vstudio/vc9/minizip.vcproj.

535
536
537
538
539
540
541
542

543
544
545
546
547
548
549
535
536
537
538
539
540
541

542
543
544
545
546
547
548
549







-
+







		</Configuration>
	</Configurations>
	<References>
	</References>
	<Files>
		<Filter
			Name="Source Files"
			Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm"
			Filter="cpp;c;cxx;def;odl;idl;hpj;bat"
			>
			<File
				RelativePath="..\..\minizip\minizip.c"
				>
			</File>
		</Filter>
		<Filter

Changes to compat/zlib/contrib/vstudio/vc9/testzlib.vcproj.

44
45
46
47
48
49
50
51

52
53
54
55
56
57
58
44
45
46
47
48
49
50

51
52
53
54
55
56
57
58







-
+







			<Tool
				Name="VCMIDLTool"
			/>
			<Tool
				Name="VCCLCompilerTool"
				Optimization="0"
				AdditionalIncludeDirectories="..\..\.."
				PreprocessorDefinitions="ASMV;ASMINF;WIN32;ZLIB_WINAPI;_DEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS"
				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)\"
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
67
68
69
70
71
72
73

74
75
76
77
78
79
80







-







				Name="VCResourceCompilerTool"
			/>
			<Tool
				Name="VCPreLinkEventTool"
			/>
			<Tool
				Name="VCLinkerTool"
				AdditionalDependencies="..\..\masmx86\match686.obj ..\..\masmx86\inffas32.obj"
				OutputFile="$(OutDir)/testzlib.exe"
				LinkIncremental="2"
				GenerateManifest="false"
				GenerateDebugInformation="true"
				ProgramDatabaseFile="$(OutDir)/testzlib.pdb"
				SubSystem="1"
				RandomizedBaseAddress="1"
124
125
126
127
128
129
130
131

132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
123
124
125
126
127
128
129

130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146

147
148
149
150
151
152
153







-
+
















-







			/>
			<Tool
				Name="VCMIDLTool"
			/>
			<Tool
				Name="VCCLCompilerTool"
				AdditionalIncludeDirectories="..\..\.."
				PreprocessorDefinitions="ASMV;ASMINF;WIN32;ZLIB_WINAPI;_DEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS"
				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"
				AdditionalDependencies="..\..\masmx64\gvmat64.obj ..\..\masmx64\inffasx64.obj"
				GenerateManifest="false"
			/>
			<Tool
				Name="VCALinkTool"
			/>
			<Tool
				Name="VCManifestTool"
513
514
515
516
517
518
519
520

521
522
523
524
525
526
527
511
512
513
514
515
516
517

518
519
520
521
522
523
524
525







-
+







			/>
			<Tool
				Name="VCCLCompilerTool"
				Optimization="2"
				InlineFunctionExpansion="1"
				OmitFramePointers="true"
				AdditionalIncludeDirectories="..\..\.."
				PreprocessorDefinitions="ASMV;ASMINF;WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS"
				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)\"
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
534
535
536
537
538
539
540

541
542
543
544
545
546
547







-







				Name="VCResourceCompilerTool"
			/>
			<Tool
				Name="VCPreLinkEventTool"
			/>
			<Tool
				Name="VCLinkerTool"
				AdditionalDependencies="..\..\masmx86\match686.obj ..\..\masmx86\inffas32.obj"
				OutputFile="$(OutDir)/testzlib.exe"
				LinkIncremental="1"
				GenerateManifest="false"
				GenerateDebugInformation="true"
				SubSystem="1"
				OptimizeReferences="2"
				EnableCOMDATFolding="2"
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
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







-
+
















-







			/>
			<Tool
				Name="VCMIDLTool"
			/>
			<Tool
				Name="VCCLCompilerTool"
				AdditionalIncludeDirectories="..\..\.."
				PreprocessorDefinitions="ASMV;ASMINF;WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS"
				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"
				AdditionalDependencies="..\..\masmx64\gvmat64.obj ..\..\masmx64\inffasx64.obj"
				GenerateManifest="false"
			/>
			<Tool
				Name="VCALinkTool"
			/>
			<Tool
				Name="VCManifestTool"
729
730
731
732
733
734
735
736

737
738
739
740
741
742
743
725
726
727
728
729
730
731

732
733
734
735
736
737
738
739







-
+







		</Configuration>
	</Configurations>
	<References>
	</References>
	<Files>
		<Filter
			Name="Source Files"
			Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm"
			Filter="cpp;c;cxx;def;odl;idl;hpj;bat"
			>
			<File
				RelativePath="..\..\..\adler32.c"
				>
			</File>
			<File
				RelativePath="..\..\..\compress.c"
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
748
749
750
751
752
753
754




















































755
756
757
758
759
760
761







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-







				>
			</File>
			<File
				RelativePath="..\..\..\infback.c"
				>
			</File>
			<File
				RelativePath="..\..\masmx64\inffas8664.c"
				>
				<FileConfiguration
					Name="Debug|Win32"
					ExcludedFromBuild="true"
					>
					<Tool
						Name="VCCLCompilerTool"
					/>
				</FileConfiguration>
				<FileConfiguration
					Name="Debug|Itanium"
					ExcludedFromBuild="true"
					>
					<Tool
						Name="VCCLCompilerTool"
					/>
				</FileConfiguration>
				<FileConfiguration
					Name="ReleaseWithoutAsm|Win32"
					ExcludedFromBuild="true"
					>
					<Tool
						Name="VCCLCompilerTool"
					/>
				</FileConfiguration>
				<FileConfiguration
					Name="ReleaseWithoutAsm|Itanium"
					ExcludedFromBuild="true"
					>
					<Tool
						Name="VCCLCompilerTool"
					/>
				</FileConfiguration>
				<FileConfiguration
					Name="Release|Win32"
					ExcludedFromBuild="true"
					>
					<Tool
						Name="VCCLCompilerTool"
					/>
				</FileConfiguration>
				<FileConfiguration
					Name="Release|Itanium"
					ExcludedFromBuild="true"
					>
					<Tool
						Name="VCCLCompilerTool"
					/>
				</FileConfiguration>
			</File>
			<File
				RelativePath="..\..\..\inffast.c"
				>
			</File>
			<File
				RelativePath="..\..\..\inflate.c"
				>
			</File>

Changes to compat/zlib/contrib/vstudio/vc9/testzlibdll.vcproj.

538
539
540
541
542
543
544
545

546
547
548
549
550
551
552
538
539
540
541
542
543
544

545
546
547
548
549
550
551
552







-
+







		</Configuration>
	</Configurations>
	<References>
	</References>
	<Files>
		<Filter
			Name="Source Files"
			Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm"
			Filter="cpp;c;cxx;def;odl;idl;hpj;bat"
			>
			<File
				RelativePath="..\..\testzlib\testzlib.c"
				>
			</File>
		</Filter>
		<Filter

Changes to 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
1
2
3
4


5
6
7
8
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, 2, 11, 0
  PRODUCTVERSION 1, 2, 11, 0
  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.2.11\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-2017 Jean-loup Gailly & Mark Adler\0"
      VALUE "LegalCopyright", "(C) 1995-2024 Jean-loup Gailly & Mark Adler\0"
    END
  END
  BLOCK "VarFileInfo"
  BEGIN
    VALUE "Translation", 0x0409, 1252
  END
END

Changes to compat/zlib/contrib/vstudio/vc9/zlibstat.vcproj.

43
44
45
46
47
48
49
50

51
52
53
54
55
56
57
43
44
45
46
47
48
49

50
51
52
53
54
55
56
57







-
+







			/>
			<Tool
				Name="VCMIDLTool"
			/>
			<Tool
				Name="VCCLCompilerTool"
				Optimization="0"
				AdditionalIncludeDirectories="..\..\..;..\..\masmx86"
				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)\"
117
118
119
120
121
122
123
124

125
126
127
128
129
130
131
117
118
119
120
121
122
123

124
125
126
127
128
129
130
131







-
+







			<Tool
				Name="VCMIDLTool"
				TargetEnvironment="3"
			/>
			<Tool
				Name="VCCLCompilerTool"
				Optimization="0"
				AdditionalIncludeDirectories="..\..\..;..\..\masmx86"
				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)\"
191
192
193
194
195
196
197
198

199
200
201
202
203
204
205
191
192
193
194
195
196
197

198
199
200
201
202
203
204
205







-
+







			<Tool
				Name="VCMIDLTool"
				TargetEnvironment="2"
			/>
			<Tool
				Name="VCCLCompilerTool"
				Optimization="0"
				AdditionalIncludeDirectories="..\..\..;..\..\masmx86"
				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)\"
264
265
266
267
268
269
270
271
272


273
274
275
276
277
278
279
264
265
266
267
268
269
270


271
272
273
274
275
276
277
278
279







-
-
+
+







			/>
			<Tool
				Name="VCMIDLTool"
			/>
			<Tool
				Name="VCCLCompilerTool"
				InlineFunctionExpansion="1"
				AdditionalIncludeDirectories="..\..\..;..\..\masmx86"
				PreprocessorDefinitions="WIN32;ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ASMV;ASMINF"
				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)\"
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
291
292
293
294
295
296
297

298
299
300
301
302
303
304







-







			/>
			<Tool
				Name="VCPreLinkEventTool"
			/>
			<Tool
				Name="VCLibrarianTool"
				AdditionalOptions="/MACHINE:X86 /NODEFAULTLIB"
				AdditionalDependencies="..\..\masmx86\match686.obj ..\..\masmx86\inffas32.obj "
				OutputFile="$(OutDir)\zlibstat.lib"
				SuppressStartupBanner="true"
			/>
			<Tool
				Name="VCALinkTool"
			/>
			<Tool
339
340
341
342
343
344
345
346
347


348
349
350
351
352
353
354
338
339
340
341
342
343
344


345
346
347
348
349
350
351
352
353







-
-
+
+







			<Tool
				Name="VCMIDLTool"
				TargetEnvironment="3"
			/>
			<Tool
				Name="VCCLCompilerTool"
				InlineFunctionExpansion="1"
				AdditionalIncludeDirectories="..\..\..;..\..\masmx86"
				PreprocessorDefinitions="ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ASMV;ASMINF;WIN64"
				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)\"
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
365
366
367
368
369
370
371

372
373
374
375
376
377
378







-







			/>
			<Tool
				Name="VCPreLinkEventTool"
			/>
			<Tool
				Name="VCLibrarianTool"
				AdditionalOptions="/MACHINE:AMD64 /NODEFAULTLIB"
				AdditionalDependencies="..\..\masmx64\gvmat64.obj ..\..\masmx64\inffasx64.obj "
				OutputFile="$(OutDir)\zlibstat.lib"
				SuppressStartupBanner="true"
			/>
			<Tool
				Name="VCALinkTool"
			/>
			<Tool
414
415
416
417
418
419
420
421

422
423
424
425
426
427
428
412
413
414
415
416
417
418

419
420
421
422
423
424
425
426







-
+







			<Tool
				Name="VCMIDLTool"
				TargetEnvironment="2"
			/>
			<Tool
				Name="VCCLCompilerTool"
				InlineFunctionExpansion="1"
				AdditionalIncludeDirectories="..\..\..;..\..\masmx86"
				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"
487
488
489
490
491
492
493
494

495
496
497
498
499
500
501
485
486
487
488
489
490
491

492
493
494
495
496
497
498
499







-
+







			/>
			<Tool
				Name="VCMIDLTool"
			/>
			<Tool
				Name="VCCLCompilerTool"
				InlineFunctionExpansion="1"
				AdditionalIncludeDirectories="..\..\..;..\..\masmx86"
				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"
561
562
563
564
565
566
567
568

569
570
571
572
573
574
575
559
560
561
562
563
564
565

566
567
568
569
570
571
572
573







-
+







			<Tool
				Name="VCMIDLTool"
				TargetEnvironment="3"
			/>
			<Tool
				Name="VCCLCompilerTool"
				InlineFunctionExpansion="1"
				AdditionalIncludeDirectories="..\..\..;..\..\masmx86"
				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"
635
636
637
638
639
640
641
642

643
644
645
646
647
648
649
633
634
635
636
637
638
639

640
641
642
643
644
645
646
647







-
+







			<Tool
				Name="VCMIDLTool"
				TargetEnvironment="2"
			/>
			<Tool
				Name="VCCLCompilerTool"
				InlineFunctionExpansion="1"
				AdditionalIncludeDirectories="..\..\..;..\..\masmx86"
				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"
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
727
728
729
730
731
732
733




















































734
735
736
737
738
739
740







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-







				>
			</File>
			<File
				RelativePath="..\..\..\infback.c"
				>
			</File>
			<File
				RelativePath="..\..\masmx64\inffas8664.c"
				>
				<FileConfiguration
					Name="Debug|Win32"
					ExcludedFromBuild="true"
					>
					<Tool
						Name="VCCLCompilerTool"
					/>
				</FileConfiguration>
				<FileConfiguration
					Name="Debug|Itanium"
					ExcludedFromBuild="true"
					>
					<Tool
						Name="VCCLCompilerTool"
					/>
				</FileConfiguration>
				<FileConfiguration
					Name="Release|Win32"
					ExcludedFromBuild="true"
					>
					<Tool
						Name="VCCLCompilerTool"
					/>
				</FileConfiguration>
				<FileConfiguration
					Name="Release|Itanium"
					ExcludedFromBuild="true"
					>
					<Tool
						Name="VCCLCompilerTool"
					/>
				</FileConfiguration>
				<FileConfiguration
					Name="ReleaseWithoutAsm|Win32"
					ExcludedFromBuild="true"
					>
					<Tool
						Name="VCCLCompilerTool"
					/>
				</FileConfiguration>
				<FileConfiguration
					Name="ReleaseWithoutAsm|Itanium"
					ExcludedFromBuild="true"
					>
					<Tool
						Name="VCCLCompilerTool"
					/>
				</FileConfiguration>
			</File>
			<File
				RelativePath="..\..\..\inffast.c"
				>
			</File>
			<File
				RelativePath="..\..\..\inflate.c"
				>
			</File>

Changes to compat/zlib/contrib/vstudio/vc9/zlibvc.def.

1
2
3
4

5
6
7
8
9
10
11
1
2
3

4
5
6
7
8
9
10
11



-
+







LIBRARY
; zlib data compression and ZIP file I/O library

VERSION		1.2
VERSION		1.3.1

EXPORTS
        adler32                                  @1
        compress                                 @2
        crc32                                    @3
        deflate                                  @4
        deflateCopy                              @5
147
148
149
150
151
152
153





147
148
149
150
151
152
153
154
155
156
157
158







+
+
+
+
+
        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

Changes to compat/zlib/contrib/vstudio/vc9/zlibvc.vcproj.

49
50
51
52
53
54
55
56
57


58
59
60
61
62
63
64
49
50
51
52
53
54
55


56
57
58
59
60
61
62
63
64







-
-
+
+







				SuppressStartupBanner="true"
				TargetEnvironment="1"
				TypeLibraryName="$(OutDir)/zlibvc.tlb"
			/>
			<Tool
				Name="VCCLCompilerTool"
				Optimization="0"
				AdditionalIncludeDirectories="..\..\..;..\..\masmx86"
				PreprocessorDefinitions="WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;ASMV;ASMINF"
				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)\"
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
77
78
79
80
81
82
83

84
85
86
87
88
89
90







-







			/>
			<Tool
				Name="VCPreLinkEventTool"
			/>
			<Tool
				Name="VCLinkerTool"
				AdditionalOptions="/MACHINE:I386"
				AdditionalDependencies="..\..\masmx86\match686.obj ..\..\masmx86\inffas32.obj"
				OutputFile="$(OutDir)\zlibwapi.dll"
				LinkIncremental="2"
				SuppressStartupBanner="true"
				GenerateManifest="false"
				ModuleDefinitionFile=".\zlibvc.def"
				GenerateDebugInformation="true"
				ProgramDatabaseFile="$(OutDir)/zlibwapi.pdb"
146
147
148
149
150
151
152
153
154


155
156
157
158
159
160
161
145
146
147
148
149
150
151


152
153
154
155
156
157
158
159
160







-
-
+
+







				SuppressStartupBanner="true"
				TargetEnvironment="3"
				TypeLibraryName="$(OutDir)/zlibvc.tlb"
			/>
			<Tool
				Name="VCCLCompilerTool"
				Optimization="0"
				AdditionalIncludeDirectories="..\..\..;..\..\masmx86"
				PreprocessorDefinitions="WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;ASMV;ASMINF;WIN64"
				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)\"
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
172
173
174
175
176
177
178

179
180
181
182
183
184
185







-







				Culture="1036"
			/>
			<Tool
				Name="VCPreLinkEventTool"
			/>
			<Tool
				Name="VCLinkerTool"
				AdditionalDependencies="..\..\masmx64\gvmat64.obj ..\..\masmx64\inffasx64.obj "
				OutputFile="$(OutDir)\zlibwapi.dll"
				LinkIncremental="2"
				SuppressStartupBanner="true"
				GenerateManifest="false"
				ModuleDefinitionFile=".\zlibvc.def"
				GenerateDebugInformation="true"
				ProgramDatabaseFile="$(OutDir)/zlibwapi.pdb"
241
242
243
244
245
246
247
248

249
250
251
252
253
254
255
239
240
241
242
243
244
245

246
247
248
249
250
251
252
253







-
+







				SuppressStartupBanner="true"
				TargetEnvironment="2"
				TypeLibraryName="$(OutDir)/zlibvc.tlb"
			/>
			<Tool
				Name="VCCLCompilerTool"
				Optimization="0"
				AdditionalIncludeDirectories="..\..\..;..\..\masmx86"
				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)\"
336
337
338
339
340
341
342
343

344
345
346
347
348
349
350
334
335
336
337
338
339
340

341
342
343
344
345
346
347
348







-
+







				SuppressStartupBanner="true"
				TargetEnvironment="1"
				TypeLibraryName="$(OutDir)/zlibvc.tlb"
			/>
			<Tool
				Name="VCCLCompilerTool"
				InlineFunctionExpansion="1"
				AdditionalIncludeDirectories="..\..\..;..\..\masmx86"
				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"
436
437
438
439
440
441
442
443

444
445
446
447
448
449
450
434
435
436
437
438
439
440

441
442
443
444
445
446
447
448







-
+







				SuppressStartupBanner="true"
				TargetEnvironment="3"
				TypeLibraryName="$(OutDir)/zlibvc.tlb"
			/>
			<Tool
				Name="VCCLCompilerTool"
				InlineFunctionExpansion="1"
				AdditionalIncludeDirectories="..\..\..;..\..\masmx86"
				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"
534
535
536
537
538
539
540
541

542
543
544
545
546
547
548
532
533
534
535
536
537
538

539
540
541
542
543
544
545
546







-
+







				SuppressStartupBanner="true"
				TargetEnvironment="2"
				TypeLibraryName="$(OutDir)/zlibvc.tlb"
			/>
			<Tool
				Name="VCCLCompilerTool"
				InlineFunctionExpansion="1"
				AdditionalIncludeDirectories="..\..\..;..\..\masmx86"
				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"
632
633
634
635
636
637
638
639
640


641
642
643
644
645
646
647
630
631
632
633
634
635
636


637
638
639
640
641
642
643
644
645







-
-
+
+







				SuppressStartupBanner="true"
				TargetEnvironment="1"
				TypeLibraryName="$(OutDir)/zlibvc.tlb"
			/>
			<Tool
				Name="VCCLCompilerTool"
				InlineFunctionExpansion="1"
				AdditionalIncludeDirectories="..\..\..;..\..\masmx86"
				PreprocessorDefinitions="WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;ASMV;ASMINF"
				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"
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
660
661
662
663
664
665
666

667
668
669
670
671
672
673







-







			/>
			<Tool
				Name="VCPreLinkEventTool"
			/>
			<Tool
				Name="VCLinkerTool"
				AdditionalOptions="/MACHINE:I386"
				AdditionalDependencies="..\..\masmx86\match686.obj ..\..\masmx86\inffas32.obj "
				OutputFile="$(OutDir)\zlibwapi.dll"
				LinkIncremental="1"
				SuppressStartupBanner="true"
				GenerateManifest="false"
				IgnoreAllDefaultLibraries="false"
				ModuleDefinitionFile=".\zlibvc.def"
				ProgramDatabaseFile="$(OutDir)/zlibwapi.pdb"
733
734
735
736
737
738
739
740
741


742
743
744
745
746
747
748
730
731
732
733
734
735
736


737
738
739
740
741
742
743
744
745







-
-
+
+







				SuppressStartupBanner="true"
				TargetEnvironment="3"
				TypeLibraryName="$(OutDir)/zlibvc.tlb"
			/>
			<Tool
				Name="VCCLCompilerTool"
				InlineFunctionExpansion="1"
				AdditionalIncludeDirectories="..\..\..;..\..\masmx86"
				PreprocessorDefinitions="_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;ASMV;ASMINF;WIN64"
				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"
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
759
760
761
762
763
764
765

766
767
768
769
770
771
772







-







				Culture="1036"
			/>
			<Tool
				Name="VCPreLinkEventTool"
			/>
			<Tool
				Name="VCLinkerTool"
				AdditionalDependencies="..\..\masmx64\gvmat64.obj ..\..\masmx64\inffasx64.obj "
				OutputFile="$(OutDir)\zlibwapi.dll"
				LinkIncremental="1"
				SuppressStartupBanner="true"
				GenerateManifest="false"
				IgnoreAllDefaultLibraries="false"
				ModuleDefinitionFile=".\zlibvc.def"
				ProgramDatabaseFile="$(OutDir)/zlibwapi.pdb"
832
833
834
835
836
837
838
839

840
841
842
843
844
845
846
828
829
830
831
832
833
834

835
836
837
838
839
840
841
842







-
+







				SuppressStartupBanner="true"
				TargetEnvironment="2"
				TypeLibraryName="$(OutDir)/zlibvc.tlb"
			/>
			<Tool
				Name="VCCLCompilerTool"
				InlineFunctionExpansion="1"
				AdditionalIncludeDirectories="..\..\..;..\..\masmx86"
				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"
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
942
943
944
945
946
947
948




















































949
950
951
952
953
954
955







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-







				>
			</File>
			<File
				RelativePath="..\..\..\infback.c"
				>
			</File>
			<File
				RelativePath="..\..\masmx64\inffas8664.c"
				>
				<FileConfiguration
					Name="Debug|Win32"
					ExcludedFromBuild="true"
					>
					<Tool
						Name="VCCLCompilerTool"
					/>
				</FileConfiguration>
				<FileConfiguration
					Name="Debug|Itanium"
					ExcludedFromBuild="true"
					>
					<Tool
						Name="VCCLCompilerTool"
					/>
				</FileConfiguration>
				<FileConfiguration
					Name="ReleaseWithoutAsm|Win32"
					ExcludedFromBuild="true"
					>
					<Tool
						Name="VCCLCompilerTool"
					/>
				</FileConfiguration>
				<FileConfiguration
					Name="ReleaseWithoutAsm|Itanium"
					ExcludedFromBuild="true"
					>
					<Tool
						Name="VCCLCompilerTool"
					/>
				</FileConfiguration>
				<FileConfiguration
					Name="Release|Win32"
					ExcludedFromBuild="true"
					>
					<Tool
						Name="VCCLCompilerTool"
					/>
				</FileConfiguration>
				<FileConfiguration
					Name="Release|Itanium"
					ExcludedFromBuild="true"
					>
					<Tool
						Name="VCCLCompilerTool"
					/>
				</FileConfiguration>
			</File>
			<File
				RelativePath="..\..\..\inffast.c"
				>
			</File>
			<File
				RelativePath="..\..\..\inflate.c"
				>
			</File>

Changes to 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
1

2
3
4



5
6
7


8
9
10
11
12
13
14

15
16
17
18

19
20
21
22
23
24
25
26
27
28
29

30
31
32
33
34
35
36
37
38
39
40
41
42



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88




89
90
91
92
93
94
95
96
97
98
99
100


101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144


145
146
147
148
149







150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189




190
191
192
193
194
195
196
197
198
199
200
201

202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289

290
291

292
293

294
295
296
297

298
299




300
301
302
303
304



305
306
307



308


309







310






311




312
313






314
315
316
317
318
319



320




321









322
323



324
325
326



327
328
329
330

331
332
333
334
335
336

337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356



357
358
359
360











361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
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-2006, 2010, 2011, 2012, 2016 Mark Adler
 * Copyright (C) 1995-2022 Mark Adler
 * For conditions of distribution and use, see copyright notice in zlib.h
 *
 * Thanks to Rodney Brown <rbrown64@csc.com.au> for his contribution of faster
 * CRC methods: exclusive-oring 32 bits of data at a time, and pre-computing
 * tables for updating the shift register in one step with three exclusive-ors
 * 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.
 * instead of four steps with four exclusive-ors.  This results in about a
 * factor of two increase in speed on a Power PC G4 (PPC7455) using gcc -O3.
 */

/* @(#) $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
  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().

  DYNAMIC_CRC_TABLE and MAKECRCH can be #defined to write out crc32.h.
  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 STDC and FAR definitions */
#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.
/* Definitions for doing the crc four data bytes at a time. */
#if !defined(NOBYFOUR) && defined(Z_U4)
#  define BYFOUR

  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 BYFOUR
   local unsigned long crc32_little OF((unsigned long,
                        const unsigned char FAR *, z_size_t));
   local unsigned long crc32_big OF((unsigned long,
#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

                        const unsigned char FAR *, z_size_t));
#  define TBLS 8
/* 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().
 */
#  define TBLS 1
#endif /* BYFOUR */
#  include "crc32.h"
#endif

/* CRC polynomial. */
#define POLY 0xedb88320         /* p(x) reflected, with x^32 implied */
/* Local functions for crc concatenation */
local unsigned long gf2_matrix_times OF((unsigned long *mat,
                                         unsigned long vec));
local void gf2_matrix_square OF((unsigned long *square, unsigned long *mat));
local uLong crc32_combine_ OF((uLong crc1, uLong crc2, z_off64_t len2));



/*
  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

local volatile int crc_table_empty = 1;
local z_crc_t FAR crc_table[TBLS][256];
local void make_crc_table OF((void));
/* =========================================================================
 * 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 OF((FILE *, const z_crc_t FAR *));
   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
  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
  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+1), then the CRC is (q*x^32) mod p,
  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
  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.
  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 first 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.  The remaining tables
  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.
  allow for word-at-a-time CRC calculation for both big-endian and little-
  endian machines, where a word is four bytes.
*/
 */
local void make_crc_table()
{

    z_crc_t c;
    int n, k;
    z_crc_t poly;                       /* polynomial exclusive-or pattern */
    /* terms of polynomial defining this crc (except x^32): */
    static volatile int first = 1;      /* flag to limit concurrent making */
    static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26};

local void make_crc_table(void) {
    /* See if another task is already doing this (not thread-safe, but better
       than nothing -- significantly reduces duration of vulnerability in
       case the advice about DYNAMIC_CRC_TABLE is ignored) */
    if (first) {
        first = 0;

    unsigned i, j, n;
        /* make exclusive-or pattern from polynomial (0xedb88320UL) */
        poly = 0;
        for (n = 0; n < (int)(sizeof(p)/sizeof(unsigned char)); n++)
            poly |= (z_crc_t)1 << (31 - p[n]);
    z_crc_t p;

        /* generate a crc for every 8-bit value */
        for (n = 0; n < 256; n++) {
            c = (z_crc_t)n;
            for (k = 0; k < 8; k++)
                c = c & 1 ? poly ^ (c >> 1) : c >> 1;
            crc_table[0][n] = c;
    /* 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 BYFOUR
#ifdef W
        /* generate crc for each value followed by one, two, and three zeros,
           and then the byte reversal of those as well as the first table */
        for (n = 0; n < 256; n++) {
            c = crc_table[0][n];
        crc_big_table[i] = byte_swap(p);
            crc_table[4][n] = ZSWAP32(c);
            for (k = 1; k < 4; k++) {
                c = crc_table[0][c & 0xff] ^ (c >> 8);
                crc_table[k][n] = c;
                crc_table[k + 4][n] = ZSWAP32(c);
            }
        }
#endif /* BYFOUR */

#endif
    }
        crc_table_empty = 0;
    }
    else {      /* not first */

    /* initialize the x^2^n mod p(x) table */
    p = (z_crc_t)1 << 30;         /* x^1 */
        /* wait for the other guy to finish (not efficient, but rare) */
        while (crc_table_empty)
            ;
    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
    /* write out CRC tables to crc32.h */
    {
        /*
          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,
        fprintf(out, "/* crc32.h -- tables for rapid CRC calculation\n");
        fprintf(out, " * Generated automatically by crc32.c\n */\n\n");
        fprintf(out, "local const z_crc_t FAR ");
            "/* 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"
        fprintf(out, "crc_table[TBLS][256] =\n{\n  {\n");
        write_table(out, crc_table[0]);
#  ifdef BYFOUR
        fprintf(out, "#ifdef BYFOUR\n");
        for (k = 1; k < 8; k++) {
            fprintf(out, "  },\n  {\n");
            write_table(out, crc_table[k]);
        }
        fprintf(out, "#endif\n");
#  endif /* BYFOUR */
        fprintf(out, "  }\n};\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(out, table)
    FILE *out;
    const z_crc_t FAR *table;
{
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 < 256; n++)
        fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : "    ",
                (unsigned long)(table[n]),
                n == 255 ? "\n" : (n % 5 == 4 ? ",\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" : ", "));
}
#endif /* MAKECRCH */

/*
  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" : ", "));
}
#else /* !DYNAMIC_CRC_TABLE */
/* ========================================================================
 * Tables of CRC-32s of all single-byte values, made by make_crc_table().

/* 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
#include "crc32.h"

#endif /* DYNAMIC_CRC_TABLE */

/* =========================================================================
 * This function can be used by asm versions of crc32()
 * 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()
const z_crc_t FAR * ZEXPORT get_crc_table(void) {
{
#ifdef DYNAMIC_CRC_TABLE
    if (crc_table_empty)
        make_crc_table();
    once(&made, make_crc_table);
#endif /* DYNAMIC_CRC_TABLE */
    return (const z_crc_t FAR *)crc_table;
}

/* ========================================================================= */
#define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8)
#define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1
/* =========================================================================
 * 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(crc, buf, len)
    unsigned long crc;
    const unsigned char FAR *buf;
    z_size_t len;
{
    if (buf == Z_NULL) return 0UL;

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
    if (crc_table_empty)
        make_crc_table();
#endif /* DYNAMIC_CRC_TABLE */

#ifdef BYFOUR
    if (sizeof(void *) == sizeof(ptrdiff_t)) {
        z_crc_t endian;

#ifdef DYNAMIC_CRC_TABLE
    once(&made, make_crc_table);
        endian = 1;
        if (*((unsigned char *)(&endian)))
            return crc32_little(crc, buf, len);
#endif /* DYNAMIC_CRC_TABLE */

    /* Pre-condition the CRC */
    crc = (~crc) & 0xffffffff;
        else
            return crc32_big(crc, buf, len);
    }
#endif /* BYFOUR */

    /* Compute the CRC up to a word boundary. */
    crc = crc ^ 0xffffffffUL;
    while (len >= 8) {
    while (len && ((z_size_t)buf & 7) != 0) {
        DO8;
        len -= 8;
        len--;
        val = *buf++;
        __asm__ volatile("crc32b %w0, %w0, %w1" : "+r"(crc) : "r"(val));
    }
    if (len) do {

        DO1;
    } while (--len);
    return crc ^ 0xffffffffUL;
}

    /* 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));
        }
/* ========================================================================= */
unsigned long ZEXPORT crc32(crc, buf, len)
    unsigned long crc;
    const unsigned char FAR *buf;
    uInt len;
{
    return crc32_z(crc, buf, len);
}

#ifdef BYFOUR

/*
   This BYFOUR code accesses the passed unsigned char * buffer with a 32-bit
   integer pointer type. This violates the strict aliasing rule, where a
   compiler can assume, for optimization purposes, that two pointers to
   fundamentally different types won't ever point to the same memory. This can
   manifest as a problem only if one of the pointers is written to. This code
        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

   only reads from those pointers. So long as this code remains isolated in
   this compilation unit, there won't be a problem. For this reason, this code
   should not be copied and pasted into a compilation unit in which other code
   writes to the buffer that is passed to these routines.
/*
  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;

/* ========================================================================= */
#define DOLIT4 c ^= *buf4++; \
}

local z_word_t crc_word_big(z_word_t data) {
    int k;
        c = crc_table[3][c & 0xff] ^ crc_table[2][(c >> 8) & 0xff] ^ \
            crc_table[1][(c >> 16) & 0xff] ^ crc_table[0][c >> 24]
#define DOLIT32 DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4
    for (k = 0; k < W; k++)
        data = (data << 8) ^
            crc_big_table[(data >> ((W - 1) << 3)) & 0xff];
    return data;
}

#endif

/* ========================================================================= */
local unsigned long crc32_little(crc, buf, len)
    unsigned long crc;
    const unsigned char FAR *buf;
    z_size_t len;
{
    register z_crc_t c;
    register const z_crc_t FAR *buf4;
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;

    c = (z_crc_t)crc;
    c = ~c;
    while (len && ((ptrdiff_t)buf & 3)) {
        /* Compute the CRC up to a z_word_t boundary. */
        while (len && ((z_size_t)buf & (W - 1)) != 0) {
        c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8);
        len--;
    }
            len--;
            crc = (crc >> 8) ^ crc_table[(crc ^ *buf++) & 0xff];
        }

    buf4 = (const z_crc_t FAR *)(const void FAR *)buf;
    while (len >= 32) {
        /* Compute the CRC on as many N z_word_t blocks as are available. */
        blks = len / (N * W);
        DOLIT32;
        len -= 32;
    }
    while (len >= 4) {
        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;
        DOLIT4;
        len -= 4;
    }
        if (*(unsigned char *)&endian) {
            /* Little endian. */

    buf = (const unsigned char FAR *)buf4;

    if (len) do {
        c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8);
    } while (--len);
    c = ~c;
            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
    return (unsigned long)c;
}

            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. */
/* ========================================================================= */
#define DOBIG4 c ^= *buf4++; \
        c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \
            crc_table[6][(c >> 16) & 0xff] ^ crc_table[7][c >> 24]
            crc0 = crc;
#if N > 1
            crc1 = 0;
#define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4

#if N > 2
            crc2 = 0;
#if N > 3
            crc3 = 0;
#if N > 4
            crc4 = 0;
#if N > 5
            crc5 = 0;
#endif
#endif
#endif
/* ========================================================================= */
local unsigned long crc32_big(crc, buf, len)
    unsigned long crc;
    const unsigned char FAR *buf;
    z_size_t len;
{
    register z_crc_t c;
    register const z_crc_t FAR *buf4;

    c = ZSWAP32((z_crc_t)crc);
    c = ~c;
#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];
    while (len && ((ptrdiff_t)buf & 3)) {
        c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8);
        len--;
    }

    buf4 = (const z_crc_t FAR *)(const void FAR *)buf;
    while (len >= 32) {
        DOBIG32;
        len -= 32;
#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
    }
    while (len >= 4) {
#endif
        DOBIG4;
        len -= 4;
#endif
    }
    buf = (const unsigned char FAR *)buf4;

    if (len) do {
        c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8);
    } while (--len);
#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
    c = ~c;
    return (unsigned long)(ZSWAP32(c));
}

#endif /* BYFOUR */

#endif
#endif
#endif
#endif
                }
#define GF2_DIM 32      /* dimension of GF(2) vectors (length of CRC) */

/* ========================================================================= */
local unsigned long gf2_matrix_times(mat, vec)
    unsigned long *mat;
    unsigned long vec;
{
            }

            /*
              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. */

    unsigned long sum;
            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
    sum = 0;
            crc1 = 0;
    while (vec) {
        if (vec & 1)
            sum ^= *mat;
        vec >>= 1;
#if N > 2
            crc2 = 0;
#if N > 3
        mat++;
    }
    return sum;
            crc3 = 0;
#if N > 4
            crc4 = 0;
#if N > 5
            crc5 = 0;
#endif
}

#endif
/* ========================================================================= */
local void gf2_matrix_square(square, mat)
    unsigned long *square;
    unsigned long *mat;
{
    int n;

    for (n = 0; n < GF2_DIM; n++)
#endif
#endif
#endif
        square[n] = gf2_matrix_times(mat, mat[n]);
}


            /*
/* ========================================================================= */
local uLong crc32_combine_(crc1, crc2, len2)
    uLong crc1;
    uLong crc2;
    z_off64_t len2;
{
    int n;
              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. */
    unsigned long row;
    unsigned long even[GF2_DIM];    /* even-power-of-two zeros operator */
    unsigned long odd[GF2_DIM];     /* odd-power-of-two zeros operator */

    /* degenerate case (also disallow negative lengths) */
    if (len2 <= 0)
        return crc1;

                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
    /* put operator for one zero bit in odd */
    odd[0] = 0xedb88320UL;          /* CRC-32 polynomial */
    row = 1;
    for (n = 1; n < GF2_DIM; n++) {
        odd[n] = row;
        row <<= 1;
                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];
    /* put operator for two zero bits in even */
    gf2_matrix_square(even, odd);

#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
                }
    /* put operator for four zero bits in odd */
    gf2_matrix_square(odd, even);

    /* apply len2 zeros to crc1 (first square will put the operator for one
       zero byte, eight zero bits, in even) */
    do {
            }

            /*
              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
        /* apply zeros operator for this bit of len2 */
        gf2_matrix_square(even, odd);
        if (len2 & 1)
            crc1 = gf2_matrix_times(even, crc1);
        len2 >>= 1;

        /* if no more bits set, then done */
#endif
#endif
#endif
#endif
            words += N;
            crc = byte_swap(comb);
        }

        if (len2 == 0)
            break;

        /* another iteration of the loop with odd and even swapped */
        gf2_matrix_square(odd, even);
        if (len2 & 1)
        /*
          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) {
            crc1 = gf2_matrix_times(odd, crc1);
        len2 >>= 1;

        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];
    }
        /* if no more bits set, then done */
    } while (len2 != 0);

    /* return combined crc */
    while (len) {
        len--;
        crc = (crc >> 8) ^ crc_table[(crc ^ *buf++) & 0xff];
    }

    crc1 ^= crc2;
    return crc1;
    /* 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_combine(crc1, crc2, len2)
    uLong crc1;
    uLong crc2;
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);
    z_off_t len2;
{
    return crc32_combine_(crc1, crc2, len2);
}

/* ========================================================================= */
uLong ZEXPORT crc32_combine(uLong crc1, uLong crc2, z_off_t len2) {
    return crc32_combine64(crc1, crc2, (z_off64_t)len2);
}

/* ========================================================================= */
uLong ZEXPORT crc32_combine64(crc1, crc2, len2)
    uLong crc1;
    uLong crc2;
    z_off64_t len2;
{
    return crc32_combine_(crc1, crc2, 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);
}

Changes to 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









1
2
3
4



























































5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
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[TBLS][256] =
{
  {
    0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL,
    0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL,
    0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL,
    0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL,
    0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL,
    0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL,
    0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL,
    0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL,
    0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL,
    0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL,
    0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL,
    0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL,
    0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL,
    0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL,
    0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL,
    0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL,
    0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL,
    0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL,
    0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL,
    0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL,
    0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL,
    0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL,
    0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL,
    0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL,
    0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL,
    0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL,
    0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL,
    0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL,
    0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL,
    0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL,
    0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL,
    0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL,
    0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL,
    0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL,
    0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL,
    0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL,
    0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL,
    0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL,
    0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL,
    0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL,
    0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL,
    0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL,
    0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL,
    0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL,
    0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL,
    0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL,
    0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL,
    0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL,
    0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL,
    0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL,
    0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL,
    0x2d02ef8dUL
#ifdef BYFOUR
  },
  {
    0x00000000UL, 0x191b3141UL, 0x32366282UL, 0x2b2d53c3UL, 0x646cc504UL,
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 */

    0x7d77f445UL, 0x565aa786UL, 0x4f4196c7UL, 0xc8d98a08UL, 0xd1c2bb49UL,
    0xfaefe88aUL, 0xe3f4d9cbUL, 0xacb54f0cUL, 0xb5ae7e4dUL, 0x9e832d8eUL,
    0x87981ccfUL, 0x4ac21251UL, 0x53d92310UL, 0x78f470d3UL, 0x61ef4192UL,
    0x2eaed755UL, 0x37b5e614UL, 0x1c98b5d7UL, 0x05838496UL, 0x821b9859UL,
    0x9b00a918UL, 0xb02dfadbUL, 0xa936cb9aUL, 0xe6775d5dUL, 0xff6c6c1cUL,
    0xd4413fdfUL, 0xcd5a0e9eUL, 0x958424a2UL, 0x8c9f15e3UL, 0xa7b24620UL,
    0xbea97761UL, 0xf1e8e1a6UL, 0xe8f3d0e7UL, 0xc3de8324UL, 0xdac5b265UL,
    0x5d5daeaaUL, 0x44469febUL, 0x6f6bcc28UL, 0x7670fd69UL, 0x39316baeUL,
    0x202a5aefUL, 0x0b07092cUL, 0x121c386dUL, 0xdf4636f3UL, 0xc65d07b2UL,
    0xed705471UL, 0xf46b6530UL, 0xbb2af3f7UL, 0xa231c2b6UL, 0x891c9175UL,
    0x9007a034UL, 0x179fbcfbUL, 0x0e848dbaUL, 0x25a9de79UL, 0x3cb2ef38UL,
    0x73f379ffUL, 0x6ae848beUL, 0x41c51b7dUL, 0x58de2a3cUL, 0xf0794f05UL,
    0xe9627e44UL, 0xc24f2d87UL, 0xdb541cc6UL, 0x94158a01UL, 0x8d0ebb40UL,
    0xa623e883UL, 0xbf38d9c2UL, 0x38a0c50dUL, 0x21bbf44cUL, 0x0a96a78fUL,
    0x138d96ceUL, 0x5ccc0009UL, 0x45d73148UL, 0x6efa628bUL, 0x77e153caUL,
    0xbabb5d54UL, 0xa3a06c15UL, 0x888d3fd6UL, 0x91960e97UL, 0xded79850UL,
    0xc7cca911UL, 0xece1fad2UL, 0xf5facb93UL, 0x7262d75cUL, 0x6b79e61dUL,
    0x4054b5deUL, 0x594f849fUL, 0x160e1258UL, 0x0f152319UL, 0x243870daUL,
    0x3d23419bUL, 0x65fd6ba7UL, 0x7ce65ae6UL, 0x57cb0925UL, 0x4ed03864UL,
    0x0191aea3UL, 0x188a9fe2UL, 0x33a7cc21UL, 0x2abcfd60UL, 0xad24e1afUL,
    0xb43fd0eeUL, 0x9f12832dUL, 0x8609b26cUL, 0xc94824abUL, 0xd05315eaUL,
    0xfb7e4629UL, 0xe2657768UL, 0x2f3f79f6UL, 0x362448b7UL, 0x1d091b74UL,
    0x04122a35UL, 0x4b53bcf2UL, 0x52488db3UL, 0x7965de70UL, 0x607eef31UL,
    0xe7e6f3feUL, 0xfefdc2bfUL, 0xd5d0917cUL, 0xcccba03dUL, 0x838a36faUL,
    0x9a9107bbUL, 0xb1bc5478UL, 0xa8a76539UL, 0x3b83984bUL, 0x2298a90aUL,
    0x09b5fac9UL, 0x10aecb88UL, 0x5fef5d4fUL, 0x46f46c0eUL, 0x6dd93fcdUL,
    0x74c20e8cUL, 0xf35a1243UL, 0xea412302UL, 0xc16c70c1UL, 0xd8774180UL,
    0x9736d747UL, 0x8e2de606UL, 0xa500b5c5UL, 0xbc1b8484UL, 0x71418a1aUL,
    0x685abb5bUL, 0x4377e898UL, 0x5a6cd9d9UL, 0x152d4f1eUL, 0x0c367e5fUL,
    0x271b2d9cUL, 0x3e001cddUL, 0xb9980012UL, 0xa0833153UL, 0x8bae6290UL,
    0x92b553d1UL, 0xddf4c516UL, 0xc4eff457UL, 0xefc2a794UL, 0xf6d996d5UL,
    0xae07bce9UL, 0xb71c8da8UL, 0x9c31de6bUL, 0x852aef2aUL, 0xca6b79edUL,
    0xd37048acUL, 0xf85d1b6fUL, 0xe1462a2eUL, 0x66de36e1UL, 0x7fc507a0UL,
    0x54e85463UL, 0x4df36522UL, 0x02b2f3e5UL, 0x1ba9c2a4UL, 0x30849167UL,
    0x299fa026UL, 0xe4c5aeb8UL, 0xfdde9ff9UL, 0xd6f3cc3aUL, 0xcfe8fd7bUL,
    0x80a96bbcUL, 0x99b25afdUL, 0xb29f093eUL, 0xab84387fUL, 0x2c1c24b0UL,
    0x350715f1UL, 0x1e2a4632UL, 0x07317773UL, 0x4870e1b4UL, 0x516bd0f5UL,
    0x7a468336UL, 0x635db277UL, 0xcbfad74eUL, 0xd2e1e60fUL, 0xf9ccb5ccUL,
    0xe0d7848dUL, 0xaf96124aUL, 0xb68d230bUL, 0x9da070c8UL, 0x84bb4189UL,
    0x03235d46UL, 0x1a386c07UL, 0x31153fc4UL, 0x280e0e85UL, 0x674f9842UL,
    0x7e54a903UL, 0x5579fac0UL, 0x4c62cb81UL, 0x8138c51fUL, 0x9823f45eUL,
    0xb30ea79dUL, 0xaa1596dcUL, 0xe554001bUL, 0xfc4f315aUL, 0xd7626299UL,
    0xce7953d8UL, 0x49e14f17UL, 0x50fa7e56UL, 0x7bd72d95UL, 0x62cc1cd4UL,
    0x2d8d8a13UL, 0x3496bb52UL, 0x1fbbe891UL, 0x06a0d9d0UL, 0x5e7ef3ecUL,
    0x4765c2adUL, 0x6c48916eUL, 0x7553a02fUL, 0x3a1236e8UL, 0x230907a9UL,
    0x0824546aUL, 0x113f652bUL, 0x96a779e4UL, 0x8fbc48a5UL, 0xa4911b66UL,
    0xbd8a2a27UL, 0xf2cbbce0UL, 0xebd08da1UL, 0xc0fdde62UL, 0xd9e6ef23UL,
    0x14bce1bdUL, 0x0da7d0fcUL, 0x268a833fUL, 0x3f91b27eUL, 0x70d024b9UL,
    0x69cb15f8UL, 0x42e6463bUL, 0x5bfd777aUL, 0xdc656bb5UL, 0xc57e5af4UL,
    0xee530937UL, 0xf7483876UL, 0xb809aeb1UL, 0xa1129ff0UL, 0x8a3fcc33UL,
    0x9324fd72UL
  },
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},
  {
    0x00000000UL, 0x01c26a37UL, 0x0384d46eUL, 0x0246be59UL, 0x0709a8dcUL,
   {0x0000000000000000, 0x6f4ca59b00000000, 0x9f9e3bec00000000,
    0x06cbc2ebUL, 0x048d7cb2UL, 0x054f1685UL, 0x0e1351b8UL, 0x0fd13b8fUL,
    0x0d9785d6UL, 0x0c55efe1UL, 0x091af964UL, 0x08d89353UL, 0x0a9e2d0aUL,
    0x0b5c473dUL, 0x1c26a370UL, 0x1de4c947UL, 0x1fa2771eUL, 0x1e601d29UL,
    0x1b2f0bacUL, 0x1aed619bUL, 0x18abdfc2UL, 0x1969b5f5UL, 0x1235f2c8UL,
    0x13f798ffUL, 0x11b126a6UL, 0x10734c91UL, 0x153c5a14UL, 0x14fe3023UL,
    0x16b88e7aUL, 0x177ae44dUL, 0x384d46e0UL, 0x398f2cd7UL, 0x3bc9928eUL,
    0x3a0bf8b9UL, 0x3f44ee3cUL, 0x3e86840bUL, 0x3cc03a52UL, 0x3d025065UL,
    0x365e1758UL, 0x379c7d6fUL, 0x35dac336UL, 0x3418a901UL, 0x3157bf84UL,
    0x3095d5b3UL, 0x32d36beaUL, 0x331101ddUL, 0x246be590UL, 0x25a98fa7UL,
    0x27ef31feUL, 0x262d5bc9UL, 0x23624d4cUL, 0x22a0277bUL, 0x20e69922UL,
    0x2124f315UL, 0x2a78b428UL, 0x2bbade1fUL, 0x29fc6046UL, 0x283e0a71UL,
    0x2d711cf4UL, 0x2cb376c3UL, 0x2ef5c89aUL, 0x2f37a2adUL, 0x709a8dc0UL,
    0x7158e7f7UL, 0x731e59aeUL, 0x72dc3399UL, 0x7793251cUL, 0x76514f2bUL,
    0x7417f172UL, 0x75d59b45UL, 0x7e89dc78UL, 0x7f4bb64fUL, 0x7d0d0816UL,
    0x7ccf6221UL, 0x798074a4UL, 0x78421e93UL, 0x7a04a0caUL, 0x7bc6cafdUL,
    0x6cbc2eb0UL, 0x6d7e4487UL, 0x6f38fadeUL, 0x6efa90e9UL, 0x6bb5866cUL,
    0x6a77ec5bUL, 0x68315202UL, 0x69f33835UL, 0x62af7f08UL, 0x636d153fUL,
    0x612bab66UL, 0x60e9c151UL, 0x65a6d7d4UL, 0x6464bde3UL, 0x662203baUL,
    0x67e0698dUL, 0x48d7cb20UL, 0x4915a117UL, 0x4b531f4eUL, 0x4a917579UL,
    0x4fde63fcUL, 0x4e1c09cbUL, 0x4c5ab792UL, 0x4d98dda5UL, 0x46c49a98UL,
    0x4706f0afUL, 0x45404ef6UL, 0x448224c1UL, 0x41cd3244UL, 0x400f5873UL,
    0x4249e62aUL, 0x438b8c1dUL, 0x54f16850UL, 0x55330267UL, 0x5775bc3eUL,
    0x56b7d609UL, 0x53f8c08cUL, 0x523aaabbUL, 0x507c14e2UL, 0x51be7ed5UL,
    0x5ae239e8UL, 0x5b2053dfUL, 0x5966ed86UL, 0x58a487b1UL, 0x5deb9134UL,
    0x5c29fb03UL, 0x5e6f455aUL, 0x5fad2f6dUL, 0xe1351b80UL, 0xe0f771b7UL,
    0xe2b1cfeeUL, 0xe373a5d9UL, 0xe63cb35cUL, 0xe7fed96bUL, 0xe5b86732UL,
    0xe47a0d05UL, 0xef264a38UL, 0xeee4200fUL, 0xeca29e56UL, 0xed60f461UL,
    0xe82fe2e4UL, 0xe9ed88d3UL, 0xebab368aUL, 0xea695cbdUL, 0xfd13b8f0UL,
    0xfcd1d2c7UL, 0xfe976c9eUL, 0xff5506a9UL, 0xfa1a102cUL, 0xfbd87a1bUL,
    0xf99ec442UL, 0xf85cae75UL, 0xf300e948UL, 0xf2c2837fUL, 0xf0843d26UL,
    0xf1465711UL, 0xf4094194UL, 0xf5cb2ba3UL, 0xf78d95faUL, 0xf64fffcdUL,
    0xd9785d60UL, 0xd8ba3757UL, 0xdafc890eUL, 0xdb3ee339UL, 0xde71f5bcUL,
    0xdfb39f8bUL, 0xddf521d2UL, 0xdc374be5UL, 0xd76b0cd8UL, 0xd6a966efUL,
    0xd4efd8b6UL, 0xd52db281UL, 0xd062a404UL, 0xd1a0ce33UL, 0xd3e6706aUL,
    0xd2241a5dUL, 0xc55efe10UL, 0xc49c9427UL, 0xc6da2a7eUL, 0xc7184049UL,
    0xc25756ccUL, 0xc3953cfbUL, 0xc1d382a2UL, 0xc011e895UL, 0xcb4dafa8UL,
    0xca8fc59fUL, 0xc8c97bc6UL, 0xc90b11f1UL, 0xcc440774UL, 0xcd866d43UL,
    0xcfc0d31aUL, 0xce02b92dUL, 0x91af9640UL, 0x906dfc77UL, 0x922b422eUL,
    0x93e92819UL, 0x96a63e9cUL, 0x976454abUL, 0x9522eaf2UL, 0x94e080c5UL,
    0x9fbcc7f8UL, 0x9e7eadcfUL, 0x9c381396UL, 0x9dfa79a1UL, 0x98b56f24UL,
    0x99770513UL, 0x9b31bb4aUL, 0x9af3d17dUL, 0x8d893530UL, 0x8c4b5f07UL,
    0x8e0de15eUL, 0x8fcf8b69UL, 0x8a809decUL, 0x8b42f7dbUL, 0x89044982UL,
    0x88c623b5UL, 0x839a6488UL, 0x82580ebfUL, 0x801eb0e6UL, 0x81dcdad1UL,
    0x8493cc54UL, 0x8551a663UL, 0x8717183aUL, 0x86d5720dUL, 0xa9e2d0a0UL,
    0xa820ba97UL, 0xaa6604ceUL, 0xaba46ef9UL, 0xaeeb787cUL, 0xaf29124bUL,
    0xad6fac12UL, 0xacadc625UL, 0xa7f18118UL, 0xa633eb2fUL, 0xa4755576UL,
    0xa5b73f41UL, 0xa0f829c4UL, 0xa13a43f3UL, 0xa37cfdaaUL, 0xa2be979dUL,
    0xb5c473d0UL, 0xb40619e7UL, 0xb640a7beUL, 0xb782cd89UL, 0xb2cddb0cUL,
    0xb30fb13bUL, 0xb1490f62UL, 0xb08b6555UL, 0xbbd72268UL, 0xba15485fUL,
    0xb853f606UL, 0xb9919c31UL, 0xbcde8ab4UL, 0xbd1ce083UL, 0xbf5a5edaUL,
    0xbe9834edUL
  },
    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},
  {
    0x00000000UL, 0xb8bc6765UL, 0xaa09c88bUL, 0x12b5afeeUL, 0x8f629757UL,
    0x37def032UL, 0x256b5fdcUL, 0x9dd738b9UL, 0xc5b428efUL, 0x7d084f8aUL,
    0x6fbde064UL, 0xd7018701UL, 0x4ad6bfb8UL, 0xf26ad8ddUL, 0xe0df7733UL,
    0x58631056UL, 0x5019579fUL, 0xe8a530faUL, 0xfa109f14UL, 0x42acf871UL,
    0xdf7bc0c8UL, 0x67c7a7adUL, 0x75720843UL, 0xcdce6f26UL, 0x95ad7f70UL,
    0x2d111815UL, 0x3fa4b7fbUL, 0x8718d09eUL, 0x1acfe827UL, 0xa2738f42UL,
    0xb0c620acUL, 0x087a47c9UL, 0xa032af3eUL, 0x188ec85bUL, 0x0a3b67b5UL,
    0xb28700d0UL, 0x2f503869UL, 0x97ec5f0cUL, 0x8559f0e2UL, 0x3de59787UL,
    0x658687d1UL, 0xdd3ae0b4UL, 0xcf8f4f5aUL, 0x7733283fUL, 0xeae41086UL,
    0x525877e3UL, 0x40edd80dUL, 0xf851bf68UL, 0xf02bf8a1UL, 0x48979fc4UL,
    0x5a22302aUL, 0xe29e574fUL, 0x7f496ff6UL, 0xc7f50893UL, 0xd540a77dUL,
    0x6dfcc018UL, 0x359fd04eUL, 0x8d23b72bUL, 0x9f9618c5UL, 0x272a7fa0UL,
    0xbafd4719UL, 0x0241207cUL, 0x10f48f92UL, 0xa848e8f7UL, 0x9b14583dUL,
    0x23a83f58UL, 0x311d90b6UL, 0x89a1f7d3UL, 0x1476cf6aUL, 0xaccaa80fUL,
    0xbe7f07e1UL, 0x06c36084UL, 0x5ea070d2UL, 0xe61c17b7UL, 0xf4a9b859UL,
    0x4c15df3cUL, 0xd1c2e785UL, 0x697e80e0UL, 0x7bcb2f0eUL, 0xc377486bUL,
    0xcb0d0fa2UL, 0x73b168c7UL, 0x6104c729UL, 0xd9b8a04cUL, 0x446f98f5UL,
    0xfcd3ff90UL, 0xee66507eUL, 0x56da371bUL, 0x0eb9274dUL, 0xb6054028UL,
    0xa4b0efc6UL, 0x1c0c88a3UL, 0x81dbb01aUL, 0x3967d77fUL, 0x2bd27891UL,
    0x936e1ff4UL, 0x3b26f703UL, 0x839a9066UL, 0x912f3f88UL, 0x299358edUL,
    0xb4446054UL, 0x0cf80731UL, 0x1e4da8dfUL, 0xa6f1cfbaUL, 0xfe92dfecUL,
    0x462eb889UL, 0x549b1767UL, 0xec277002UL, 0x71f048bbUL, 0xc94c2fdeUL,
    0xdbf98030UL, 0x6345e755UL, 0x6b3fa09cUL, 0xd383c7f9UL, 0xc1366817UL,
    0x798a0f72UL, 0xe45d37cbUL, 0x5ce150aeUL, 0x4e54ff40UL, 0xf6e89825UL,
    0xae8b8873UL, 0x1637ef16UL, 0x048240f8UL, 0xbc3e279dUL, 0x21e91f24UL,
    0x99557841UL, 0x8be0d7afUL, 0x335cb0caUL, 0xed59b63bUL, 0x55e5d15eUL,
    0x47507eb0UL, 0xffec19d5UL, 0x623b216cUL, 0xda874609UL, 0xc832e9e7UL,
    0x708e8e82UL, 0x28ed9ed4UL, 0x9051f9b1UL, 0x82e4565fUL, 0x3a58313aUL,
    0xa78f0983UL, 0x1f336ee6UL, 0x0d86c108UL, 0xb53aa66dUL, 0xbd40e1a4UL,
    0x05fc86c1UL, 0x1749292fUL, 0xaff54e4aUL, 0x322276f3UL, 0x8a9e1196UL,
    0x982bbe78UL, 0x2097d91dUL, 0x78f4c94bUL, 0xc048ae2eUL, 0xd2fd01c0UL,
    0x6a4166a5UL, 0xf7965e1cUL, 0x4f2a3979UL, 0x5d9f9697UL, 0xe523f1f2UL,
    0x4d6b1905UL, 0xf5d77e60UL, 0xe762d18eUL, 0x5fdeb6ebUL, 0xc2098e52UL,
    0x7ab5e937UL, 0x680046d9UL, 0xd0bc21bcUL, 0x88df31eaUL, 0x3063568fUL,
    0x22d6f961UL, 0x9a6a9e04UL, 0x07bda6bdUL, 0xbf01c1d8UL, 0xadb46e36UL,
    0x15080953UL, 0x1d724e9aUL, 0xa5ce29ffUL, 0xb77b8611UL, 0x0fc7e174UL,
    0x9210d9cdUL, 0x2aacbea8UL, 0x38191146UL, 0x80a57623UL, 0xd8c66675UL,
    0x607a0110UL, 0x72cfaefeUL, 0xca73c99bUL, 0x57a4f122UL, 0xef189647UL,
    0xfdad39a9UL, 0x45115eccUL, 0x764dee06UL, 0xcef18963UL, 0xdc44268dUL,
    0x64f841e8UL, 0xf92f7951UL, 0x41931e34UL, 0x5326b1daUL, 0xeb9ad6bfUL,
    0xb3f9c6e9UL, 0x0b45a18cUL, 0x19f00e62UL, 0xa14c6907UL, 0x3c9b51beUL,
    0x842736dbUL, 0x96929935UL, 0x2e2efe50UL, 0x2654b999UL, 0x9ee8defcUL,
    0x8c5d7112UL, 0x34e11677UL, 0xa9362eceUL, 0x118a49abUL, 0x033fe645UL,
    0xbb838120UL, 0xe3e09176UL, 0x5b5cf613UL, 0x49e959fdUL, 0xf1553e98UL,
    0x6c820621UL, 0xd43e6144UL, 0xc68bceaaUL, 0x7e37a9cfUL, 0xd67f4138UL,
    0x6ec3265dUL, 0x7c7689b3UL, 0xc4caeed6UL, 0x591dd66fUL, 0xe1a1b10aUL,
    0xf3141ee4UL, 0x4ba87981UL, 0x13cb69d7UL, 0xab770eb2UL, 0xb9c2a15cUL,
    0x017ec639UL, 0x9ca9fe80UL, 0x241599e5UL, 0x36a0360bUL, 0x8e1c516eUL,
    0x866616a7UL, 0x3eda71c2UL, 0x2c6fde2cUL, 0x94d3b949UL, 0x090481f0UL,
    0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL,
    0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL,
    0xde0506f1UL
  },
  {
    0x00000000UL, 0x96300777UL, 0x2c610eeeUL, 0xba510999UL, 0x19c46d07UL,
    0x8ff46a70UL, 0x35a563e9UL, 0xa395649eUL, 0x3288db0eUL, 0xa4b8dc79UL,
    0x1ee9d5e0UL, 0x88d9d297UL, 0x2b4cb609UL, 0xbd7cb17eUL, 0x072db8e7UL,
    0x911dbf90UL, 0x6410b71dUL, 0xf220b06aUL, 0x4871b9f3UL, 0xde41be84UL,
    0x7dd4da1aUL, 0xebe4dd6dUL, 0x51b5d4f4UL, 0xc785d383UL, 0x56986c13UL,
    0xc0a86b64UL, 0x7af962fdUL, 0xecc9658aUL, 0x4f5c0114UL, 0xd96c0663UL,
    0x633d0ffaUL, 0xf50d088dUL, 0xc8206e3bUL, 0x5e10694cUL, 0xe44160d5UL,
    0x727167a2UL, 0xd1e4033cUL, 0x47d4044bUL, 0xfd850dd2UL, 0x6bb50aa5UL,
    0xfaa8b535UL, 0x6c98b242UL, 0xd6c9bbdbUL, 0x40f9bcacUL, 0xe36cd832UL,
    0x755cdf45UL, 0xcf0dd6dcUL, 0x593dd1abUL, 0xac30d926UL, 0x3a00de51UL,
    0x8051d7c8UL, 0x1661d0bfUL, 0xb5f4b421UL, 0x23c4b356UL, 0x9995bacfUL,
    0x0fa5bdb8UL, 0x9eb80228UL, 0x0888055fUL, 0xb2d90cc6UL, 0x24e90bb1UL,
    0x877c6f2fUL, 0x114c6858UL, 0xab1d61c1UL, 0x3d2d66b6UL, 0x9041dc76UL,
    0x0671db01UL, 0xbc20d298UL, 0x2a10d5efUL, 0x8985b171UL, 0x1fb5b606UL,
    0xa5e4bf9fUL, 0x33d4b8e8UL, 0xa2c90778UL, 0x34f9000fUL, 0x8ea80996UL,
    0x18980ee1UL, 0xbb0d6a7fUL, 0x2d3d6d08UL, 0x976c6491UL, 0x015c63e6UL,
    0xf4516b6bUL, 0x62616c1cUL, 0xd8306585UL, 0x4e0062f2UL, 0xed95066cUL,
    0x7ba5011bUL, 0xc1f40882UL, 0x57c40ff5UL, 0xc6d9b065UL, 0x50e9b712UL,
    0xeab8be8bUL, 0x7c88b9fcUL, 0xdf1ddd62UL, 0x492dda15UL, 0xf37cd38cUL,
    0x654cd4fbUL, 0x5861b24dUL, 0xce51b53aUL, 0x7400bca3UL, 0xe230bbd4UL,
    0x41a5df4aUL, 0xd795d83dUL, 0x6dc4d1a4UL, 0xfbf4d6d3UL, 0x6ae96943UL,
    0xfcd96e34UL, 0x468867adUL, 0xd0b860daUL, 0x732d0444UL, 0xe51d0333UL,
    0x5f4c0aaaUL, 0xc97c0dddUL, 0x3c710550UL, 0xaa410227UL, 0x10100bbeUL,
    0x86200cc9UL, 0x25b56857UL, 0xb3856f20UL, 0x09d466b9UL, 0x9fe461ceUL,
    0x0ef9de5eUL, 0x98c9d929UL, 0x2298d0b0UL, 0xb4a8d7c7UL, 0x173db359UL,
    0x810db42eUL, 0x3b5cbdb7UL, 0xad6cbac0UL, 0x2083b8edUL, 0xb6b3bf9aUL,
    0x0ce2b603UL, 0x9ad2b174UL, 0x3947d5eaUL, 0xaf77d29dUL, 0x1526db04UL,
    0x8316dc73UL, 0x120b63e3UL, 0x843b6494UL, 0x3e6a6d0dUL, 0xa85a6a7aUL,
    0x0bcf0ee4UL, 0x9dff0993UL, 0x27ae000aUL, 0xb19e077dUL, 0x44930ff0UL,
    0xd2a30887UL, 0x68f2011eUL, 0xfec20669UL, 0x5d5762f7UL, 0xcb676580UL,
    0x71366c19UL, 0xe7066b6eUL, 0x761bd4feUL, 0xe02bd389UL, 0x5a7ada10UL,
    0xcc4add67UL, 0x6fdfb9f9UL, 0xf9efbe8eUL, 0x43beb717UL, 0xd58eb060UL,
    0xe8a3d6d6UL, 0x7e93d1a1UL, 0xc4c2d838UL, 0x52f2df4fUL, 0xf167bbd1UL,
    0x6757bca6UL, 0xdd06b53fUL, 0x4b36b248UL, 0xda2b0dd8UL, 0x4c1b0aafUL,
    0xf64a0336UL, 0x607a0441UL, 0xc3ef60dfUL, 0x55df67a8UL, 0xef8e6e31UL,
    0x79be6946UL, 0x8cb361cbUL, 0x1a8366bcUL, 0xa0d26f25UL, 0x36e26852UL,
    0x95770cccUL, 0x03470bbbUL, 0xb9160222UL, 0x2f260555UL, 0xbe3bbac5UL,
    0x280bbdb2UL, 0x925ab42bUL, 0x046ab35cUL, 0xa7ffd7c2UL, 0x31cfd0b5UL,
    0x8b9ed92cUL, 0x1daede5bUL, 0xb0c2649bUL, 0x26f263ecUL, 0x9ca36a75UL,
    0x0a936d02UL, 0xa906099cUL, 0x3f360eebUL, 0x85670772UL, 0x13570005UL,
    0x824abf95UL, 0x147ab8e2UL, 0xae2bb17bUL, 0x381bb60cUL, 0x9b8ed292UL,
    0x0dbed5e5UL, 0xb7efdc7cUL, 0x21dfdb0bUL, 0xd4d2d386UL, 0x42e2d4f1UL,
    0xf8b3dd68UL, 0x6e83da1fUL, 0xcd16be81UL, 0x5b26b9f6UL, 0xe177b06fUL,
    0x7747b718UL, 0xe65a0888UL, 0x706a0fffUL, 0xca3b0666UL, 0x5c0b0111UL,
    0xff9e658fUL, 0x69ae62f8UL, 0xd3ff6b61UL, 0x45cf6c16UL, 0x78e20aa0UL,
    0xeed20dd7UL, 0x5483044eUL, 0xc2b30339UL, 0x612667a7UL, 0xf71660d0UL,
    0x4d476949UL, 0xdb776e3eUL, 0x4a6ad1aeUL, 0xdc5ad6d9UL, 0x660bdf40UL,
    0xf03bd837UL, 0x53aebca9UL, 0xc59ebbdeUL, 0x7fcfb247UL, 0xe9ffb530UL,
    0x1cf2bdbdUL, 0x8ac2bacaUL, 0x3093b353UL, 0xa6a3b424UL, 0x0536d0baUL,
    0x9306d7cdUL, 0x2957de54UL, 0xbf67d923UL, 0x2e7a66b3UL, 0xb84a61c4UL,
    0x021b685dUL, 0x942b6f2aUL, 0x37be0bb4UL, 0xa18e0cc3UL, 0x1bdf055aUL,
    0x8def022dUL
  },
  {
    0x00000000UL, 0x41311b19UL, 0x82623632UL, 0xc3532d2bUL, 0x04c56c64UL,
   {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,
    0x45f4777dUL, 0x86a75a56UL, 0xc796414fUL, 0x088ad9c8UL, 0x49bbc2d1UL,
    0x8ae8effaUL, 0xcbd9f4e3UL, 0x0c4fb5acUL, 0x4d7eaeb5UL, 0x8e2d839eUL,
    0xcf1c9887UL, 0x5112c24aUL, 0x1023d953UL, 0xd370f478UL, 0x9241ef61UL,
    0x55d7ae2eUL, 0x14e6b537UL, 0xd7b5981cUL, 0x96848305UL, 0x59981b82UL,
    0x18a9009bUL, 0xdbfa2db0UL, 0x9acb36a9UL, 0x5d5d77e6UL, 0x1c6c6cffUL,
    0xdf3f41d4UL, 0x9e0e5acdUL, 0xa2248495UL, 0xe3159f8cUL, 0x2046b2a7UL,
    0x6177a9beUL, 0xa6e1e8f1UL, 0xe7d0f3e8UL, 0x2483dec3UL, 0x65b2c5daUL,
    0xaaae5d5dUL, 0xeb9f4644UL, 0x28cc6b6fUL, 0x69fd7076UL, 0xae6b3139UL,
    0xef5a2a20UL, 0x2c09070bUL, 0x6d381c12UL, 0xf33646dfUL, 0xb2075dc6UL,
    0x715470edUL, 0x30656bf4UL, 0xf7f32abbUL, 0xb6c231a2UL, 0x75911c89UL,
    0x34a00790UL, 0xfbbc9f17UL, 0xba8d840eUL, 0x79dea925UL, 0x38efb23cUL,
    0xff79f373UL, 0xbe48e86aUL, 0x7d1bc541UL, 0x3c2ade58UL, 0x054f79f0UL,
    0x447e62e9UL, 0x872d4fc2UL, 0xc61c54dbUL, 0x018a1594UL, 0x40bb0e8dUL,
    0x83e823a6UL, 0xc2d938bfUL, 0x0dc5a038UL, 0x4cf4bb21UL, 0x8fa7960aUL,
    0xce968d13UL, 0x0900cc5cUL, 0x4831d745UL, 0x8b62fa6eUL, 0xca53e177UL,
    0x545dbbbaUL, 0x156ca0a3UL, 0xd63f8d88UL, 0x970e9691UL, 0x5098d7deUL,
    0x11a9ccc7UL, 0xd2fae1ecUL, 0x93cbfaf5UL, 0x5cd76272UL, 0x1de6796bUL,
    0xdeb55440UL, 0x9f844f59UL, 0x58120e16UL, 0x1923150fUL, 0xda703824UL,
    0x9b41233dUL, 0xa76bfd65UL, 0xe65ae67cUL, 0x2509cb57UL, 0x6438d04eUL,
    0xa3ae9101UL, 0xe29f8a18UL, 0x21cca733UL, 0x60fdbc2aUL, 0xafe124adUL,
    0xeed03fb4UL, 0x2d83129fUL, 0x6cb20986UL, 0xab2448c9UL, 0xea1553d0UL,
    0x29467efbUL, 0x687765e2UL, 0xf6793f2fUL, 0xb7482436UL, 0x741b091dUL,
    0x352a1204UL, 0xf2bc534bUL, 0xb38d4852UL, 0x70de6579UL, 0x31ef7e60UL,
    0xfef3e6e7UL, 0xbfc2fdfeUL, 0x7c91d0d5UL, 0x3da0cbccUL, 0xfa368a83UL,
    0xbb07919aUL, 0x7854bcb1UL, 0x3965a7a8UL, 0x4b98833bUL, 0x0aa99822UL,
    0xc9fab509UL, 0x88cbae10UL, 0x4f5def5fUL, 0x0e6cf446UL, 0xcd3fd96dUL,
    0x8c0ec274UL, 0x43125af3UL, 0x022341eaUL, 0xc1706cc1UL, 0x804177d8UL,
    0x47d73697UL, 0x06e62d8eUL, 0xc5b500a5UL, 0x84841bbcUL, 0x1a8a4171UL,
    0x5bbb5a68UL, 0x98e87743UL, 0xd9d96c5aUL, 0x1e4f2d15UL, 0x5f7e360cUL,
    0x9c2d1b27UL, 0xdd1c003eUL, 0x120098b9UL, 0x533183a0UL, 0x9062ae8bUL,
    0xd153b592UL, 0x16c5f4ddUL, 0x57f4efc4UL, 0x94a7c2efUL, 0xd596d9f6UL,
    0xe9bc07aeUL, 0xa88d1cb7UL, 0x6bde319cUL, 0x2aef2a85UL, 0xed796bcaUL,
    0xac4870d3UL, 0x6f1b5df8UL, 0x2e2a46e1UL, 0xe136de66UL, 0xa007c57fUL,
    0x6354e854UL, 0x2265f34dUL, 0xe5f3b202UL, 0xa4c2a91bUL, 0x67918430UL,
    0x26a09f29UL, 0xb8aec5e4UL, 0xf99fdefdUL, 0x3accf3d6UL, 0x7bfde8cfUL,
    0xbc6ba980UL, 0xfd5ab299UL, 0x3e099fb2UL, 0x7f3884abUL, 0xb0241c2cUL,
    0xf1150735UL, 0x32462a1eUL, 0x73773107UL, 0xb4e17048UL, 0xf5d06b51UL,
    0x3683467aUL, 0x77b25d63UL, 0x4ed7facbUL, 0x0fe6e1d2UL, 0xccb5ccf9UL,
    0x8d84d7e0UL, 0x4a1296afUL, 0x0b238db6UL, 0xc870a09dUL, 0x8941bb84UL,
    0x465d2303UL, 0x076c381aUL, 0xc43f1531UL, 0x850e0e28UL, 0x42984f67UL,
    0x03a9547eUL, 0xc0fa7955UL, 0x81cb624cUL, 0x1fc53881UL, 0x5ef42398UL,
    0x9da70eb3UL, 0xdc9615aaUL, 0x1b0054e5UL, 0x5a314ffcUL, 0x996262d7UL,
    0xd85379ceUL, 0x174fe149UL, 0x567efa50UL, 0x952dd77bUL, 0xd41ccc62UL,
    0x138a8d2dUL, 0x52bb9634UL, 0x91e8bb1fUL, 0xd0d9a006UL, 0xecf37e5eUL,
    0xadc26547UL, 0x6e91486cUL, 0x2fa05375UL, 0xe836123aUL, 0xa9070923UL,
    0x6a542408UL, 0x2b653f11UL, 0xe479a796UL, 0xa548bc8fUL, 0x661b91a4UL,
    0x272a8abdUL, 0xe0bccbf2UL, 0xa18dd0ebUL, 0x62defdc0UL, 0x23efe6d9UL,
    0xbde1bc14UL, 0xfcd0a70dUL, 0x3f838a26UL, 0x7eb2913fUL, 0xb924d070UL,
    0xf815cb69UL, 0x3b46e642UL, 0x7a77fd5bUL, 0xb56b65dcUL, 0xf45a7ec5UL,
    0x370953eeUL, 0x763848f7UL, 0xb1ae09b8UL, 0xf09f12a1UL, 0x33cc3f8aUL,
    0x72fd2493UL
  },
    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},
  {
    0x00000000UL, 0x376ac201UL, 0x6ed48403UL, 0x59be4602UL, 0xdca80907UL,
   {0x0000000000000000, 0xe2fd888d00000000, 0x85fd60c000000000,
    0xebc2cb06UL, 0xb27c8d04UL, 0x85164f05UL, 0xb851130eUL, 0x8f3bd10fUL,
    0xd685970dUL, 0xe1ef550cUL, 0x64f91a09UL, 0x5393d808UL, 0x0a2d9e0aUL,
    0x3d475c0bUL, 0x70a3261cUL, 0x47c9e41dUL, 0x1e77a21fUL, 0x291d601eUL,
    0xac0b2f1bUL, 0x9b61ed1aUL, 0xc2dfab18UL, 0xf5b56919UL, 0xc8f23512UL,
    0xff98f713UL, 0xa626b111UL, 0x914c7310UL, 0x145a3c15UL, 0x2330fe14UL,
    0x7a8eb816UL, 0x4de47a17UL, 0xe0464d38UL, 0xd72c8f39UL, 0x8e92c93bUL,
    0xb9f80b3aUL, 0x3cee443fUL, 0x0b84863eUL, 0x523ac03cUL, 0x6550023dUL,
    0x58175e36UL, 0x6f7d9c37UL, 0x36c3da35UL, 0x01a91834UL, 0x84bf5731UL,
    0xb3d59530UL, 0xea6bd332UL, 0xdd011133UL, 0x90e56b24UL, 0xa78fa925UL,
    0xfe31ef27UL, 0xc95b2d26UL, 0x4c4d6223UL, 0x7b27a022UL, 0x2299e620UL,
    0x15f32421UL, 0x28b4782aUL, 0x1fdeba2bUL, 0x4660fc29UL, 0x710a3e28UL,
    0xf41c712dUL, 0xc376b32cUL, 0x9ac8f52eUL, 0xada2372fUL, 0xc08d9a70UL,
    0xf7e75871UL, 0xae591e73UL, 0x9933dc72UL, 0x1c259377UL, 0x2b4f5176UL,
    0x72f11774UL, 0x459bd575UL, 0x78dc897eUL, 0x4fb64b7fUL, 0x16080d7dUL,
    0x2162cf7cUL, 0xa4748079UL, 0x931e4278UL, 0xcaa0047aUL, 0xfdcac67bUL,
    0xb02ebc6cUL, 0x87447e6dUL, 0xdefa386fUL, 0xe990fa6eUL, 0x6c86b56bUL,
    0x5bec776aUL, 0x02523168UL, 0x3538f369UL, 0x087faf62UL, 0x3f156d63UL,
    0x66ab2b61UL, 0x51c1e960UL, 0xd4d7a665UL, 0xe3bd6464UL, 0xba032266UL,
    0x8d69e067UL, 0x20cbd748UL, 0x17a11549UL, 0x4e1f534bUL, 0x7975914aUL,
    0xfc63de4fUL, 0xcb091c4eUL, 0x92b75a4cUL, 0xa5dd984dUL, 0x989ac446UL,
    0xaff00647UL, 0xf64e4045UL, 0xc1248244UL, 0x4432cd41UL, 0x73580f40UL,
    0x2ae64942UL, 0x1d8c8b43UL, 0x5068f154UL, 0x67023355UL, 0x3ebc7557UL,
    0x09d6b756UL, 0x8cc0f853UL, 0xbbaa3a52UL, 0xe2147c50UL, 0xd57ebe51UL,
    0xe839e25aUL, 0xdf53205bUL, 0x86ed6659UL, 0xb187a458UL, 0x3491eb5dUL,
    0x03fb295cUL, 0x5a456f5eUL, 0x6d2fad5fUL, 0x801b35e1UL, 0xb771f7e0UL,
    0xeecfb1e2UL, 0xd9a573e3UL, 0x5cb33ce6UL, 0x6bd9fee7UL, 0x3267b8e5UL,
    0x050d7ae4UL, 0x384a26efUL, 0x0f20e4eeUL, 0x569ea2ecUL, 0x61f460edUL,
    0xe4e22fe8UL, 0xd388ede9UL, 0x8a36abebUL, 0xbd5c69eaUL, 0xf0b813fdUL,
    0xc7d2d1fcUL, 0x9e6c97feUL, 0xa90655ffUL, 0x2c101afaUL, 0x1b7ad8fbUL,
    0x42c49ef9UL, 0x75ae5cf8UL, 0x48e900f3UL, 0x7f83c2f2UL, 0x263d84f0UL,
    0x115746f1UL, 0x944109f4UL, 0xa32bcbf5UL, 0xfa958df7UL, 0xcdff4ff6UL,
    0x605d78d9UL, 0x5737bad8UL, 0x0e89fcdaUL, 0x39e33edbUL, 0xbcf571deUL,
    0x8b9fb3dfUL, 0xd221f5ddUL, 0xe54b37dcUL, 0xd80c6bd7UL, 0xef66a9d6UL,
    0xb6d8efd4UL, 0x81b22dd5UL, 0x04a462d0UL, 0x33cea0d1UL, 0x6a70e6d3UL,
    0x5d1a24d2UL, 0x10fe5ec5UL, 0x27949cc4UL, 0x7e2adac6UL, 0x494018c7UL,
    0xcc5657c2UL, 0xfb3c95c3UL, 0xa282d3c1UL, 0x95e811c0UL, 0xa8af4dcbUL,
    0x9fc58fcaUL, 0xc67bc9c8UL, 0xf1110bc9UL, 0x740744ccUL, 0x436d86cdUL,
    0x1ad3c0cfUL, 0x2db902ceUL, 0x4096af91UL, 0x77fc6d90UL, 0x2e422b92UL,
    0x1928e993UL, 0x9c3ea696UL, 0xab546497UL, 0xf2ea2295UL, 0xc580e094UL,
    0xf8c7bc9fUL, 0xcfad7e9eUL, 0x9613389cUL, 0xa179fa9dUL, 0x246fb598UL,
    0x13057799UL, 0x4abb319bUL, 0x7dd1f39aUL, 0x3035898dUL, 0x075f4b8cUL,
    0x5ee10d8eUL, 0x698bcf8fUL, 0xec9d808aUL, 0xdbf7428bUL, 0x82490489UL,
    0xb523c688UL, 0x88649a83UL, 0xbf0e5882UL, 0xe6b01e80UL, 0xd1dadc81UL,
    0x54cc9384UL, 0x63a65185UL, 0x3a181787UL, 0x0d72d586UL, 0xa0d0e2a9UL,
    0x97ba20a8UL, 0xce0466aaUL, 0xf96ea4abUL, 0x7c78ebaeUL, 0x4b1229afUL,
    0x12ac6fadUL, 0x25c6adacUL, 0x1881f1a7UL, 0x2feb33a6UL, 0x765575a4UL,
    0x413fb7a5UL, 0xc429f8a0UL, 0xf3433aa1UL, 0xaafd7ca3UL, 0x9d97bea2UL,
    0xd073c4b5UL, 0xe71906b4UL, 0xbea740b6UL, 0x89cd82b7UL, 0x0cdbcdb2UL,
    0x3bb10fb3UL, 0x620f49b1UL, 0x55658bb0UL, 0x6822d7bbUL, 0x5f4815baUL,
    0x06f653b8UL, 0x319c91b9UL, 0xb48adebcUL, 0x83e01cbdUL, 0xda5e5abfUL,
    0xed3498beUL
  },
  {
    0x00000000UL, 0x6567bcb8UL, 0x8bc809aaUL, 0xeeafb512UL, 0x5797628fUL,
    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,
    0x32f0de37UL, 0xdc5f6b25UL, 0xb938d79dUL, 0xef28b4c5UL, 0x8a4f087dUL,
    0x64e0bd6fUL, 0x018701d7UL, 0xb8bfd64aUL, 0xddd86af2UL, 0x3377dfe0UL,
    0x56106358UL, 0x9f571950UL, 0xfa30a5e8UL, 0x149f10faUL, 0x71f8ac42UL,
    0xc8c07bdfUL, 0xada7c767UL, 0x43087275UL, 0x266fcecdUL, 0x707fad95UL,
    0x1518112dUL, 0xfbb7a43fUL, 0x9ed01887UL, 0x27e8cf1aUL, 0x428f73a2UL,
    0xac20c6b0UL, 0xc9477a08UL, 0x3eaf32a0UL, 0x5bc88e18UL, 0xb5673b0aUL,
    0xd00087b2UL, 0x6938502fUL, 0x0c5fec97UL, 0xe2f05985UL, 0x8797e53dUL,
    0xd1878665UL, 0xb4e03addUL, 0x5a4f8fcfUL, 0x3f283377UL, 0x8610e4eaUL,
    0xe3775852UL, 0x0dd8ed40UL, 0x68bf51f8UL, 0xa1f82bf0UL, 0xc49f9748UL,
    0x2a30225aUL, 0x4f579ee2UL, 0xf66f497fUL, 0x9308f5c7UL, 0x7da740d5UL,
    0x18c0fc6dUL, 0x4ed09f35UL, 0x2bb7238dUL, 0xc518969fUL, 0xa07f2a27UL,
    0x1947fdbaUL, 0x7c204102UL, 0x928ff410UL, 0xf7e848a8UL, 0x3d58149bUL,
    0x583fa823UL, 0xb6901d31UL, 0xd3f7a189UL, 0x6acf7614UL, 0x0fa8caacUL,
    0xe1077fbeUL, 0x8460c306UL, 0xd270a05eUL, 0xb7171ce6UL, 0x59b8a9f4UL,
    0x3cdf154cUL, 0x85e7c2d1UL, 0xe0807e69UL, 0x0e2fcb7bUL, 0x6b4877c3UL,
    0xa20f0dcbUL, 0xc768b173UL, 0x29c70461UL, 0x4ca0b8d9UL, 0xf5986f44UL,
    0x90ffd3fcUL, 0x7e5066eeUL, 0x1b37da56UL, 0x4d27b90eUL, 0x284005b6UL,
    0xc6efb0a4UL, 0xa3880c1cUL, 0x1ab0db81UL, 0x7fd76739UL, 0x9178d22bUL,
    0xf41f6e93UL, 0x03f7263bUL, 0x66909a83UL, 0x883f2f91UL, 0xed589329UL,
    0x546044b4UL, 0x3107f80cUL, 0xdfa84d1eUL, 0xbacff1a6UL, 0xecdf92feUL,
    0x89b82e46UL, 0x67179b54UL, 0x027027ecUL, 0xbb48f071UL, 0xde2f4cc9UL,
    0x3080f9dbUL, 0x55e74563UL, 0x9ca03f6bUL, 0xf9c783d3UL, 0x176836c1UL,
    0x720f8a79UL, 0xcb375de4UL, 0xae50e15cUL, 0x40ff544eUL, 0x2598e8f6UL,
    0x73888baeUL, 0x16ef3716UL, 0xf8408204UL, 0x9d273ebcUL, 0x241fe921UL,
    0x41785599UL, 0xafd7e08bUL, 0xcab05c33UL, 0x3bb659edUL, 0x5ed1e555UL,
    0xb07e5047UL, 0xd519ecffUL, 0x6c213b62UL, 0x094687daUL, 0xe7e932c8UL,
    0x828e8e70UL, 0xd49eed28UL, 0xb1f95190UL, 0x5f56e482UL, 0x3a31583aUL,
    0x83098fa7UL, 0xe66e331fUL, 0x08c1860dUL, 0x6da63ab5UL, 0xa4e140bdUL,
    0xc186fc05UL, 0x2f294917UL, 0x4a4ef5afUL, 0xf3762232UL, 0x96119e8aUL,
    0x78be2b98UL, 0x1dd99720UL, 0x4bc9f478UL, 0x2eae48c0UL, 0xc001fdd2UL,
    0xa566416aUL, 0x1c5e96f7UL, 0x79392a4fUL, 0x97969f5dUL, 0xf2f123e5UL,
    0x05196b4dUL, 0x607ed7f5UL, 0x8ed162e7UL, 0xebb6de5fUL, 0x528e09c2UL,
    0x37e9b57aUL, 0xd9460068UL, 0xbc21bcd0UL, 0xea31df88UL, 0x8f566330UL,
    0x61f9d622UL, 0x049e6a9aUL, 0xbda6bd07UL, 0xd8c101bfUL, 0x366eb4adUL,
    0x53090815UL, 0x9a4e721dUL, 0xff29cea5UL, 0x11867bb7UL, 0x74e1c70fUL,
    0xcdd91092UL, 0xa8beac2aUL, 0x46111938UL, 0x2376a580UL, 0x7566c6d8UL,
    0x10017a60UL, 0xfeaecf72UL, 0x9bc973caUL, 0x22f1a457UL, 0x479618efUL,
    0xa939adfdUL, 0xcc5e1145UL, 0x06ee4d76UL, 0x6389f1ceUL, 0x8d2644dcUL,
    0xe841f864UL, 0x51792ff9UL, 0x341e9341UL, 0xdab12653UL, 0xbfd69aebUL,
    0xe9c6f9b3UL, 0x8ca1450bUL, 0x620ef019UL, 0x07694ca1UL, 0xbe519b3cUL,
    0xdb362784UL, 0x35999296UL, 0x50fe2e2eUL, 0x99b95426UL, 0xfcdee89eUL,
    0x12715d8cUL, 0x7716e134UL, 0xce2e36a9UL, 0xab498a11UL, 0x45e63f03UL,
    0x208183bbUL, 0x7691e0e3UL, 0x13f65c5bUL, 0xfd59e949UL, 0x983e55f1UL,
    0x2106826cUL, 0x44613ed4UL, 0xaace8bc6UL, 0xcfa9377eUL, 0x38417fd6UL,
    0x5d26c36eUL, 0xb389767cUL, 0xd6eecac4UL, 0x6fd61d59UL, 0x0ab1a1e1UL,
    0xe41e14f3UL, 0x8179a84bUL, 0xd769cb13UL, 0xb20e77abUL, 0x5ca1c2b9UL,
    0x39c67e01UL, 0x80fea99cUL, 0xe5991524UL, 0x0b36a036UL, 0x6e511c8eUL,
    0xa7166686UL, 0xc271da3eUL, 0x2cde6f2cUL, 0x49b9d394UL, 0xf0810409UL,
    0x95e6b8b1UL, 0x7b490da3UL, 0x1e2eb11bUL, 0x483ed243UL, 0x2d596efbUL,
    0xc3f6dbe9UL, 0xa6916751UL, 0x1fa9b0ccUL, 0x7ace0c74UL, 0x9461b966UL,
    0xf10605deUL
    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};

Changes to compat/zlib/deflate.c.

1
2

3
4
5
6
7
8
9
1

2
3
4
5
6
7
8
9

-
+







/* deflate.c -- compress data using the deflation algorithm
 * Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler
 * 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
48
49
50
51
52
53
54
55

56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73

74
75
76
77
78
79
80


81
82

83
84
85


86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
48
49
50
51
52
53
54

55
56
57
58
59
60
61
62



63
64
65
66
67
68
69

70
71
72





73
74
75

76
77


78
79
















80
81
82
83
84
85
86







-
+







-
-
-







-
+


-
-
-
-
-
+
+

-
+

-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-







 */

/* @(#) $Id$ */

#include "deflate.h"

const char deflate_copyright[] =
   " deflate 1.2.11 Copyright 1995-2017 Jean-loup Gailly and Mark Adler ";
   " 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.
 */

/* ===========================================================================
 *  Function prototypes.
 */
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) OF((deflate_state *s, int flush));
typedef block_state (*compress_func)(deflate_state *s, int flush);
/* Compression function. Returns the block state after the call. */

local int deflateStateCheck      OF((z_streamp strm));
local void slide_hash     OF((deflate_state *s));
local void fill_window    OF((deflate_state *s));
local block_state deflate_stored OF((deflate_state *s, int flush));
local block_state deflate_fast   OF((deflate_state *s, int flush));
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   OF((deflate_state *s, int flush));
local block_state deflate_slow(deflate_state *s, int flush);
#endif
local block_state deflate_rle    OF((deflate_state *s, int flush));
local block_state deflate_huff   OF((deflate_state *s, int flush));
local block_state deflate_rle(deflate_state *s, int flush);
local block_state deflate_huff(deflate_state *s, int flush);
local void lm_init        OF((deflate_state *s));
local void putShortMSB    OF((deflate_state *s, uInt b));
local void flush_pending  OF((z_streamp strm));
local unsigned read_buf   OF((z_streamp strm, Bytef *buf, unsigned size));
#ifdef ASMV
#  pragma message("Assembler code may have bugs -- use at your own risk")
      void match_init OF((void)); /* asm code initialization */
      uInt longest_match  OF((deflate_state *s, IPos cur_match));
#else
local uInt longest_match  OF((deflate_state *s, IPos cur_match));
#endif

#ifdef ZLIB_DEBUG
local  void check_match OF((deflate_state *s, IPos start, IPos match,
                            int length));
#endif

/* ===========================================================================
 * Local data
 */

#define NIL 0
/* Tail of hash chains */
156
157
158
159
160
161
162
163

164
165
166
167
168
169
170
134
135
136
137
138
139
140

141
142
143
144
145
146
147
148







-
+








/* ===========================================================================
 * 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)
#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
186
187
188
189
190
191
192

193
194




195
196
197
198
199
200





201

202
203
204
205
206
207
208
209
210
164
165
166
167
168
169
170
171


172
173
174
175
176
177
178
179
180
181
182
183
184
185
186

187


188
189
190
191
192
193
194







+
-
-
+
+
+
+






+
+
+
+
+
-
+
-
-







#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));
        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(s)
local void slide_hash(deflate_state *s) {
    deflate_state *s;
{
    unsigned n, m;
    Posf *p;
    uInt wsize = s->w_size;

    n = s->hash_size;
    p = &s->head[n];
    do {
219
220
221
222
223
224
225































































































































































226
227
228
229
230
231
232


233
234
235
236
237
238
239
240

241
242
243
244
245

246
247
248

249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370





371
372

373
374
375
376
377
378

379





380



381


382
383
384
385





386
387
388
389
390
391
392







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


-
-
-
-
-
+
+
-






-
+
-
-
-
-
-
+
-
-
-
+
-
-




-
-
-
-
-







        *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_(strm, level, version, stream_size)
    z_streamp strm;
    int level;
    const char *version;
    int stream_size;
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_(strm, level, method, windowBits, memLevel, strategy,
int ZEXPORT deflateInit2_(z_streamp strm, int level, int method,
                  version, stream_size)
    z_streamp strm;
    int  level;
    int  method;
    int  windowBits;
                          int windowBits, int memLevel, int strategy,
    int  memLevel;
    int  strategy;
    const char *version;
                          const char *version, int stream_size) {
    int stream_size;
{
    deflate_state *s;
    int wrap = 1;
    static const char my_version[] = ZLIB_VERSION;

    ushf *overlay;
    /* We overlay pending_buf and d_buf+l_buf. This works since the average
     * output size for (length,distance) codes is <= 24 bits.
     */

    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;
283
284
285
286
287
288
289


290
291
292
293
294
295
296
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424







+
+







    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;
    }
312
313
314
315
316
317
318
319

320
321
322
323
324
325
326
327
328






































329
330
331



332
333
334
335
336
337
338
339

340
341











342
343
344
345
346
347
348
349
350
351
352
353

354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376

377
378
379

380
381
382
383
384
385
386
387
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







-
+









+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+








+
-
-
+
+
+
+
+
+
+
+
+
+
+











-
+
-
-




















-
+
-
-
-
+
-







    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->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.
     */
    overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2);
    s->pending_buf = (uchf *) overlay;
    s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L);

    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 = overlay + s->lit_bufsize/sizeof(ush);
    s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize;
    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 (strm)
local int deflateStateCheck(z_streamp strm) {
    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 (strm, dictionary, dictLength)
int ZEXPORT deflateSetDictionary(z_streamp strm, const Bytef *dictionary,
    z_streamp strm;
    const Bytef *dictionary;
    uInt  dictLength;
                                 uInt  dictLength) {
{
    deflate_state *s;
    uInt str, n;
    int wrap;
    unsigned avail;
    z_const unsigned char *next;

    if (deflateStateCheck(strm) || dictionary == Z_NULL)
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
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







-
+
-
-
-
+
-

















-
+
-
-







    strm->next_in = next;
    strm->avail_in = avail;
    s->wrap = wrap;
    return Z_OK;
}

/* ========================================================================= */
int ZEXPORT deflateGetDictionary (strm, dictionary, dictLength)
int ZEXPORT deflateGetDictionary(z_streamp strm, Bytef *dictionary,
    z_streamp strm;
    Bytef *dictionary;
    uInt  *dictLength;
                                 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 (strm)
int ZEXPORT deflateResetKeep(z_streamp strm) {
    z_streamp strm;
{
    deflate_state *s;

    if (deflateStateCheck(strm)) {
        return Z_STREAM_ERROR;
    }

    strm->total_in = strm->total_out = 0;
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
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







-
+





-
+






-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+









-
+
-
-
-







-
+
-
-
-
-









-
+
-
-
-
-





+
+
+
+
+
+
-
+

+














-
+
-
-
-
-

















-
+




-
+







    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
        s->wrap ? INIT_STATE : BUSY_STATE;
        INIT_STATE;
    strm->adler =
#ifdef GZIP
        s->wrap == 2 ? crc32(0L, Z_NULL, 0) :
#endif
        adler32(0L, Z_NULL, 0);
    s->last_flush = Z_NO_FLUSH;
    s->last_flush = -2;

    _tr_init(s);

    return Z_OK;
}

/* ========================================================================= */
int ZEXPORT deflateReset (strm)
    z_streamp strm;
{
/* ===========================================================================
 * 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 (strm, head)
int ZEXPORT deflateSetHeader(z_streamp strm, gz_headerp head) {
    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 (strm, pending, bits)
int ZEXPORT deflatePending(z_streamp strm, unsigned *pending, int *bits) {
    unsigned *pending;
    int *bits;
    z_streamp strm;
{
    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 (strm, bits, value)
int ZEXPORT deflatePrime(z_streamp strm, int bits, int value) {
    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 ||
    if ((Bytef *)(s->d_buf) < s->pending_out + ((Buf_size + 7) >> 3))
        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(strm, level, strategy)
int ZEXPORT deflateParams(z_streamp strm, int level, int strategy) {
    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->high_water) {
        s->last_flush != -2) {
        /* Flush the last buffer: */
        int err = deflate(strm, Z_BLOCK);
        if (err == Z_STREAM_ERROR)
            return err;
        if (strm->avail_out == 0)
        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
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
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







-
-
-
+
-
-
+
-
-












-
-
-
+
+
+
-
-

-
-
-
-
+
+
+
+
+
+
+
+

-
-
-
+
+
+
+
+
-
+
+
+
+

-
-
-
-
-
+
+
+
+
+
+
+
-
+
+

-
-
-
+
+
+
+

-
+

-
+







        s->max_chain_length = configuration_table[level].max_chain;
    }
    s->strategy = strategy;
    return Z_OK;
}

/* ========================================================================= */
int ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain)
    z_streamp strm;
    int good_length;
int ZEXPORT deflateTune(z_streamp strm, int good_length, int max_lazy,
    int max_lazy;
    int nice_length;
                        int nice_length, int max_chain) {
    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.
 * They are coded as constants here for a reason--if the #define's are
 * 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.
 * changed, then this function needs to be changed as well.  The return
 * value for 15 and 8 only works for those exact settings.
 *
 * For any setting other than those defaults for windowBits and memLevel,
 * the value returned is a conservative worst case for the maximum expansion
 * resulting from using fixed blocks instead of stored blocks, which deflate
 * can emit on compressed data for some combinations of the parameters.
 * 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.
 *
 * This function could be more sophisticated to provide closer upper bounds for
 * every combination of windowBits and memLevel.  But even the conservative
 * upper bound of about 14% expansion does not seem onerous for output buffer
 * 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
 * allocation.
 * 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(strm, sourceLen)
    z_streamp strm;
    uLong sourceLen;
{
    deflate_state *s;
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 */
    uLong complen, wraplen;
    fixedlen = sourceLen + (sourceLen >> 3) + (sourceLen >> 8) +
               (sourceLen >> 9) + 4;

    /* conservative upper bound for compressed data */
    complen = sourceLen +
              ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 5;
    /* 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 conservative bound plus zlib wrapper */
    /* if can't get parameters, return larger bound plus a zlib wrapper */
    if (deflateStateCheck(strm))
        return complen + 6;
        return (fixedlen > storelen ? fixedlen : storelen) + 6;

    /* compute wrapper length */
    s = strm->state;
    switch (s->wrap) {
    case 0:                                 /* raw deflate */
        wraplen = 0;
        break;
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
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







-
+

+
-
+

-
+
+









-
+
-
-
-










-
+
-
-







        }
        break;
#endif
    default:                                /* for compiler happiness */
        wraplen = 6;
    }

    /* if not default parameters, return conservative bound */
    /* 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) +
        return complen + wraplen;
               wraplen;

    /* default settings: return tight bound for that case */
    /* 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 (s, b)
local void putShortMSB(deflate_state *s, uInt b) {
    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(strm)
local void flush_pending(z_streamp strm) {
    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;
756
757
758
759
760
761
762
763

764
765
766
767
768
769
770
771
772
773
939
940
941
942
943
944
945

946



947
948
949
950
951
952
953







-
+
-
-
-







    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 (strm, flush)
int ZEXPORT deflate(z_streamp strm, int flush) {
    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;
807
808
809
810
811
812
813


814
815
816

817
818
819
820
821
822
823
987
988
989
990
991
992
993
994
995
996
997

998
999
1000
1001
1002
1003
1004
1005







+
+


-
+








    /* 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 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)
1069
1070
1071
1072
1073
1074
1075
1076

1077
1078
1079
1080
1081
1082
1083
1084
1085
1251
1252
1253
1254
1255
1256
1257

1258


1259
1260
1261
1262
1263
1264
1265







-
+
-
-







     * 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 (strm)
int ZEXPORT deflateEnd(z_streamp strm) {
    z_streamp strm;
{
    int status;

    if (deflateStateCheck(strm)) return Z_STREAM_ERROR;

    status = strm->state->status;

    /* Deallocate in reverse order of allocations: */
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
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







-
+
-
-
-

+
+




-



















-
-
+










-
+


+
-
-
+
+
+
+
+









-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-










-
-
-
-
-
+
-
-
-




















-
+


-
+

















-
+
+

















-
+





-
+

-
+






-
-
-
-
+
+
+
+



-
-
+
+
+


-
+




-
-
-
-
+
+
+
+

-
+









-
+








-
+
+











-
+

-
+









-






-
+
-
-
-










-
+
+









-
+









-
+








-
+



















-
+
-
-
-
-

-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+

-
-
+
+



-
+







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-







}

/* =========================================================================
 * 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 (dest, source)
int ZEXPORT deflateCopy(z_streamp dest, z_streamp source) {
    z_streamp dest;
    z_streamp source;
{
#ifdef MAXSEG_64K
    (void)dest;
    (void)source;
    return Z_STREAM_ERROR;
#else
    deflate_state *ds;
    deflate_state *ss;
    ushf *overlay;


    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));
    overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2);
    ds->pending_buf = (uchf *) overlay;
    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, (uInt)ds->pending_buf_size);
    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 = overlay + ds->lit_bufsize/sizeof(ush);
    ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize;
    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 */
}

/* ===========================================================================
 * 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(strm, buf, size)
    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;
}

/* ===========================================================================
 * Initialize the "longest match" routines for a new zlib stream
 */
local void lm_init (s)
    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;
#ifndef FASTEST
#ifdef ASMV
    match_init(); /* initialize the asm code */
#endif
#endif
}

#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.
 */
#ifndef ASMV
/* For 80x86 and 680x0, an optimized version will be provided in match.asm or
 * match.S. The code will be functionally equivalent.
 */
local uInt longest_match(s, cur_match)
local uInt longest_match(deflate_state *s, IPos cur_match) {
    deflate_state *s;
    IPos cur_match;                             /* current 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);
    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_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");
    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 ||
        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
         * 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
         * 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) &&
        } 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");
        /* 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);
        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;
        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
        /* 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.
         * 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");
        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);
            scan_end = *(ushf*)(scan + best_len - 1);
#else
            scan_end1  = scan[best_len-1];
            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;
}
#endif /* ASMV */

#else /* FASTEST */

/* ---------------------------------------------------------------------------
 * Optimized version for FASTEST only
 */
local uInt longest_match(s, cur_match)
local uInt longest_match(deflate_state *s, IPos cur_match) {
    deflate_state *s;
    IPos cur_match;                             /* current 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((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
    /* 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.
     * 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");
    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(s, start, match, length)
local void check_match(deflate_state *s, IPos start, IPos match, int length) {
    deflate_state *s;
    IPos start, match;
    int length;
{
    /* check that the match is indeed a match */
    if (zmemcmp(s->window + match,
                s->window + start, length) != EQUAL) {
        fprintf(stderr, " start %u, match %u, length %d\n",
                start, match, length);
    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, "%c%c", s->window[match++], s->window[start++]);
        } while (--length != 0);
            fprintf(stderr, "(%02x %02x)", *back++, *here++);
        } while (--len != 0);
        z_error("invalid match");
    }
    if (z_verbose > 1) {
        fprintf(stderr,"\\[%d,%d]", start-match, length);
        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 */

/* ===========================================================================
 * 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(s)
    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;
            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");
}

/* ===========================================================================
 * 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] : \
1634
1635
1636
1637
1638
1639
1640
1641

1642
1643

1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1618
1619
1620
1621
1622
1623
1624

1625
1626

1627



1628
1629
1630
1631
1632
1633
1634







-
+

-
+
-
-
-







 * 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 opportunites to have a single copy from next_in to next_out.
 * maximizes the opportunities to have a single copy from next_in to next_out.
 */
local block_state deflate_stored(s, flush)
local block_state deflate_stored(deflate_state *s, int flush) {
    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
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
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







+








+
+



+


-














-
+








+
+






+







        /* 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;
        s->insert += MIN(used, s->w_size - s->insert);
    }
    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 - 1;
    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
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
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







-
+
-
-
-

















-
+







/* ===========================================================================
 * 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(s, flush)
local block_state deflate_fast(deflate_state *s, int flush) {
    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
        /* 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);
        }

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
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







-
+










-
+










-
+










-
+
-
-
-


















-
+







                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]);
                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);
            _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->last_lit)
    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(s, flush)
local block_state deflate_slow(deflate_state *s, int flush) {
    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
        /* 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);
        }

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
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







-
+

-
+



-
+



-
+

















-
-
+
+

















-
-
+
+







-
+










-
+
-
-
-







        /* 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);
            check_match(s, s->strstart - 1, s->prev_match, s->prev_length);

            _tr_tally_dist(s, s->strstart -1 - s->prev_match,
            _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
             * 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->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);
            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);
        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->last_lit)
    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(s, flush)
local block_state deflate_rle(deflate_state *s, int flush) {
    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
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
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







-
+
+














-
+










-
+








-
+
-
-
-
















-
+









-
+



                         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");
            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);
            _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->last_lit)
    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(s, flush)
local block_state deflate_huff(deflate_state *s, int flush) {
    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);
        _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->last_lit)
    if (s->sym_next)
        FLUSH_BLOCK(s, 0);
    return block_done;
}

Changes to compat/zlib/deflate.h.

1
2

3
4
5
6
7
8
9
1

2
3
4
5
6
7
8
9

-
+







/* deflate.h -- internal compression state
 * Copyright (C) 1995-2016 Jean-loup Gailly
 * 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.
 */
18
19
20
21
22
23
24




25
26
27
28
29
30
31
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35







+
+
+
+







/* 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 */
213
214
215
216
217
218
219



220





221
222
223
224
225
226
227
217
218
219
220
221
222
223
224
225
226

227
228
229
230
231
232
233
234
235
236
237
238







+
+
+
-
+
+
+
+
+







     * 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 or lengths */
    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
235
236
237
238
239
240
241
242
243


244
245
246
247
248
249
250
251
252
253
254
255
246
247
248
249
250
251
252


253
254





255
256
257
258
259
260
261







-
-
+
+
-
-
-
-
-







     *     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 last_lit;      /* running index in l_buf */

    uInt sym_next;      /* running index in symbol buffer */
    uInt sym_end;       /* symbol table full when sym_next reaches this */
    ushf *d_buf;
    /* Buffer for distances. To simplify the code, d_buf and l_buf have
     * the same number of elements. To use different lengths, an extra flag
     * array would be necessary.
     */

    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
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306








307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325

326
327
328
329


330
331




















332
333
334
335
336
337



338
339
340
341

342

343
344
345
346
347
348
349
298
299
300
301
302
303
304








305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334


335
336
337

338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361


362
363
364
365
366
367

368
369
370
371
372
373
374
375
376
377







-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+



















+


-
-
+
+

-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+




-
-
+
+
+



-
+

+







 */

#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 OF((deflate_state *s));
int ZLIB_INTERNAL _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc));
void ZLIB_INTERNAL _tr_flush_block OF((deflate_state *s, charf *buf,
                        ulg stored_len, int last));
void ZLIB_INTERNAL _tr_flush_bits OF((deflate_state *s));
void ZLIB_INTERNAL _tr_align OF((deflate_state *s));
void ZLIB_INTERNAL _tr_stored_block OF((deflate_state *s, charf *buf,
                        ulg stored_len, int last));
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->last_lit] = 0; \
    s->l_buf[s->last_lit++] = cc; \
    s->d_buf[s->sym_next] = 0; \
    s->l_buf[s->sym_next++] = cc; \
    s->dyn_ltree[cc].Freq++; \
    flush = (s->last_lit == s->lit_bufsize-1); \
    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->d_buf[s->last_lit] = dist; \
    s->l_buf[s->last_lit++] = len; \
    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->last_lit == s->lit_bufsize-1); \
    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 */

Changes to compat/zlib/examples/README.examples.

30
31
32
33
34
35
36




37
38
39
40
41
42
43
44
45
46

47
48
49
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54







+
+
+
+










+



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

Changes to 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
1



2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24



25
26
27
28
29

30
31
32
33
34

35
36
37

38
39

40
41


42
43
44
45














46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63



64
65
66
67
68
69

70
71

72
73
74

75
76
77
78

79
80
81
82
83
84



85
86
87
88
89
90






91
92
93
94
95
96
97





98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113






114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134

135
136


137
138
139
140
141






142
143
144
145
146
147

148
149



150
151
152
153
154
155
156
157
158

159
160

161
162


163
164
165


166
167
168
169

170

171
172



173
174
175







176
177
178
179
180
181
182
183
184
185







186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219



220













221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260


261









262
263
264
265


266
267
268



269
270
271
272

273
274



275
276
277
278
279
280





281
282
283
284
285
286




287
288
289
290
291


292
293
294
295

296
297
298


299
300
301
302
303




304
305
306
307












308
309
310
311
312
313
314
315

316
317




318
319
320
321
322

323
324

325
326

327
328
329
330
331
332



333
334
335
336
337

338
339

340
341
342

343


344





345
346



347
348
349
350
351


352
353
354
355
356




357
358
359
360


361






362
363


364
365
366

367
368
369

370
371
372
373
374
375
376









377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402


403
404
405
406
407


408
409
410
411



412
413
414
415
416
417





418
419
420
421
422
423


424
425
426
427

428
429
430
431
432

433
434



435
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 Huffman codes, subject to a length limit.
 * Copyright (C) 2007, 2008, 2012 Mark Adler
 * Version 1.4  18 August 2012  Mark Adler
 * 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 Huffman codes for a given number of symbols and a
   maximum code length in bits to determine the maximum table size for zilb's
   inflate.  Only complete Huffman codes are counted.
   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
   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
   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
   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
   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
   remaining.
   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 in the current inflate implementation).  The 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.
   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 2^(root + 1).
   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
   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
   the maximum code length in bits. However this is a very small price to pay
   for the vast speedup.

   First, all of the possible Huffman codes are counted, and reachable
   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
   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 Huffman codes, only about 2x10^6 sub-codes
   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 an unsigned long long 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 unsigned long long arithmetic in that case.  This
   program will abort if an overflow occurs.  The big_t type identifies where
   the counting takes place.
   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.

   An unsigned long long 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.
   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 unsigned long long big_t;   /* type for code counting */
typedef unsigned long long code_t;  /* type for bit pattern counting */
struct tab {                        /* type for been here check */
    size_t len;         /* length of bit vector in char's */
    char *vec;          /* allocated bit vector */
// 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
   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.
   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
   count() for the index, and the calculation of size in main() for the size
   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.
   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 Huffman codes.
   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
   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
   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
   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 beenhere() for the details.
   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 approximately 21 MB, in addition to the 4.3 MB done[]
   vectors grow to total 5.5 MB, in addition to the 4.3 MB done array itself.
   array itself.
 */

/* Globals to avoid propagating constants or constant pointers recursively */
local int max;          /* maximum allowed bit length for the codes */
local int root;         /* size of base code table in bits */
// Type for a variable-length, allocated string.
typedef struct {
    char *str;          // pointer to allocated string
local int large;        /* largest code table so far */
local size_t size;      /* number of elements in num and done */
local int *code;        /* number of symbols assigned to each bit length */
local big_t *num;       /* saved results array for code counting */
local struct tab *done; /* states already evaluated array */

/* Index function for num[] and done[] */
    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;
}

#define INDEX(i,j,k) (((size_t)((i-1)>>1)*((i-2)>>1)+(j>>1)-1)*(max-1)+k-1)

/* Free allocated space.  Uses globals code, num, and done. */
local void cleanup(void)
{
    size_t n;

// 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);
    }
    if (done != NULL) {
        for (n = 0; n < size; n++)
            if (done[n].len)
    va_end(ap);
                free(done[n].vec);
        free(done);
    }
    if (num != NULL)
        free(num);
    if (code != NULL)
        free(code);
}

/* Return the number of possible Huffman 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
}

// 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.
   calculation.  Uses the globals max and num. */
local big_t count(int syms, int len, int left)
local big_t count(int syms, int left, int len) {
{
    big_t sum;          /* number of possible codes from this juncture */
    big_t got;          /* value returned from count() */
    int least;          /* least number of syms to use at this juncture */
    int most;           /* most number of syms to use at this juncture */
    int use;            /* number of bit patterns to use in next call */
    size_t index;       /* index of this case in *num */

    /* see if only one possible code */
    // see if only one possible code
    if (syms == left)
        return 1;

    /* note and verify the expected state */
    assert(syms > left && left > 0 && len < max);
    // note and verify the expected state
    assert(syms > left && left > 0 && len < g.max);

    /* see if we've done this one already */
    index = INDEX(syms, left, len);
    got = num[index];
    // 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 */
        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) */
    least = (left << 1) - syms;
    // 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) */
    most = (((code_t)left << (max - len)) - syms) /
            (((code_t)1 << (max - len)) - 1);
    // 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 */
    sum = 0;
    for (use = least; use <= most; use++) {
        got = count(syms - use, len + 1, (left - use) << 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)0 - 1 || sum < got)   /* overflow */
            return (big_t)0 - 1;
        if (got == (big_t)-1 || sum < got)      // overflow
            return (big_t)-1;
    }

    /* verify that all recursive calls are productive */
    // verify that all recursive calls are productive
    assert(sum != 0);

    /* save the result and return it */
    num[index] = sum;
    // 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 if needed to allow setting the (mem,rem) bit. */
// 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 beenhere(int syms, int len, int left, int mem, int rem)
{
    size_t index;       /* index for this state's bit vector */
    size_t offset;      /* offset in this state's bit vector */
    int bit;            /* mask for this state's bit */
    size_t length;      /* length of the bit vector in bytes */
    char *vector;       /* new or enlarged bit vector */

    /* point to vector for (syms,left,len), bit in vector for (mem,rem) */
    index = INDEX(syms, left, len);
    mem -= 1 << root;
    offset = (mem >> 3) + rem;
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;
    bit = 1 << (mem & 7);
    int bit = 1 << (mem & 7);

    /* see if we've been here */
    length = done[index].len;
    if (offset < length && (done[index].vec[offset] & bit) != 0)
        return 1;       /* done this! */
    // 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 */
    // 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 */
    // 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 */
        // if we have one already, enlarge it, zero out the appended space
        char *vector;
        if (length) {
            do {
                length <<= 1;
            } while (length <= offset);
            vector = realloc(done[index].vec, length);
            if (vector != NULL)
                memset(vector + done[index].len, 0, length - done[index].len);
            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 */
        // otherwise we need to make a new vector and zero it out
        else {
            length = 1 << (len - root);
            length = 16;
            while (length <= offset)
                length <<= 1;
            vector = calloc(length, sizeof(char));
            vector = calloc(length, 1);
        }

            assert(vector != NULL && "out of memory");
        /* in either case, bail if we can't get the memory */
        if (vector == NULL) {
            fputs("abort: unable to allocate enough memory\n", stderr);
            cleanup();
            exit(1);
        }

        /* install the new vector */
        done[index].len = length;
        done[index].vec = vector;
        // install the new vector
        g.done[index].len = length;
        g.done[index].vec = vector;
    }

    /* set the bit */
    done[index].vec[offset] |= bit;
    // 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.  Uses the globals max, code, root, large, and
// 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.
   done. */
local void examine(int syms, int len, int left, int mem, int rem)
local void examine(int syms, int left, int len, int mem, int rem) {
{
    int least;          /* least number of syms to use at this juncture */
    int most;           /* most number of syms to use at this juncture */
    int use;            /* number of bit patterns to use in next call */

    /* see if we have a complete code */
    // see if we have a complete code
    if (syms == left) {
        /* set the last code entry */
        code[len] = left;
        // set the last code entry
        g.code[len] = left;

        /* complete computation of memory used by this code */
        // complete computation of memory used by this code
        while (rem < left) {
            left -= rem;
            rem = 1 << (len - root);
            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, show the entries used and the sub-code */
        if (mem > large) {
            large = mem;
            printf("max %d: ", mem);
            for (use = root + 1; use <= max; use++)
                if (code[use])
                    printf("%d[%d] ", code[use], use);
            putchar('\n');
            fflush(stdout);
            // 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 */
        code[len] = 0;
        // remove entries as we drop back down in the recursion
        g.code[len] = 0;
        return;
    }

    /* prune the tree if we can */
    if (beenhere(syms, len, left, mem, rem))
    // 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) */
    least = (left << 1) - syms;
    // 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) */
    most = (((code_t)left << (max - len)) - syms) /
            (((code_t)1 << (max - len)) - 1);
    // 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 */
    use = least;
    // occupy least table spaces, creating new sub-tables as needed
    int use = least;
    while (rem < use) {
        use -= rem;
        rem = 1 << (len - root);
        rem = 1 << (len - g.root);
        mem += rem;
    }
    rem -= use;

    /* examine codes from here, updating table space as we go */
    // examine codes from here, updating table space as we go
    for (use = least; use <= most; use++) {
        code[len] = use;
        examine(syms - use, len + 1, (left - use) << 1,
                mem + (rem ? 1 << (len - root) : 0), rem << 1);
        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 - root);
            rem = 1 << (len - g.root);
            mem += rem;
        }
        rem--;
    }

    /* remove entries as we drop back down in the recursion */
    code[len] = 0;
    // 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 code that
   requires that maximum.  Uses the globals max, root, and num. */
local void enough(int syms)
// 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) {
{
    int n;              /* number of remaing symbols for this node */
    int left;           /* number of unused bit patterns at this length */
    size_t index;       /* index of this case in *num */

    /* clear code */
    for (n = 0; n <= max; n++)
        code[n] = 0;
    // clear code
    for (int n = 0; n <= g.max; n++)
        g.code[n] = 0;

    /* look at all (root + 1) bit and longer codes */
    large = 1 << root;              /* base table */
    if (root < max)                 /* otherwise, there's only a base table */
        for (n = 3; n <= syms; n++)
            for (left = 2; left < n; left += 2)
    // 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) */
                index = INDEX(n, left, root + 1);
                if (root + 1 < max && num[index])       /* reachable node */
                    examine(n, root + 1, left, 1 << root, 0);
                // 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 (num[index - 1] && n <= left << 1)
                    examine((n - left) << 1, root + 1, (n - left) << 1,
                            1 << 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("done: maximum of %d table entries\n", large);
}


    // done
/*
   Examine and show the total number of possible Huffman 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 entires 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.

    printf("maximum of %d table entries for root = %d\n", g.large, g.root);
   To count and examine Huffman codes that are not length-limited, provide a
   maximum length equal to the number of symbols minus one.

    fputs(g.out.str, stdout);
   For the deflate literal/length code, use "enough".  For the deflate distance
   code, use "enough 30 6".

}
   This uses the %llu printf format to print big_t numbers, which assumes that
   big_t is an unsigned long long.  If the big_t type is changed (for example
   to a multiple precision type), the method of printing will also need to be
   updated.
 */
int main(int argc, char **argv)
{
    int syms;           /* total number of symbols to code */
    int n;              /* number of symbols to code for this run */

// 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
    big_t got;          /* return value of count() */
    big_t sum;          /* accumulated number of codes over n */
    code_t word;        /* for counting bits in code_t */

    /* set up globals for cleanup() */
    code = NULL;
    num = NULL;
    done = NULL;
// 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 */
    syms = 286;
    root = 9;
    max = 15;
    // 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) {
            root = atoi(argv[2]);
            g.root = atoi(argv[2]);
            if (argc > 3)
                max = atoi(argv[3]);
                g.max = atoi(argv[3]);
        }
    }
    if (argc > 4 || syms < 2 || root < 1 || max < 1) {
    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 (max > syms - 1)
        max = syms - 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 */
    for (n = 0, word = 1; word; n++, word <<= 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 (max > n || (code_t)(syms - 2) >= (((code_t)0 - 1) >> (max - 1))) {
    // 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 << max) - 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, max);
                syms, g.max);
        return 1;
    }

    /* allocate code vector */
    code = calloc(max + 1, sizeof(int));
    if (code == NULL) {
    // allocate code vector
    g.code = calloc(g.max + 1, sizeof(int));
    assert(g.code != NULL && "out of memory");
        fputs("abort: unable to allocate enough memory\n", stderr);
        return 1;
    }


    /* 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 */
        num = NULL;             /* won't be saving any results */
    // 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 {
        size = syms >> 1;
        if (size > ((size_t)0 - 1) / (n = (syms - 1) >> 1) ||
                (size *= n, size > ((size_t)0 - 1) / (n = max - 1)) ||
                (size *= n, size > ((size_t)0 - 1) / sizeof(big_t)) ||
                (num = calloc(size, sizeof(big_t))) == NULL) {
        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));
            fputs("abort: unable to allocate enough memory\n", stderr);
            cleanup();
            return 1;
        }
    }
        assert(g.num != NULL && "out of memory");
    }


    /* count possible codes for all numbers of symbols, add up counts */
    sum = 0;
    for (n = 2; n <= syms; n++) {
        got = count(n, 1, 2);
    // 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;
        if (got == (big_t)0 - 1 || sum < got) {     /* overflow */
        assert(got != (big_t)-1 && sum >= got && "overflow");
            fputs("abort: can't count that high!\n", stderr);
            cleanup();
            return 1;
        }
    }
        printf("%llu %d-codes\n", got, n);
    }
    printf("%llu total codes for 2 to %d symbols", sum, syms);
    if (max < syms - 1)
        printf(" (%d-bit length limit)\n", max);
    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 beenhere() */
    // allocate and clear done array for been_here()
    if (syms == 2)
        done = NULL;
    else if (size > ((size_t)0 - 1) / sizeof(struct tab) ||
             (done = calloc(size, sizeof(struct tab))) == NULL) {
        fputs("abort: unable to allocate enough memory\n", stderr);
        g.done = NULL;
    else {
        g.done = calloc(g.size, sizeof(struct tab));
        assert(g.done != NULL && "out of memory");
        cleanup();
        return 1;
    }

    /* find and show maximum inflate table usage */
    if (root > max)                 /* reduce root to max length */
        root = max;
    if ((code_t)syms < ((code_t)1 << (root + 1)))
    // 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
        puts("cannot handle minimum code lengths > root");
        fputs("cannot handle minimum code lengths > root", stderr);

    /* done */
    // done
    cleanup();
    return 0;
}

Changes to compat/zlib/examples/fitblk.c.

13
14
15
16
17
18
19
20

21
22
23
24
25
26
27
13
14
15
16
17
18
19

20
21
22
23
24
25
26
27







-
+








/* 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 specfied excess amount (see the EXCESS define
   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
105
106
107
108
109
110
111
112

113
114
115
116
117
118
119
105
106
107
108
109
110
111

112
113
114
115
116
117
118
119







-
+







        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 decompresed until done or no room */
        /* 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);
194
195
196
197
198
199
200
201

202
203
204
205
206
207
208
194
195
196
197
198
199
200

201
202
203
204
205
206
207
208







-
+







    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 reocmpression */
    /* 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 */

Changes to compat/zlib/examples/gun.c.

39
40
41
42
43
44
45
46

47
48
49
50
51
52
53
39
40
41
42
43
44
45

46
47
48
49
50
51
52
53







-
+







   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 concantenated.  If a Unix compress stream is
   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

Changes to compat/zlib/examples/gzappend.c.

29
30
31
32
33
34
35
36

37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57

58
59
60
61
62
63
64
29
30
31
32
33
34
35

36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

57
58
59
60
61
62
63
64







-
+




















-
+







 * 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
 *                      - Simplfy and unify file operations
 *                      - 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 unusued bits in the last input byte used.  gzappend will not work
   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.
133
134
135
136
137
138
139
140

141
142
143
144
145
146
147
133
134
135
136
137
138
139

140
141
142
143
144
145
146
147







-
+








    /* pointer to last entry in list */
    last = list + (len - 1);

    /* do simple left shift by one */
    if (rot == 1) {
        tmp = *list;
        memcpy(list, list + 1, len - 1);
        memmove(list, list + 1, len - 1);
        *last = tmp;
        return;
    }

    /* do simple right shift by one */
    if (rot == len - 1) {
        tmp = *last;

Changes to compat/zlib/examples/gzlog.c.

1
2
3

4
5

6
7
8
9
10
11
12
1
2

3
4

5
6
7
8
9
10
11
12


-
+

-
+







/*
 * gzlog.c
 * Copyright (C) 2004, 2008, 2012, 2016 Mark Adler, all rights reserved
 * 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.2, 14 Aug 2012
 * 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
208
209
210
211
212
213
214
215
216


217
218
219
220
221
222
223
208
209
210
211
212
213
214


215
216
217
218
219
220
221
222
223







-
-
+
+







   - 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 the compress recovery, a missing foo.add file results in foo.gz
     being restored to the previous compressed-only 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
752
753
754
755
756
757
758

759
760
761
762
763
764

765
766
767
768
769
770
771
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773







+






+







            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!");

Changes to compat/zlib/examples/gzlog.h.

36
37
38
39
40
41
42
43

44
45
46
47
48
49
50
36
37
38
39
40
41
42

43
44
45
46
47
48
49
50







-
+







   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 interupted at any point due to an application or
   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

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;
}

Changes to 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


1
2
3
4
5
6

7
8
9
10
11
12
13
14
15
16
17
18
19

20
21
22
23
24
25
26
27
-
-
+
+




-
+












-
+







<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"
  "http://www.w3.org/TR/REC-html40/loose.dtd">
<!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, 2005 Mark Adler.  -->
<!--  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 adieu, here is the program <a href="zpipe.c"><tt>zpipe.c</tt></a>:
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
151
152
153
154
155
156
157
158


159
160

161
162
163
164

165
166
167
168
169
170
171
151
152
153
154
155
156
157

158
159
160

161




162
163
164
165
166
167
168
169







-
+
+

-
+
-
-
-
-
+







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.  If we are at the end of file, then <tt>flush</tt> is set to the
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.  We need to use <tt>feof()</tt>
indicate that this is the last chunk of input data to compress.
to check for end-of-file as opposed to seeing if fewer than <tt>CHUNK</tt> bytes have been read.  The
reason is that if the input file length is an exact multiple of <tt>CHUNK</tt>, we will miss
the fact that we got to the end-of-file, and not know to tell <tt>deflate()</tt> to finish
up the compressed stream.  If we are not yet at the end of the input, then the <em>zlib</em>
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
536
537
538
539
540
541
542
543







544
545
534
535
536
537
538
539
540

541
542
543
544
545
546
547
548
549







-
+
+
+
+
+
+
+


    else {
        fputs("zpipe usage: zpipe [-d] &lt; source &gt; dest\n", stderr);
        return 1;
    }
}
</b></pre>
<hr>
<i>Copyright (c) 2004, 2005 by Mark Adler<br>Last modified 11 December 2005</i>
<i>Last modified 24 January 2023<br>
Copyright &#169; 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>

Changes to 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




1
2
3

4
5
6
7
8
9
10
11
12
13
14
15
16
17
18



























19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45















46
47
48
49
50
51
52
53
54
55

56
57
58
59
60
61

62

63



64
65
66








67







68



69
70
71
72
73
74




75
76
77
78
79
80



81

82
83



84
85
86
87
88
89
90
91
92
93


94
95




96
97
98
99
100

101
102
103
104
105
106



107
108
109
110
111
112
113
114
115

116
117
118
119
120

121

122
123
124
125






126
127
128
129
130
131





132
133
134
135
136
137
138
139
140
141
142




143
144
145
146
147
148
149
150




151
152
153
154
155


156










157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182






183
184
185
186



187
188
189
190
191
192
193
194
195
196
197
198


199
200
201





202
203
204
205
206






207
208
209
210
211
212
213
214
215
216
217
218



219
220
221
222






223
224
225



226
227
228
229
230


231
232
233
234
235
























236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275




276
277
278
279
280
281
282
283
284
285
286
287
288
289
290









291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324










325
326
327
328
329



330
331

332
333
334
335


336
337
338
339









340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355




356
357
358
359
360
361
362






363
364
365
366
367
368
369
370
371
372
373


374
375
376
377




378
379
380
381
382
383
384
385
386
387
388
389
390
391









392

393






394
395
396
397
398
399





400
401
402
403
404




405
406
407
408
409
410










411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
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 zlib/gzip stream indexing and random access
 * Copyright (C) 2005, 2012 Mark Adler
/* 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.1  29 Sep 2012  Mark Adler */
 * 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 zlib or gzip
   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 referece for locating a desired starting
   point in the uncompressed stream.  build_index() works by decompressing the
   input zlib or gzip stream a block at a time, and at the end of each block
   deciding 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
   the decompression then proceeds until the desired offset in the file is
   reached.  Then the decompression continues to read the desired uncompressed
   data from the file.

// 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.
//
   Another approach would be to generate the index on demand.  In that case,
   requests for random access reads from the compressed data would try to use
   the index, but if a read far enough past the end of the index is required,
   then further index entries would be generated and added.

   There is some fair bit of overhead to starting inflation for the random
   access, mainly copying the 32K byte dictionary.  So if small pieces of the
   file are being accessed, it would make sense to implement a cache to hold
   some lookahead and avoid many calls to 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 requires
   more memory per access point, and also cannot be saved to file due to the
   use of pointers in the state.  The approach here allows for storage of the
   index in a 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 local static

#define SPAN 1048576L       /* desired distance between access points */
#define WINSIZE 32768U      /* sliding window size */
#define CHUNK 16384         /* file input buffer size */
#define WINSIZE 32768U      // sliding window size
#define CHUNK 16384         // file input buffer size

/* access point entry */
struct point {
    off_t out;          /* corresponding offset in uncompressed data */
    off_t in;           /* offset in input file of first full byte */
    int bits;           /* number of bits (1-7) from byte at in - 1, or 0 */
    unsigned char window[WINSIZE];  /* preceding 32K of uncompressed data */
};

// See comments in zran.h.
/* access point list */
struct access {
    int have;           /* number of list entries filled in */
    int size;           /* number of list entries allocated */
    struct point *list; /* allocated list */
};

void deflate_index_free(struct deflate_index *index) {
/* Deallocate an index built by build_index() */
local void free_index(struct access *index)
{
    if (index != NULL) {
        free(index->list);
        free(index);
    }
}

/* Add an entry to the access point list.  If out of memory, deallocate the
   existing list and return NULL. */
local struct access *addpoint(struct access *index, int bits,
    off_t in, off_t out, unsigned left, unsigned char *window)
// 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,
{
    struct point *next;

                                       unsigned char *window) {
    /* if list is empty, create it (start with eight points) */
    if (index == NULL) {
        // The list is empty. Create it, starting with eight access points.
        index = malloc(sizeof(struct access));
        if (index == NULL) return NULL;
        index->list = malloc(sizeof(struct point) << 3);
        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;
        }
        index->size = 8;
        index->have = 0;
    }

    /* if list is full, make it bigger */
    else if (index->have == index->size) {
        index->size <<= 1;
        next = realloc(index->list, sizeof(struct point) * index->size);
    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) {
            free_index(index);
            deflate_index_free(index);
            return NULL;
        }
        index->list = next;
    }

    /* fill in entry and increment how many we have */
    next = index->list + index->have;
    next->bits = bits;
    // 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->out = out;
    next->bits = bits;
    if (left)
        memcpy(next->window, window + WINSIZE - left, left);
    if (left < WINSIZE)
        memcpy(next->window + left, window, WINSIZE - left);
    index->have++;

    /* return list, possibly reallocated */
    // Return the index, which may have been newly allocated or destroyed.
    return index;
}

/* Make one entire pass through the compressed stream and build an index, with
   access points about every span bytes of uncompressed output -- span is
   chosen to balance the speed of random access against the memory requirements
   of the list, about 32K bytes per access point.  Note that data after the end
   of the first zlib or gzip stream in the file is ignored.  build_index()
   returns the number of access points on success (>= 1), Z_MEM_ERROR for out
// Decompression modes. These are the inflateInit2() windowBits parameter.
#define RAW -15
#define ZLIB 15
#define GZIP 31

// See comments in zran.h.
   of memory, Z_DATA_ERROR for an error in the input file, or Z_ERRNO for a
   file read error.  On success, *built points to the resulting index. */
local int build_index(FILE *in, off_t span, struct access **built)
{
    int ret;
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 totin, totout;        /* our own total counters to avoid 4GB limit */
    off_t last;                 /* totout value of last access point */
    struct access *index;       /* access points being generated */
    z_stream strm;
    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;
    unsigned char input[CHUNK];
    unsigned char window[WINSIZE];

    /* initialize inflate */
            if (strm.avail_in < sizeof(buf) && ferror(in)) {
                ret = Z_ERRNO;
                break;
            }

    strm.zalloc = Z_NULL;
    strm.zfree = Z_NULL;
            if (mode == 0) {
    strm.opaque = Z_NULL;
    strm.avail_in = 0;
    strm.next_in = Z_NULL;
    ret = inflateInit2(&strm, 47);      /* automatic zlib or gzip decoding */
    if (ret != Z_OK)
        return ret;

    /* inflate the input, maintain a sliding window, and build an index -- this
       also validates the integrity of the compressed data using the check
       information at the end of the gzip or zlib stream */
                // 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.
    totin = totout = last = 0;
    index = NULL;               /* will be allocated by first addpoint() */
    strm.avail_out = 0;
    do {
        /* get some compressed data from input file */
        strm.avail_in = fread(input, 1, CHUNK, in);
            strm.data_type = 0x80;
        else {
            // Inflate and update the number of uncompressed bytes.
            unsigned before = strm.avail_out;
        if (ferror(in)) {
            ret = Z_ERRNO;
            goto build_index_error;
            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,
        if (strm.avail_in == 0) {
            ret = Z_DATA_ERROR;
                              totout, strm.avail_out, win);
            if (index == NULL) {
                ret = Z_MEM_ERROR;
            goto build_index_error;
        }
        strm.next_in = input;

        /* process all of that, or until end of stream */
                break;
            }
            last = totout;
        }

        do {
            /* reset sliding window if necessary */
            if (strm.avail_out == 0) {
                strm.avail_out = WINSIZE;
                strm.next_out = window;
            }
        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);

            /* inflate until out of input, output, or at end of block --
               update the total input and output counters */
            totin += strm.avail_in;
    if (ret != Z_STREAM_END) {
        // An error was encountered. Discard the index and return a negative
        // error code.
        deflate_index_free(index);
            totout += strm.avail_out;
            ret = inflate(&strm, Z_BLOCK);      /* return at end of block */
            totin -= strm.avail_in;
            totout -= strm.avail_out;
            if (ret == Z_NEED_DICT)
                ret = Z_DATA_ERROR;
        return ret == Z_NEED_DICT ? Z_DATA_ERROR : ret;
    }

            if (ret == Z_MEM_ERROR || ret == Z_DATA_ERROR)
                goto build_index_error;
            if (ret == Z_STREAM_END)
    // 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) {
                break;

        // Seems like a realloc() to make something smaller should always work,
        // but just in case.
        deflate_index_free(index);
        return Z_MEM_ERROR;
    }
            /* if at end of block, consider adding an index entry (note that if
               data_type indicates an end-of-block, then all of the
               uncompressed data from that block has been delivered, and none
               of the compressed data after that block has been consumed,
               except for up to seven bits) -- the totout == 0 provides an
               entry point after the zlib or gzip header, and assures that the
               index always has at least one access point; we avoid creating an
               access point after the last block by checking bit 6 of data_type
             */
            if ((strm.data_type & 128) && !(strm.data_type & 64) &&
                (totout == 0 || totout - last > span)) {
                index = addpoint(index, strm.data_type & 7, totin,
                                 totout, strm.avail_out, window);
                if (index == NULL) {
                    ret = Z_MEM_ERROR;
                    goto build_index_error;
                }
                last = totout;
            }
        } while (strm.avail_in != 0);
    } while (ret != Z_STREAM_END);

    /* clean up and return index (release unused entries in list) */
    (void)inflateEnd(&strm);
    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.
    index->list = realloc(index->list, sizeof(struct point) * index->have);
    index->size = index->have;
    *built = index;
    return index->size;
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

    /* return error */
  build_index_error:
    (void)inflateEnd(&strm);
    if (index != NULL)
        free_index(index);
    return ret;
}

/* Use the index to read len bytes from offset into buf, return bytes read or
    // 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);
}

   negative for error (Z_DATA_ERROR or Z_MEM_ERROR).  If data is requested past
   the end of the uncompressed data, then extract() will return a value less
   than len, indicating how much as actually read into buf.  This function
   should not return a data error unless the file was modified since the index
   was generated.  extract() may also return Z_ERRNO if there is an error on
   reading or seeking the input file. */
local int extract(FILE *in, struct access *index, off_t offset,
                  unsigned char *buf, int len)
{
    int ret, skip;
#else
#  define INFLATEPRIME inflatePrime
#endif

// See comments in zran.h.
    z_stream strm;
    struct point *here;
    unsigned char input[CHUNK];
ptrdiff_t deflate_index_extract(FILE *in, struct deflate_index *index,
                                off_t offset, unsigned char *buf, size_t len) {
    unsigned char discard[WINSIZE];
    // Do a quick sanity check on the index.
    if (index == NULL || index->have < 1 || index->list[0].out != 0)
        return Z_STREAM_ERROR;

    /* proceed only if something reasonable to do */
    if (len < 0)
    // If nothing to extract, return zero bytes extracted.
    if (len == 0 || offset < 0 || offset >= index->length)
        return 0;

    /* find where in stream to start */
    here = index->list;
    ret = index->have;
    while (--ret && here[1].out <= offset)
        here++;

    /* initialize file and inflate state to start there */
    strm.zalloc = Z_NULL;
    strm.zfree = Z_NULL;
    // 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;
    strm.opaque = Z_NULL;
    strm.avail_in = 0;
    strm.next_in = Z_NULL;
    ret = inflateInit2(&strm, -15);         /* raw inflate */
    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;
    ret = fseeko(in, here->in - (here->bits ? 1 : 0), SEEK_SET);
    if (ret == -1)
        goto extract_ret;
    if (here->bits) {
        ret = getc(in);
        if (ret == -1) {
    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) {
            ret = ferror(in) ? Z_ERRNO : Z_DATA_ERROR;
            goto extract_ret;
            // Discard up to offset uncompressed bytes.
            strm.avail_out = offset < WINSIZE ? (unsigned)offset : WINSIZE;
            strm.next_out = discard;
        }
        (void)inflatePrime(&strm, here->bits, ret >> (8 - here->bits));
    }
    (void)inflateSetDictionary(&strm, here->window, WINSIZE);

        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;
            }
    /* skip uncompressed bytes until offset reached, then satisfy request */
    offset -= here->out;
    strm.avail_in = 0;
    skip = 1;                               /* while skipping to offset */
    do {
        /* define where to put uncompressed data, and how much */
        if (offset == 0 && skip) {          /* at offset now */
            strm.avail_out = len;
            strm.next_out = buf;
            strm.next_in = input;
            skip = 0;                       /* only do this once */
        }
        if (offset > WINSIZE) {             /* skip WINSIZE bytes */
            strm.avail_out = WINSIZE;
            strm.next_out = discard;
            offset -= WINSIZE;
        }
        else if (offset != 0) {             /* last skip */
        unsigned got = strm.avail_out;
        ret = inflate(&strm, Z_NO_FLUSH);
        got -= strm.avail_out;

        // Update the appropriate count.
        if (offset)
            strm.avail_out = (unsigned)offset;
            strm.next_out = discard;
            offset = 0;
        }

            offset -= got;
        else
            left -= got;

        // If we're at the end of a gzip member and there's more to read,
        /* uncompress until avail_out filled, or end of stream */
        do {
            if (strm.avail_in == 0) {
                strm.avail_in = fread(input, 1, CHUNK, in);
        // 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;
                if (ferror(in)) {
                    ret = Z_ERRNO;
                    goto extract_ret;
                }
                if (strm.avail_in == 0) {
                    ret = Z_DATA_ERROR;
                    goto extract_ret;
                }
                strm.next_in = input;
            }
                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;
                    }
            ret = inflate(&strm, Z_NO_FLUSH);       /* normal inflate */
            if (ret == Z_NEED_DICT)
                ret = Z_DATA_ERROR;
            if (ret == Z_MEM_ERROR || ret == Z_DATA_ERROR)
                goto extract_ret;
            if (ret == Z_STREAM_END)
                break;
        } while (strm.avail_out != 0);

                    strm.avail_out = WINSIZE;
                    strm.next_out = discard;
        /* if reach end of stream, then don't keep trying to get more */
        if (ret == Z_STREAM_END)
            break;

                    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);
        /* do until offset reached and requested data read, or stream ends */
    } while (skip);

            }
    /* compute number of uncompressed bytes read after offset */
    ret = skip ? 0 : len - strm.avail_out;

    /* clean up and return bytes read or error */
  extract_ret:
    (void)inflateEnd(&strm);
    return ret;
        }

        // 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
/* Demonstrate the use of build_index() and extract() by processing the file
   provided on the command line, and the extracting 16K from about 2/3rds of
   the way through the uncompressed output, and writing that to stdout. */
int main(int argc, char **argv)

#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) {
{
    int len;
    off_t offset;
    FILE *in;
    struct access *index = NULL;
    unsigned char buf[CHUNK];

    /* open input file */
    if (argc != 2) {
        fprintf(stderr, "usage: zran file.gz\n");
    // Open the input file.
    if (argc < 2 || argc > 3) {
        fprintf(stderr, "usage: zran file.raw [offset]\n");
        return 1;
    }
    in = fopen(argv[1], "rb");
    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 */
    len = build_index(in, SPAN, &index);
    // 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 */
    offset = (index->list[index->have - 1].out << 1) / 3;
    len = extract(in, index, offset, buf, CHUNK);
    if (len < 0)
    // 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",
                len == Z_MEM_ERROR ? "out of memory" : "input corrupted");
                got == Z_MEM_ERROR ? "out of memory" : "input corrupted");
    else {
        fwrite(buf, 1, len, stdout);
        fprintf(stderr, "zran: extracted %d bytes at %llu\n", len, offset);
        fwrite(buf, 1, got, stdout);
        fprintf(stderr, "zran: extracted %ld bytes at %lld\n", got, offset);
    }

    /* clean up and exit */
    free_index(index);
    // 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);

Changes to compat/zlib/gzclose.c.

1
2
3
4
5
6
7
8
9
10
11

12
13
14
15
16
17
18
19
20
1
2
3
4
5
6
7
8
9
10

11


12
13
14
15
16
17
18










-
+
-
-







/* 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(file)
int ZEXPORT gzclose(gzFile file) {
    gzFile file;
{
#ifndef NO_GZCOMPRESS
    gz_statep state;

    if (file == NULL)
        return Z_STREAM_ERROR;
    state = (gz_statep)file;

Changes to compat/zlib/gzguts.h.

1
2

3
4
5
6
7
8
9
10
11


12
13
14
15
16
17
18
19
1

2
3
4
5
6
7
8
9


10
11

12
13
14
15
16
17
18

-
+







-
-
+
+
-







/* gzguts.h -- zlib internal header definitions for gz* operations
 * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013, 2016 Mark Adler
 * 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
#  ifdef _FILE_OFFSET_BITS
#    undef _FILE_OFFSET_BITS
#  undef _FILE_OFFSET_BITS
#  undef _TIME_BITS
#  endif
#endif

#ifdef HAVE_HIDDEN
#  define ZLIB_INTERNAL __attribute__((visibility ("hidden")))
#else
#  define ZLIB_INTERNAL
#endif
35
36
37
38
39
40
41
42

43
44
45
46
47
48
49
34
35
36
37
38
39
40

41
42
43
44
45
46
47
48







-
+







#  include <stddef.h>
#endif

#if defined(__TURBOC__) || defined(_MSC_VER) || defined(_WIN32)
#  include <io.h>
#endif

#if defined(_WIN32) || defined(__CYGWIN__)
#if defined(_WIN32)
#  define WIDECHAR
#endif

#ifdef WINAPI_FAMILY
#  define open _open
#  define read _read
#  define write _write
115
116
117
118
119
120
121
122
123


124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144




145
146
147
148
149
150
151
114
115
116
117
118
119
120


121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139




140
141
142
143
144
145
146
147
148
149
150







-
-
+
+

















-
-
-
-
+
+
+
+







#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 OF((uInt size));
  extern void   free   OF((voidpf ptr));
  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 OF((const char *, const char *));
    ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int));
    ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile));
    ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile));
    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
186
187
188
189
190
191
192

193
194
195
196
197
198
199
200
201
202
203
204
205

206
207

208
209
210
211
212
213
214
215
216
217


218
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204

205
206

207
208
209
210
211
212





213
214








+












-
+

-
+





-
-
-
-
-
+
+
-
    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 OF((gz_statep, int, const char *));
void ZLIB_INTERNAL gz_error(gz_statep, int, const char *);
#if defined UNDER_CE
char ZLIB_INTERNAL *gz_strwinerror OF((DWORD error));
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) */
#ifdef INT_MAX
#  define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > INT_MAX)
#else
unsigned ZLIB_INTERNAL gz_intmax OF((void));
#  define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > gz_intmax())
unsigned ZLIB_INTERNAL gz_intmax(void);
#define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > gz_intmax())
#endif

Changes to 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
1

2
3
4
5
6
7

8
9
10
11
12
13
14
15
16
17




18
19
20
21
22
23
24
25
26
27
28

29


30
31
32
33
34
35
36

-
+





-
+









-
-
-
-











-
+
-
-







/* gzlib.c -- zlib functions common to reading and writing gzip files
 * Copyright (C) 2004-2017 Mark Adler
 * 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__) && !defined(__MINGW32__)
#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

/* Local functions */
local void gz_reset OF((gz_statep));
local gzFile gz_open OF((const void *, int, const char *));

#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 (error)
char ZLIB_INTERNAL *gz_strwinerror(DWORD 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,
68
69
70
71
72
73
74
75

76
77
78
79
80
81
82
83


84
85
86
87
88
89
90
91

92
93
94
95
96
97
98
99
100
101
102
62
63
64
65
66
67
68

69


70
71
72
73
74
75
76
77
78
79
80
81
82
83
84

85




86
87
88
89
90
91
92







-
+
-
-






+
+







-
+
-
-
-
-







    SetLastError(lasterr);
    return buf;
}

#endif /* UNDER_CE */

/* Reset gzip file state */
local void gz_reset(state)
local void gz_reset(gz_statep state) {
    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(path, fd, mode)
local gzFile gz_open(const void *path, int fd, const char *mode) {
    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
263
264
265
266
267
268
269
270
271


272
273




274
275
276
277
278
279
280
281
282
283
284
285
286

287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307

308
309
310
311
312
313
314
315
316

317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337


338
339
340
341
342
343

344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366

367
368
369
370
371
372
373
374
375
376
377
253
254
255
256
257
258
259


260
261


262
263
264
265
266
267
268
269









270



271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287

288



289
290
291
292
293

294



295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310


311
312
313
314
315
316
317

318


319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338

339




340
341
342
343
344
345
346







-
-
+
+
-
-
+
+
+
+




-
-
-
-
-
-
-
-
-
+
-
-
-

















-
+
-
-
-





-
+
-
-
-
















-
-
+
+





-
+
-
-




















-
+
-
-
-
-







    gz_reset(state);

    /* return stream */
    return (gzFile)state;
}

/* -- see zlib.h -- */
gzFile ZEXPORT gzopen(path, mode)
    const char *path;
gzFile ZEXPORT gzopen(const char *path, const char *mode) {
    return gz_open(path, -1, mode);
    const char *mode;
{
}

/* -- see zlib.h -- */
gzFile ZEXPORT gzopen64(const char *path, const char *mode) {
    return gz_open(path, -1, mode);
}

/* -- see zlib.h -- */
gzFile ZEXPORT gzopen64(path, mode)
    const char *path;
    const char *mode;
{
    return gz_open(path, -1, mode);
}

/* -- see zlib.h -- */
gzFile ZEXPORT gzdopen(fd, mode)
gzFile ZEXPORT gzdopen(int fd, const char *mode) {
    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(path, mode)
gzFile ZEXPORT gzopen_w(const wchar_t *path, const char *mode) {
    const wchar_t *path;
    const char *mode;
{
    return gz_open(path, -2, mode);
}
#endif

/* -- see zlib.h -- */
int ZEXPORT gzbuffer(file, size)
int ZEXPORT gzbuffer(gzFile file, unsigned size) {
    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 < 2)
        size = 2;               /* need two bytes to check magic header */
    if (size < 8)
        size = 8;               /* needed to behave well with flushing */
    state->want = size;
    return 0;
}

/* -- see zlib.h -- */
int ZEXPORT gzrewind(file)
int ZEXPORT gzrewind(gzFile file) {
    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(file, offset, whence)
z_off64_t ZEXPORT gzseek64(gzFile file, z_off64_t offset, int whence) {
    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;
393
394
395
396
397
398
399
400

401
402
403
404
405
406
407
362
363
364
365
366
367
368

369
370
371
372
373
374
375
376







-
+







    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 - state->x.have, SEEK_CUR);
        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);
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
405
406
407
408
409
410
411

412




413
414
415
416
417
418
419

420


421
422
423
424
425
426
427
428
429
430
431
432
433
434

435


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







-
+
-
-
-
-







-
+
-
-














-
+
-
-







-
+
-
-




















-
+
-
-







-
+
-
-














-
+
-
-
-

















-
+
-
-







        state->seek = 1;
        state->skip = offset;
    }
    return state->x.pos + offset;
}

/* -- see zlib.h -- */
z_off_t ZEXPORT gzseek(file, offset, whence)
z_off_t ZEXPORT gzseek(gzFile file, z_off_t offset, int whence) {
    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(file)
z_off64_t ZEXPORT gztell64(gzFile file) {
    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(file)
z_off_t ZEXPORT gztell(gzFile file) {
    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(file)
z_off64_t ZEXPORT gzoffset64(gzFile file) {
    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(file)
z_off_t ZEXPORT gzoffset(gzFile file) {
    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(file)
int ZEXPORT gzeof(gzFile file) {
    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(file, errnum)
const char * ZEXPORT gzerror(gzFile file, int *errnum) {
    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(file)
void ZEXPORT gzclearerr(gzFile file) {
    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)
572
573
574
575
576
577
578
579

580
581
582
583
584
585
586
587
588
589
590
522
523
524
525
526
527
528

529




530
531
532
533
534
535
536







-
+
-
-
-
-








/* 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(state, err, msg)
void ZLIB_INTERNAL gz_error(gz_statep state, int err, const char *msg) {
    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;
    }

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

559
560
561
562
563
564
565

566
567
568
569

570


571
572
573
574


575
576
577
578
579
580

581
582







-




-
+
-
-
+
+
+
+
-
-






-

+
#else
    strcpy(state->msg, state->path);
    strcat(state->msg, ": ");
    strcat(state->msg, msg);
#endif
}

#ifndef INT_MAX
/* 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()
unsigned ZLIB_INTERNAL gz_intmax(void) {
{
    unsigned p, q;
#ifdef INT_MAX
    return INT_MAX;
#else
    unsigned p = 1, q;

    p = 1;
    do {
        q = p;
        p <<= 1;
        p++;
    } while (p > q);
    return q >> 1;
}
#endif
}

Changes to 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
1

2
3
4
5
6
7









8
9
10
11



12


13

14
15
16
17
18
19
20

-
+





-
-
-
-
-
-
-
-
-




-
-
-
+
-
-
+
-







/* gzread.c -- zlib functions for reading gzip files
 * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013, 2016 Mark Adler
 * Copyright (C) 2004-2017 Mark Adler
 * For conditions of distribution and use, see copyright notice in zlib.h
 */

#include "gzguts.h"

/* Local functions */
local int gz_load OF((gz_statep, unsigned char *, unsigned, unsigned *));
local int gz_avail OF((gz_statep));
local int gz_look OF((gz_statep));
local int gz_decomp OF((gz_statep));
local int gz_fetch OF((gz_statep));
local int gz_skip OF((gz_statep, z_off64_t));
local z_size_t gz_read OF((gz_statep, voidp, z_size_t));

/* 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(state, buf, len, have)
    gz_statep state;
    unsigned char *buf;
local int gz_load(gz_statep state, unsigned char *buf, unsigned len,
    unsigned len;
    unsigned *have;
                  unsigned *have) {
{
    int ret;
    unsigned get, max = ((unsigned)-1 >> 2) + 1;

    *have = 0;
    do {
        get = len - *have;
        if (get > max)
49
50
51
52
53
54
55
56

57
58
59
60
61
62
63
64
65
36
37
38
39
40
41
42

43


44
45
46
47
48
49
50







-
+
-
-







/* 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(state)
local int gz_avail(gz_statep state) {
    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 */
84
85
86
87
88
89
90
91

92
93
94
95
96
97
98
99
100
69
70
71
72
73
74
75

76


77
78
79
80
81
82
83







-
+
-
-







   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(state)
local int gz_look(gz_statep state) {
    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);
153
154
155
156
157
158
159
160
161
162
163



164
165
166
167
168
169
170
171
172
173
174
175

176
177
178
179
180
181
182
183
184
136
137
138
139
140
141
142




143
144
145

146
147
148
149
150
151
152
153
154
155

156


157
158
159
160
161
162
163







-
-
-
-
+
+
+
-










-
+
-
-







        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;
    if (strm->avail_in) {
        memcpy(state->x.next, strm->next_in, strm->avail_in);
        state->x.have = strm->avail_in;
        strm->avail_in = 0;
    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(state)
local int gz_decomp(gz_statep state) {
    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 {
222
223
224
225
226
227
228
229

230
231
232
233
234
235
236
237
238
201
202
203
204
205
206
207

208


209
210
211
212
213
214
215







-
+
-
-








/* 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(state)
local int gz_fetch(gz_statep state) {
    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;
252
253
254
255
256
257
258
259

260
261
262
263
264
265
266
267
268
269
229
230
231
232
233
234
235

236



237
238
239
240
241
242
243







-
+
-
-
-







                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(state, len)
local int gz_skip(gz_statep state, z_off64_t len) {
    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 ?
287
288
289
290
291
292
293
294

295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317

318
319

320
321
322
323
324
325
326
261
262
263
264
265
266
267

268




269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286

287
288

289
290
291
292
293
294
295
296







-
+
-
-
-
-


















-
+

-
+







    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(state, buf, len)
local z_size_t gz_read(gz_statep state, voidp buf, z_size_t len) {
    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 = -1;
        n = (unsigned)-1;
        if (n > len)
            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;
368
369
370
371
372
373
374
375

376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400

401
402
403
404
405
406
407
408
409
410
411

412
413
414
415
416
417
418
419
420
421
422
423
338
339
340
341
342
343
344

345




346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365

366
367
368
369
370
371
372
373
374
375
376

377





378
379
380
381
382
383
384







-
+
-
-
-
-




















-
+










-
+
-
-
-
-
-







    } while (len);

    /* return number of bytes read into user buffer */
    return got;
}

/* -- see zlib.h -- */
int ZEXPORT gzread(file, buf, len)
int ZEXPORT gzread(gzFile file, voidp buf, unsigned len) {
    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 = gz_read(state, buf, len);
    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(buf, size, nitems, file)
z_size_t ZEXPORT gzfread(voidp buf, z_size_t size, z_size_t nitems, gzFile file) {
    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;
440
441
442
443
444
445
446
447

448
449
450
451
452
453
454
455
456
457
401
402
403
404
405
406
407

408



409
410
411
412
413
414
415







-
+
-
-
-








/* -- see zlib.h -- */
#ifdef Z_PREFIX_SET
#  undef z_gzgetc
#else
#  undef gzgetc
#endif
int ZEXPORT gzgetc(file)
int ZEXPORT gzgetc(gzFile file) {
    gzFile file;
{
    int ret;
    unsigned char buf[1];
    gz_statep state;

    /* get internal structure */
    if (file == NULL)
        return -1;
    state = (gz_statep)file;
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
423
424
425
426
427
428
429

430

431
432

433


434
435
436
437

438



439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455







-
+
-


-
+
-
-




-
+
-
-
-






+
+
+
+







    if (state->x.have) {
        state->x.have--;
        state->x.pos++;
        return *(state->x.next)++;
    }

    /* nothing there -- try gz_read() */
    ret = gz_read(state, buf, 1);
    return gz_read(state, buf, 1) < 1 ? -1 : buf[0];
    return ret < 1 ? -1 : buf[0];
}

int ZEXPORT gzgetc_(file)
int ZEXPORT gzgetc_(gzFile file) {
gzFile file;
{
    return gzgetc(file);
}

/* -- see zlib.h -- */
int ZEXPORT gzungetc(c, file)
int ZEXPORT gzungetc(int c, gzFile file) {
    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 */
536
537
538
539
540
541
542
543

544
545
546
547
548
549
550
551
552
553
554
492
493
494
495
496
497
498

499




500
501
502
503
504
505
506







-
+
-
-
-
-







    state->x.next[0] = (unsigned char)c;
    state->x.pos--;
    state->past = 0;
    return c;
}

/* -- see zlib.h -- */
char * ZEXPORT gzgets(file, buf, len)
char * ZEXPORT gzgets(gzFile file, char *buf, int len) {
    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)
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
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







-
+
-
-

















-
+
-
-







    if (buf == str)
        return NULL;
    buf[0] = 0;
    return str;
}

/* -- see zlib.h -- */
int ZEXPORT gzdirect(file)
int ZEXPORT gzdirect(gzFile file) {
    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(file)
int ZEXPORT gzclose_r(gzFile file) {
    gzFile file;
{
    int ret, err;
    gz_statep state;

    /* get internal structure */
    if (file == NULL)
        return Z_STREAM_ERROR;
    state = (gz_statep)file;

Changes to 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
1

2
3
4
5
6
7






8
9
10

11


12
13
14
15
16
17
18

-
+





-
-
-
-
-
-



-
+
-
-







/* gzwrite.c -- zlib functions for writing gzip files
 * Copyright (C) 2004-2017 Mark Adler
 * Copyright (C) 2004-2019 Mark Adler
 * For conditions of distribution and use, see copyright notice in zlib.h
 */

#include "gzguts.h"

/* Local functions */
local int gz_init OF((gz_statep));
local int gz_comp OF((gz_statep, int));
local int gz_zero OF((gz_statep, z_off64_t));
local z_size_t gz_write OF((gz_statep, voidpc, z_size_t));

/* 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(state)
local int gz_init(gz_statep state) {
    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");
66
67
68
69
70
71
72
73

74
75
76
77
78
79
80
81
82
83
58
59
60
61
62
63
64

65



66
67
68
69
70
71
72







-
+
-
-
-








/* 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(state, flush)
local int gz_comp(gz_statep state, int flush) {
    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;
92
93
94
95
96
97
98









99
100
101
102
103
104
105
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103







+
+
+
+
+
+
+
+
+







                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 &&
130
131
132
133
134
135
136
137

138
139
140
141
142
143
144
145

146
147
148
149
150
151
152
153
154
155
128
129
130
131
132
133
134

135
136
137
138
139
140
141
142

143



144
145
146
147
148
149
150







-
+







-
+
-
-
-







            return -1;
        }
        have -= strm->avail_out;
    } while (have);

    /* if that completed a deflate stream, allow another to start */
    if (flush == Z_FINISH)
        deflateReset(strm);
        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(state, len)
local int gz_zero(gz_statep state, z_off64_t len) {
    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;
171
172
173
174
175
176
177
178

179
180
181
182
183
184
185
186
187
188
189
166
167
168
169
170
171
172

173




174
175
176
177
178
179
180







-
+
-
-
-
-







        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(state, buf, len)
local z_size_t gz_write(gz_statep state, voidpc buf, z_size_t len) {
    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 */
205
206
207
208
209
210
211
212

213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232

233
234
235
236
237
238
239
240
241
242
243
244
245
246

247
248
249
250
251
252
253
254
255
256
257
196
197
198
199
200
201
202

203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222

223
224
225
226
227
228
229
230
231
232
233
234
235
236

237




238
239
240
241
242
243
244







-
+



















-
+













-
+
-
-
-
-








            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 = 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 = 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(file, buf, len)
int ZEXPORT gzwrite(gzFile file, voidpc buf, unsigned len) {
    gzFile file;
    voidpc buf;
    unsigned len;
{
    gz_statep state;

    /* get internal structure */
    if (file == NULL)
        return 0;
    state = (gz_statep)file;

267
268
269
270
271
272
273
274

275
276
277
278

279
280
281
282
283
284
285
286
254
255
256
257
258
259
260

261




262

263
264
265
266
267
268
269







-
+
-
-
-
-
+
-







    }

    /* 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(buf, size, nitems, file)
z_size_t ZEXPORT gzfwrite(voidpc buf, z_size_t size, z_size_t nitems,
    voidpc buf;
    z_size_t size;
    z_size_t nitems;
    gzFile file;
                          gzFile file) {
{
    z_size_t len;
    gz_statep state;

    /* get internal structure */
    if (file == NULL)
        return 0;
    state = (gz_statep)file;
297
298
299
300
301
302
303
304

305
306
307
308
309
310
311
312
313
314
280
281
282
283
284
285
286

287



288
289
290
291
292
293
294







-
+
-
-
-







    }

    /* 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(file, c)
int ZEXPORT gzputc(gzFile file, int c) {
    gzFile file;
    int c;
{
    unsigned have;
    unsigned char buf[1];
    gz_statep state;
    z_streamp strm;

    /* get internal structure */
    if (file == NULL)
345
346
347
348
349
350
351
352

353
354
355
356
357

358
359
360
361
362
363
364
365
366
367
368
369
370
371
372







373
374
375
376
377
378
379

380
381
382
383
384
385
386
387
325
326
327
328
329
330
331

332





333
334
335
336
337
338
339
340
341
342
343
344
345



346
347
348
349
350
351
352
353
354
355
356
357
358

359

360
361
362
363
364
365
366







-
+
-
-
-
-
-
+












-
-
-
+
+
+
+
+
+
+






-
+
-







    buf[0] = (unsigned char)c;
    if (gz_write(state, buf, 1) != 1)
        return -1;
    return c & 0xff;
}

/* -- see zlib.h -- */
int ZEXPORT gzputs(file, str)
int ZEXPORT gzputs(gzFile file, const char *s) {
    gzFile file;
    const char *str;
{
    int ret;
    z_size_t len;
    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(str);
    ret = gz_write(state, str, len);
    return ret == 0 && len != 0 ? -1 : ret;
    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 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 */
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
416
417
418
419
420
421
422

423
424
425
426
427
428
429

430

431
432
433
434
435
436
437
438
439
440
441
442






443
444
445
446

447
448
449
450
451
452
453







-
+






-
+
-












-
-
-
-
-
-
+
+
+
+
-







    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;
        memcpy(state->in, state->in + state->size, left);
        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, ...)
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 (file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10,
                       a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
    gzFile file;
    const char *format;
    int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10,
        a11, a12, a13, a14, a15, a16, a17, a18, a19, a20;
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)
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
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







-
+









-
+
-
-
-







    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;
        memcpy(state->in, state->in + state->size, left);
        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(file, flush)
int ZEXPORT gzflush(gzFile file, int flush) {
    gzFile file;
    int flush;
{
    gz_statep state;

    /* get internal structure */
    if (file == NULL)
        return Z_STREAM_ERROR;
    state = (gz_statep)file;

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
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







-
+
-
-
-
-










-
+








    /* compress remaining data with requested flush */
    (void)gz_comp(state, flush);
    return state->err;
}

/* -- see zlib.h -- */
int ZEXPORT gzsetparams(file, level, strategy)
int ZEXPORT gzsetparams(gzFile file, int level, int strategy) {
    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)
    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 */
620
621
622
623
624
625
626
627

628
629
630
631
632
633
634
635
636
588
589
590
591
592
593
594

595


596
597
598
599
600
601
602







-
+
-
-







    }
    state->level = level;
    state->strategy = strategy;
    return Z_OK;
}

/* -- see zlib.h -- */
int ZEXPORT gzclose_w(file)
int ZEXPORT gzclose_w(gzFile file) {
    gzFile file;
{
    int ret = Z_OK;
    gz_statep state;

    /* get internal structure */
    if (file == NULL)
        return Z_STREAM_ERROR;
    state = (gz_statep)file;

Changes to 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
1

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17



18
19
20
21
22
23
24

25



26


27

28
29
30
31
32
33
34

-
+















-
-
-







-
+
-
-
-
+
-
-
+
-







/* infback.c -- inflate using a call-back interface
 * Copyright (C) 1995-2016 Mark Adler
 * 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"

/* function prototypes */
local void fixedtables OF((struct inflate_state FAR *state));

/*
   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_(strm, windowBits, window, version, stream_size)
int ZEXPORT inflateBackInit_(z_streamp strm, int windowBits,
z_streamp strm;
int windowBits;
unsigned char FAR *window;
                             unsigned char FAR *window, const char *version,
const char *version;
int stream_size;
                             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)
62
63
64
65
66
67
68

69
70
71
72
73
74
75
76
77
78
79
80
81
82

83
84
85
86
87
88
89
90
91
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75

76


77
78
79
80
81
82
83







+













-
+
-
-







    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(state)
local void fixedtables(struct inflate_state FAR *state) {
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) {
243
244
245
246
247
248
249
250

251
252
253
254

255
256
257
258
259
260
261
262
263
235
236
237
238
239
240
241

242




243


244
245
246
247
248
249
250







-
+
-
-
-
-
+
-
-







   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(strm, in, in_desc, out, out_desc)
int ZEXPORT inflateBack(z_streamp strm, in_func in, void FAR *in_desc,
z_streamp strm;
in_func in;
void FAR *in_desc;
out_func out;
                        out_func out, void FAR *out_desc) {
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 */
473
474
475
476
477
478
479

480
481
482
483
484
485
486
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474







+







            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;
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
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







-
+

-
-
-
-






+
-
+




-
+

+
+
+
+
+





-
+
-
-







                do {
                    *put++ = *from++;
                } while (--copy);
            } while (state->length != 0);
            break;

        case DONE:
            /* inflate stream terminated properly -- write leftover output */
            /* inflate stream terminated properly */
            ret = Z_STREAM_END;
            if (left < state->wsize) {
                if (out(out_desc, state->window, state->wsize - left))
                    ret = Z_BUF_ERROR;
            }
            goto inf_leave;

        case BAD:
            ret = Z_DATA_ERROR;
            goto inf_leave;

        default:
        default:                /* can't happen, but makes compilers happy */
            /* can't happen, but makes compilers happy */
            ret = Z_STREAM_ERROR;
            goto inf_leave;
        }

    /* Return unused input */
    /* 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(strm)
int ZEXPORT inflateBackEnd(z_streamp strm) {
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;
}

Changes to compat/zlib/inffast.c.

43
44
45
46
47
48
49
50

51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73

74
75
76
77
78
79
80
43
44
45
46
47
48
49

50



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69

70
71
72
73
74
75
76
77







-
+
-
-
-



















-
+







      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(strm, start)
void ZLIB_INTERNAL inflate_fast(z_streamp strm, unsigned start) {
z_streamp strm;
unsigned start;         /* inflate()'s starting value for strm->avail_out */
{
    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 here;                  /* retrieved table entry */
    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 */
103
104
105
106
107
108
109
110

111
112

113
114
115

116
117

118
119
120


121
122
123

124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141

142
143

144
145
146

147
148

149
150
151
152
153
154
155
100
101
102
103
104
105
106

107
108

109
110
111

112
113

114
115


116
117
118
119

120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137

138
139

140
141
142

143
144

145
146
147
148
149
150
151
152







-
+

-
+


-
+

-
+

-
-
+
+


-
+

















-
+

-
+


-
+

-
+







    do {
        if (bits < 15) {
            hold += (unsigned long)(*in++) << bits;
            bits += 8;
            hold += (unsigned long)(*in++) << bits;
            bits += 8;
        }
        here = lcode[hold & lmask];
        here = lcode + (hold & lmask);
      dolen:
        op = (unsigned)(here.bits);
        op = (unsigned)(here->bits);
        hold >>= op;
        bits -= op;
        op = (unsigned)(here.op);
        op = (unsigned)(here->op);
        if (op == 0) {                          /* literal */
            Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?
            Tracevv((stderr, here->val >= 0x20 && here->val < 0x7f ?
                    "inflate:         literal '%c'\n" :
                    "inflate:         literal 0x%02x\n", here.val));
            *out++ = (unsigned char)(here.val);
                    "inflate:         literal 0x%02x\n", here->val));
            *out++ = (unsigned char)(here->val);
        }
        else if (op & 16) {                     /* length base */
            len = (unsigned)(here.val);
            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];
            here = dcode + (hold & dmask);
          dodist:
            op = (unsigned)(here.bits);
            op = (unsigned)(here->bits);
            hold >>= op;
            bits -= op;
            op = (unsigned)(here.op);
            op = (unsigned)(here->op);
            if (op & 16) {                      /* distance base */
                dist = (unsigned)(here.val);
                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;
260
261
262
263
264
265
266
267

268
269
270
271
272
273
274
275
276
277

278
279
280
281
282
283
284
257
258
259
260
261
262
263

264
265
266
267
268
269
270
271
272
273

274
275
276
277
278
279
280
281







-
+









-
+







                        *out++ = *from++;
                        if (len > 1)
                            *out++ = *from++;
                    }
                }
            }
            else if ((op & 64) == 0) {          /* 2nd level distance code */
                here = dcode[here.val + (hold & ((1U << op) - 1))];
                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))];
            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;
        }

Changes to compat/zlib/inffast.h.

1
2
3
4
5
6
7
8
9
10
11

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 OF((z_streamp strm, unsigned start));
void ZLIB_INTERNAL inflate_fast(z_streamp strm, unsigned start);

Changes to compat/zlib/inflate.c.

1
2

3
4
5
6
7
8
9
1

2
3
4
5
6
7
8
9

-
+







/* inflate.c -- zlib decompression
 * Copyright (C) 1995-2016 Mark Adler
 * 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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105

106
107
108
109
110
111
112
113
114
115
116
117
118
119

120
121
122
123
124
125
126
127
128
129
130
131
132

133
134
135
136
137
138
139
140
141
142
143
144

145
146
147
148
149
150
151
152
153
154
155
156
157

158
159
160
161
162
163
164
165
166
167
168
169


170
171
172
173
174
175
176
87
88
89
90
91
92
93












94


95
96
97
98
99
100
101
102
103
104
105

106


107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129

130


131
132
133
134
135
136
137
138
139
140

141



142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159







-
-
-
-
-
-
-
-
-
-
-
-
+
-
-











-
+
-
-











+











-
+
-
-










-
+
-
-
-









+
+








#ifdef MAKEFIXED
#  ifndef BUILDFIXED
#    define BUILDFIXED
#  endif
#endif

/* function prototypes */
local int inflateStateCheck OF((z_streamp strm));
local void fixedtables OF((struct inflate_state FAR *state));
local int updatewindow OF((z_streamp strm, const unsigned char FAR *end,
                           unsigned copy));
#ifdef BUILDFIXED
   void makefixed OF((void));
#endif
local unsigned syncsearch OF((unsigned FAR *have, const unsigned char FAR *buf,
                              unsigned len));

local int inflateStateCheck(strm)
local int inflateStateCheck(z_streamp strm) {
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(strm)
int ZEXPORT inflateResetKeep(z_streamp strm) {
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(strm)
int ZEXPORT inflateReset(z_streamp strm) {
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(strm, windowBits)
int ZEXPORT inflateReset2(z_streamp strm, int windowBits) {
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)
188
189
190
191
192
193
194
195

196
197
198

199
200
201
202
203
204
205
206
207
171
172
173
174
175
176
177

178



179


180
181
182
183
184
185
186







-
+
-
-
-
+
-
-








    /* update state and reset the rest of it */
    state->wrap = wrap;
    state->wbits = (unsigned)windowBits;
    return inflateReset(strm);
}

int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size)
int ZEXPORT inflateInit2_(z_streamp strm, int windowBits,
z_streamp strm;
int windowBits;
const char *version;
                          const char *version, int stream_size) {
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;
232
233
234
235
236
237
238
239
240
241
242


243
244
245
246
247

248
249
250
251
252
253
254


255
256
257
258
259
260
261
211
212
213
214
215
216
217




218
219

220
221
222

223




224
225
226
227
228
229
230
231
232
233
234
235







-
-
-
-
+
+
-



-
+
-
-
-
-



+
+







    if (ret != Z_OK) {
        ZFREE(strm, state);
        strm->state = Z_NULL;
    }
    return ret;
}

int ZEXPORT inflateInit_(strm, version, stream_size)
z_streamp strm;
const char *version;
int stream_size;
int ZEXPORT inflateInit_(z_streamp strm, const char *version,
                         int stream_size) {
{
    return inflateInit2_(strm, DEF_WBITS, version, stream_size);
}

int ZEXPORT inflatePrime(strm, bits, value)
int ZEXPORT inflatePrime(z_streamp strm, int bits, int value) {
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;
271
272
273
274
275
276
277
278

279
280
281
282
283
284
285
286
287
245
246
247
248
249
250
251

252


253
254
255
256
257
258
259







-
+
-
-







   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(state)
local void fixedtables(struct inflate_state FAR *state) {
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) {
335
336
337
338
339
340
341
342

343
344
345
346
347
348
349
307
308
309
310
311
312
313

314
315
316
317
318
319
320
321







-
+







        return 0;
    }

   Then that can be linked with zlib built with MAKEFIXED defined and run:

    a.out > inffixed.h
 */
void makefixed()
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().");
389
390
391
392
393
394
395
396

397
398
399
400
401
402
403
404
405
406
407
361
362
363
364
365
366
367

368




369
370
371
372
373
374
375







-
+
-
-
-
-








   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(strm, end, copy)
local int updatewindow(z_streamp strm, const Bytef *end, unsigned copy) {
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) {
443
444
445
446
447
448
449
450

451
452
453

454
455
456
457
458
459
460
411
412
413
414
415
416
417

418
419
420

421
422
423
424
425
426
427
428







-
+


-
+







    return 0;
}

/* Macros for inflate(): */

/* check function to use adler32() for zlib or crc32() for gzip */
#ifdef GUNZIP
#  define UPDATE(check, buf, len) \
#  define UPDATE_CHECK(check, buf, len) \
    (state->flags ? crc32(check, buf, len) : adler32(check, buf, len))
#else
#  define UPDATE(check, buf, len) adler32(check, buf, len)
#  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); \
615
616
617
618
619
620
621
622

623
624
625
626
627
628
629
630
631
632
583
584
585
586
587
588
589

590



591
592
593
594
595
596
597







-
+
-
-
-







   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(strm, flush)
int ZEXPORT inflate(z_streamp strm, int flush) {
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 */
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
631
632
633
634
635
636
637

638
639
640
641
642
643
644







-







                    state->wbits = 15;
                state->check = crc32(0L, Z_NULL, 0);
                CRC2(state->check, hold);
                INITBITS();
                state->mode = FLAGS;
                break;
            }
            state->flags = 0;           /* expect zlib header */
            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) {
693
694
695
696
697
698
699

700
701
702
703
704
705
706
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671







+







                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:
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
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







+








+










+













+






-
-
+
+
+














+







            }
            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 != 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 &&
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
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







+




















+







                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;
835
836
837
838
839
840
841

842
843
844
845
846
847
848

849
850

851
852
853
854
855
856
857
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







+







+


+







            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);
894
895
896
897
898
899
900

901
902

903
904
905
906
907
908
909
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887







+


+







            }
            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);
931
932
933
934
935
936
937

938
939
940
941
942
943
944
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923







+







                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)
952
953
954
955
956
957
958

959
960
961
962
963
964
965
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945







+







                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();
                }
1035
1036
1037
1038
1039
1040
1041

1042
1043

1044
1045
1046
1047
1048
1049
1050
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032







+


+







                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;
1086
1087
1088
1089
1090
1091
1092

1093
1094
1095
1096
1097
1098
1099
1100
1101
1102

1103
1104
1105
1106
1107
1108
1109
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







+










+







            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) {
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
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







+
















+







                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) {
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
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







-
+















+



-
+









+









+







            if (state->wrap) {
                NEEDBITS(32);
                out -= left;
                strm->total_out += out;
                state->total += out;
                if ((state->wrap & 4) && out)
                    strm->adler = state->check =
                        UPDATE(state->check, put - out, out);
                        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 (hold != (state->total & 0xffffffffUL)) {
                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
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
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







-
+








-
+
-
-











-
+
-
-
-
+
-


















-
+
-
-
-
+
-







    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(state->check, strm->next_out - out, out);
            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(strm)
int ZEXPORT inflateEnd(z_streamp strm) {
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(strm, dictionary, dictLength)
int ZEXPORT inflateGetDictionary(z_streamp strm, Bytef *dictionary,
z_streamp strm;
Bytef *dictionary;
uInt *dictLength;
                                 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(strm, dictionary, dictLength)
int ZEXPORT inflateSetDictionary(z_streamp strm, const Bytef *dictionary,
z_streamp strm;
const Bytef *dictionary;
uInt dictLength;
                                 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;
1342
1343
1344
1345
1346
1347
1348
1349

1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1323
1324
1325
1326
1327
1328
1329

1330



1331
1332
1333
1334
1335
1336
1337







-
+
-
-
-







        return Z_MEM_ERROR;
    }
    state->havedict = 1;
    Tracev((stderr, "inflate:   dictionary set\n"));
    return Z_OK;
}

int ZEXPORT inflateGetHeader(strm, head)
int ZEXPORT inflateGetHeader(z_streamp strm, gz_headerp head) {
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;

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
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







-
+
-
-
-
+
-


















-
+
-
-

+












-
+



















+
+
+
+
+



+












-
+
-
-







-
+
-
-
-







   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(have, buf, len)
local unsigned syncsearch(unsigned FAR *have, const unsigned char FAR *buf,
unsigned FAR *have;
const unsigned char FAR *buf;
unsigned len;
                          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(strm)
int ZEXPORT inflateSync(z_streamp strm) {
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->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(strm)
int ZEXPORT inflateSyncPoint(z_streamp strm) {
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(dest, source)
int ZEXPORT inflateCopy(z_streamp dest, z_streamp source) {
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)
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
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







-
+
-
-
-














-
+
-
-
-




-
+






-
+
-
-










-
+
-
-





        zmemcpy(window, state->window, wsize);
    }
    copy->window = window;
    dest->state = (struct internal_state FAR *)copy;
    return Z_OK;
}

int ZEXPORT inflateUndermine(strm, subvert)
int ZEXPORT inflateUndermine(z_streamp strm, int subvert) {
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(strm, check)
int ZEXPORT inflateValidate(z_streamp strm, int check) {
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)
    if (check && state->wrap)
        state->wrap |= 4;
    else
        state->wrap &= ~4;
    return Z_OK;
}

long ZEXPORT inflateMark(strm)
long ZEXPORT inflateMark(z_streamp strm) {
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(strm)
unsigned long ZEXPORT inflateCodesUsed(z_streamp strm) {
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);
}

Changes to compat/zlib/inflate.h.

1
2

3
4
5
6
7
8
9
1

2
3
4
5
6
7
8
9

-
+







/* inflate.h -- internal inflate state definition
 * Copyright (C) 1995-2016 Mark Adler
 * 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.
 */
82
83
84
85
86
87
88
89


90
91
92
93
94
95
96
82
83
84
85
86
87
88

89
90
91
92
93
94
95
96
97







-
+
+







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) */
    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 */

Changes to 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
1

2
3
4
5
6
7
8
9
10
11

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31




32
33


34


35
36
37
38
39
40
41

-
+









-
+



















-
-
-
-
+
+
-
-
+
-
-







/* inftrees.c -- generate Huffman trees for efficient decoding
 * Copyright (C) 1995-2017 Mark Adler
 * 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.2.11 Copyright 1995-2017 Mark Adler ";
   " 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(type, lens, codes, table, bits, work)
codetype type;
unsigned short FAR *lens;
unsigned codes;
int ZLIB_INTERNAL inflate_table(codetype type, unsigned short FAR *lens,
                                unsigned codes, code FAR * FAR *table,
code FAR * FAR *table;
unsigned FAR *bits;
                                unsigned FAR *bits, unsigned short FAR *work) {
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 */
58
59
60
61
62
63
64
65

66
67
68
69
70
71
72
53
54
55
56
57
58
59

60
61
62
63
64
65
66
67







-
+







    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, 77, 202};
        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,

Changes to compat/zlib/inftrees.h.

34
35
36
37
38
39
40
41

42
43
44
45


46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62



34
35
36
37
38
39
40

41
42
43


44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59



60
61
62







-
+


-
-
+
+














-
-
-
+
+
+
    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 distribtution.  The arguments to that
   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 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
   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 OF((codetype type, unsigned short FAR *lens,
                             unsigned codes, code FAR * FAR *table,
                             unsigned FAR *bits, unsigned short FAR *work));
int ZLIB_INTERNAL inflate_table(codetype type, unsigned short FAR *lens,
                                unsigned codes, code FAR * FAR *table,
                                unsigned FAR *bits, unsigned short FAR *work);

Changes to compat/zlib/make_vms.com.

10
11
12
13
14
15
16
17

18
19

20
21
22
23
24
25
26
10
11
12
13
14
15
16

17
18

19
20
21
22
23
24
25
26







-
+

-
+







$!------------------------------------------------------------------------------
$! 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 exmples
$! 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 completly redesigned
$! 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

Changes to compat/zlib/old/visual-basic.txt.

111
112
113
114
115
116
117
118

119
120
121
122
123
124
125
111
112
113
114
115
116
117

118
119
120
121
122
123
124
125







-
+







    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 the original size value to the end
            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

Changes to compat/zlib/os400/README400.

1

2
3
4
5
6

7
8
9
10
11
12
13

1
2
3
4
5

6
7
8
9
10
11
12
13
-
+




-
+







        ZLIB version 1.2.11 for OS/400 installation instructions
        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 suppors gzip format, this is straightforward,
   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')

39
40
41
42
43
44
45
46

47
48
39
40
41
42
43
44
45

46
47
48







-
+


                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 explicitely.
                text data code conversions must be done explicitly.

        Mainly for the reason above, always open zipped files in binary mode.

Changes to compat/zlib/os400/bndsrc.

111
112
113
114
115
116
117








118
119
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127







+
+
+
+
+
+
+
+


  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

Changes to 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
1
2
3

4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

25
26
27

28
29

30
31
32
33
34
35
36
37



-
+




















-
+


-
+

-
+







      *  ZLIB.INC - Interface to the general purpose compression library
      *
      *  ILE RPG400 version by Patrick Monnerat, DATASPHERE.
      *  Version 1.2.11
      *  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.2.11'
     D ZLIB_VERSION    C                   '1.3.1'
     D ZLIB_VERNUM     C                   X'12a0'
     D ZLIB_VER_MAJOR  C                   1
     D ZLIB_VER_MINOR  C                   2
     D ZLIB_VER_MINOR  C                   3
     D ZLIB_VER_REVISION...
     D                 C                   11
     D                 C                   1
     D ZLIB_VER_SUBREVISION...
     D                 C                   0
      *
      *  Other equates.
      *
     D Z_NO_FLUSH      C                   0
     D Z_PARTIAL_FLUSH...

Changes to compat/zlib/qnx/package.qpg.

21
22
23
24
25
26
27
28
29
30
31




32
33
34
35
36
37
38
21
22
23
24
25
26
27




28
29
30
31
32
33
34
35
36
37
38







-
-
-
-
+
+
+
+







      <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.2.11" install="/opt/lib/" user="root:bin" permission="644"/>
         <QPG:Add file="libz.so" install="/opt/lib/" component="dev" filetype="symlink" linkto="libz.so.1.2.11"/>
         <QPG:Add file="libz.so.1" install="/opt/lib/" filetype="symlink" linkto="libz.so.1.2.11"/>
         <QPG:Add file="../libz.so.1.2.11" install="/opt/lib/" component="slib"/>
         <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>
59
60
61
62
63
64
65
66

67
68
69
70
71
72
73
59
60
61
62
63
64
65

66
67
68
69
70
71
72
73







-
+







               <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.2.11</QPM:ReleaseVersion>
               <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>

Changes to compat/zlib/test/example.c.

29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

59
60
61
62
63
64
65
66

67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88

89
90

91
92
93
94
95
96
97
98
29
30
31
32
33
34
35
36
















37
38




39



40
41
42
43

44

45
46
47
48
49
50
51
52
53
54
55
56





57
58
59

60


61

62
63
64
65
66
67
68








-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-


-
-
-
-
+
-
-
-




-
+
-












-
-
-
-
-



-
+
-
-
+
-







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 */

void test_deflate       OF((Byte *compr, uLong comprLen));
void test_inflate       OF((Byte *compr, uLong comprLen,
                            Byte *uncompr, uLong uncomprLen));
void test_large_deflate OF((Byte *compr, uLong comprLen,
                            Byte *uncompr, uLong uncomprLen));
void test_large_inflate OF((Byte *compr, uLong comprLen,
                            Byte *uncompr, uLong uncomprLen));
void test_flush         OF((Byte *compr, uLong *comprLen));
void test_sync          OF((Byte *compr, uLong comprLen,
                            Byte *uncompr, uLong uncomprLen));
void test_dict_deflate  OF((Byte *compr, uLong comprLen));
void test_dict_inflate  OF((Byte *compr, uLong comprLen,
                            Byte *uncompr, uLong uncomprLen));
int  main               OF((int argc, char *argv[]));


#ifdef Z_SOLO

void *myalloc OF((void *, unsigned, unsigned));
void myfree OF((void *, void *));

void *myalloc(q, n, m)
static void *myalloc(void *q, unsigned n, unsigned m) {
    void *q;
    unsigned n, m;
{
    (void)q;
    return calloc(n, m);
}

void myfree(void *q, void *p)
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;

void test_compress      OF((Byte *compr, uLong comprLen,
                            Byte *uncompr, uLong uncomprLen));
void test_gzio          OF((const char *fname,
                            Byte *uncompr, uLong uncomprLen));

/* ===========================================================================
 * Test compress() and uncompress()
 */
void test_compress(compr, comprLen, uncompr, uncomprLen)
static void test_compress(Byte *compr, uLong comprLen, Byte *uncompr,
    Byte *compr, *uncompr;
    uLong comprLen, uncomprLen;
                   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");
107
108
109
110
111
112
113
114
115
116
117

118
119
120
121
122
123
124
125
77
78
79
80
81
82
83




84

85
86
87
88
89
90
91







-
-
-
-
+
-







        printf("uncompress(): %s\n", (char *)uncompr);
    }
}

/* ===========================================================================
 * Test read/write of .gz files
 */
void test_gzio(fname, uncompr, uncomprLen)
    const char *fname; /* compressed file name */
    Byte *uncompr;
    uLong uncomprLen;
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;
193
194
195
196
197
198
199
200

201
202
203
204
205
206
207
208
209
210
159
160
161
162
163
164
165

166



167
168
169
170
171
172
173







-
+
-
-
-







}

#endif /* Z_SOLO */

/* ===========================================================================
 * Test deflate() with small buffers
 */
void test_deflate(compr, comprLen)
static void test_deflate(Byte *compr, uLong comprLen) {
    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;
231
232
233
234
235
236
237
238

239
240

241
242
243
244
245
246
247
248
194
195
196
197
198
199
200

201


202

203
204
205
206
207
208
209







-
+
-
-
+
-







    err = deflateEnd(&c_stream);
    CHECK_ERR(err, "deflateEnd");
}

/* ===========================================================================
 * Test inflate() with small buffers
 */
void test_inflate(compr, comprLen, uncompr, uncomprLen)
static void test_inflate(Byte *compr, uLong comprLen, Byte *uncompr,
    Byte *compr, *uncompr;
    uLong comprLen, uncomprLen;
                  uLong uncomprLen) {
{
    int err;
    z_stream d_stream; /* decompression stream */

    strcpy((char*)uncompr, "garbage");

    d_stream.zalloc = zalloc;
    d_stream.zfree = zfree;
272
273
274
275
276
277
278
279

280
281

282
283
284
285
286
287
288
289
233
234
235
236
237
238
239

240


241

242
243
244
245
246
247
248







-
+
-
-
+
-







        printf("inflate(): %s\n", (char *)uncompr);
    }
}

/* ===========================================================================
 * Test deflate() with large buffers and dynamic change of compression level
 */
void test_large_deflate(compr, comprLen, uncompr, uncomprLen)
static void test_large_deflate(Byte *compr, uLong comprLen, Byte *uncompr,
    Byte *compr, *uncompr;
    uLong comprLen, uncomprLen;
                        uLong uncomprLen) {
{
    z_stream c_stream; /* compression stream */
    int err;

    c_stream.zalloc = zalloc;
    c_stream.zfree = zfree;
    c_stream.opaque = (voidpf)0;

304
305
306
307
308
309
310
311

312
313
314
315
316
317
318
263
264
265
266
267
268
269

270
271
272
273
274
275
276
277







-
+







        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)comprLen/2;
    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;
327
328
329
330
331
332
333
334

335
336

337
338
339
340
341
342
343
344
286
287
288
289
290
291
292

293


294

295
296
297
298
299
300
301







-
+
-
-
+
-







    err = deflateEnd(&c_stream);
    CHECK_ERR(err, "deflateEnd");
}

/* ===========================================================================
 * Test inflate() with large buffers
 */
void test_large_inflate(compr, comprLen, uncompr, uncomprLen)
static void test_large_inflate(Byte *compr, uLong comprLen, Byte *uncompr,
    Byte *compr, *uncompr;
    uLong comprLen, uncomprLen;
                        uLong uncomprLen) {
{
    int err;
    z_stream d_stream; /* decompression stream */

    strcpy((char*)uncompr, "garbage");

    d_stream.zalloc = zalloc;
    d_stream.zfree = zfree;
357
358
359
360
361
362
363
364

365
366
367
368
369
370
371
372
373
374
375
376
377

378
379
380
381
382
383
384
385
314
315
316
317
318
319
320

321
322
323
324
325
326
327
328
329
330
331



332

333
334
335
336
337
338
339







-
+










-
-
-
+
-







        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 + comprLen/2) {
    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
 */
void test_flush(compr, comprLen)
    Byte *compr;
    uLong *comprLen;
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;
406
407
408
409
410
411
412
413
414
415


416
417
418
419
420
421
422
423
360
361
362
363
364
365
366



367
368

369
370
371
372
373
374
375







-
-
-
+
+
-








    *comprLen = c_stream.total_out;
}

/* ===========================================================================
 * Test inflateSync()
 */
void test_sync(compr, comprLen, uncompr, uncomprLen)
    Byte *compr, *uncompr;
    uLong comprLen, uncomprLen;
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;
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
388
389
390
391
392
393
394


395
396

397
398
399
400
401
402
403
404
405
406
407

408



409
410
411
412
413
414
415







-
-
+
+
-











-
+
-
-
-







    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_DATA_ERROR) {
        fprintf(stderr, "inflate should report DATA_ERROR\n");
    if (err != Z_STREAM_END) {
        fprintf(stderr, "inflate should report Z_STREAM_END\n");
        /* Because of incorrect adler32 */
        exit(1);
    }
    err = inflateEnd(&d_stream);
    CHECK_ERR(err, "inflateEnd");

    printf("after inflateSync(): hel%s\n", (char *)uncompr);
}

/* ===========================================================================
 * Test deflate() with preset dictionary
 */
void test_dict_deflate(compr, comprLen)
static void test_dict_deflate(Byte *compr, uLong comprLen) {
    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;

487
488
489
490
491
492
493
494

495
496

497
498
499
500
501
502
503
504
435
436
437
438
439
440
441

442


443

444
445
446
447
448
449
450







-
+
-
-
+
-







    err = deflateEnd(&c_stream);
    CHECK_ERR(err, "deflateEnd");
}

/* ===========================================================================
 * Test inflate() with a preset dictionary
 */
void test_dict_inflate(compr, comprLen, uncompr, uncomprLen)
static void test_dict_inflate(Byte *compr, uLong comprLen, Byte *uncompr,
    Byte *compr, *uncompr;
    uLong comprLen, uncomprLen;
                       uLong uncomprLen) {
{
    int err;
    z_stream d_stream; /* decompression stream */

    strcpy((char*)uncompr, "garbage");

    d_stream.zalloc = zalloc;
    d_stream.zfree = zfree;
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
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







-
-
-
+
-

-
-
+
+







-
+
+







    }
}

/* ===========================================================================
 * Usage:  example [output.gz  [input.gz]]
 */

int main(argc, argv)
    int argc;
    char *argv[];
int main(int argc, char *argv[]) {
{
    Byte *compr, *uncompr;
    uLong comprLen = 10000*sizeof(int); /* don't overflow on MSDOS */
    uLong uncomprLen = comprLen;
    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\n");
        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);
586
587
588
589
590
591
592
593

594
595
596
597
598
599
600
601
602
530
531
532
533
534
535
536

537
538
539
540
541
542
543
544
545
546







-
+









    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 = uncomprLen;
    comprLen = 3 * uncomprLen;

    test_dict_deflate(compr, comprLen);
    test_dict_inflate(compr, comprLen, uncompr, uncomprLen);

    free(compr);
    free(uncompr);

    return 0;
}

Changes to compat/zlib/test/infcover.c.

369
370
371
372
373
374
375
376

377
378
379
380
381
382
383
369
370
371
372
373
374
375

376
377
378
379
380
381
382
383







-
+







    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, ZLIB_VERSION - 1, (int)sizeof(z_stream));
    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);
458
459
460
461
462
463
464

465

466
467
468
469
470
471
472
458
459
460
461
462
463
464
465

466
467
468
469
470
471
472
473







+
-
+







    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;
    buf += len;
    (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;

Changes to compat/zlib/test/minigzip.c.

55
56
57
58
59
60
61
62

63
64
65
66
67
68
69
55
56
57
58
59
60
61

62
63
64
65
66
67
68
69







-
+







#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 OF((const char *));
  extern int unlink(const char *);
#endif
#endif

#if defined(UNDER_CE)
#  include <windows.h>
#  define perror(s) pwinerror(s)

145
146
147
148
149
150
151
152
153
154
155

156
157
158
159
160
161
162
163

164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183

184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
145
146
147
148
149
150
151




152



153
154
155
156

157


158
159
160
161
162
163
164
165
166
167
168
169






170

















171
172
173
174
175
176
177







-
-
-
-
+
-
-
-




-
+
-
-












-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-







#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

void *myalloc OF((void *, unsigned, unsigned));
void myfree OF((void *, void *));

void *myalloc(q, n, m)
static void *myalloc(void *q, unsigned n, unsigned m) {
    void *q;
    unsigned n, m;
{
    (void)q;
    return calloc(n, m);
}

void myfree(q, p)
static void myfree(void *q, void *p) {
    void *q, *p;
{
    (void)q;
    free(p);
}

typedef struct gzFile_s {
    FILE *file;
    int write;
    int err;
    char *msg;
    z_stream strm;
} *gzFile;

gzFile gzopen OF((const char *, const char *));
gzFile gzdopen OF((int, const char *));
gzFile gz_open OF((const char *, int, const char *));

gzFile gzopen(path, mode)
const char *path;
static gzFile gz_open(const char *path, int fd, const char *mode) {
const char *mode;
{
    return gz_open(path, -1, mode);
}

gzFile gzdopen(fd, mode)
int fd;
const char *mode;
{
    return gz_open(NULL, fd, mode);
}

gzFile gz_open(path, fd, mode)
    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;
227
228
229
230
231
232
233
234
235
236
237





238
239
240




241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263

264
265
266
267
268
269
270
271
197
198
199
200
201
202
203




204
205
206
207
208



209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229






230

231
232
233
234
235
236
237







-
-
-
-
+
+
+
+
+
-
-
-
+
+
+
+

















-
-
-
-
-
-
+
-







        return NULL;
    }
    gz->err = 0;
    gz->msg = "";
    return gz;
}

int gzwrite OF((gzFile, const void *, unsigned));

int gzwrite(gz, buf, len)
    gzFile gz;
static gzFile gzopen(const char *path, const char *mode) {
    return gz_open(path, -1, mode);
}

static gzFile gzdopen(int fd, const char *mode) {
    const void *buf;
    unsigned len;
{
    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;
}

int gzread OF((gzFile, void *, unsigned));

int gzread(gz, buf, len)
    gzFile gz;
    void *buf;
    unsigned 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;
288
289
290
291
292
293
294
295
296
297

298
299
300
301
302
303
304
305
306
254
255
256
257
258
259
260



261


262
263
264
265
266
267
268







-
-
-
+
-
-







        }
        if (ret == Z_STREAM_END)
            inflateReset(strm);
    } while (strm->avail_out);
    return len - strm->avail_out;
}

int gzclose OF((gzFile));

int gzclose(gz)
static int gzclose(gzFile gz) {
    gzFile gz;
{
    z_stream *strm;
    unsigned char out[BUFLEN];

    if (gz == NULL)
        return Z_STREAM_ERROR;
    strm = &(gz->strm);
    if (gz->write) {
317
318
319
320
321
322
323
324
325
326

327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352

353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393

394
395

396
397
398
399
400
401
402
403
404
405
279
280
281
282
283
284
285



286



287
288
289
290
291
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
        inflateEnd(strm);
    fclose(gz->file);
    free(gz);
    return Z_OK;
}

const char *gzerror OF((gzFile, int *));

const char *gzerror(gz, err)
static const char *gzerror(gzFile gz, int *err) {
    gzFile gz;
    int *err;
{
    *err = gz->err;
    return gz->msg;
}

#endif

static char *prog;

void error            OF((const char *msg));
void gz_compress      OF((FILE   *in, gzFile out));
#ifdef USE_MMAP
int  gz_compress_mmap OF((FILE   *in, gzFile out));
#endif
void gz_uncompress    OF((gzFile in, FILE   *out));
void file_compress    OF((char  *file, char *mode));
void file_uncompress  OF((char  *file));
int  main             OF((int argc, char *argv[]));

/* ===========================================================================
 * Display error message and exit
 */
void error(msg)
    const char *msg;
static void error(const char *msg) {
{
    fprintf(stderr, "%s: %s\n", prog, msg);
    exit(1);
}

/* ===========================================================================
 * Compress input to output then close both files.
 */

void gz_compress(in, out)
    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");
}

#ifdef USE_MMAP /* MMAP version, Miguel Albrecht <malbrech@eso.org> */

/* Try compressing the input file at once using mmap. Return Z_OK if
 * if success, Z_ERRNO otherwise.
 * success, Z_ERRNO otherwise.
 */
int gz_compress_mmap(in, out)
static int gz_compress_mmap(FILE *in, gzFile out) {
    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;

419
420
421
422
423
424
425
426
427
428

429
430
431
432
433






























434
435
436
437
438
439
440
329
330
331
332
333
334
335
336
337

338
339




340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376









-
+

-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+








    munmap(buf, buf_len);
    fclose(in);
    if (gzclose(out) != Z_OK) error("failed gzclose");
    return Z_OK;
}
#endif /* USE_MMAP */

/* ===========================================================================
 * Uncompress input to output then close both files.
 * Compress input to output then close both files.
 */
void gz_uncompress(in, out)
    gzFile in;
    FILE   *out;
{

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));
450
451
452
453
454
455
456
457

458
459
460
461
462
463
464
465
466
467
386
387
388
389
390
391
392

393



394
395
396
397
398
399
400







-
+
-
-
-







}


/* ===========================================================================
 * Compress the given file: create a corresponding .gz file and remove the
 * original.
 */
void file_compress(file, mode)
static void file_compress(char *file, char *mode) {
    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);
489
490
491
492
493
494
495
496

497
498
499
500
501
502
503

504
505
506
507
508
509
510
422
423
424
425
426
427
428

429


430
431
432
433

434
435
436
437
438
439
440
441







-
+
-
-




-
+







    unlink(file);
}


/* ===========================================================================
 * Uncompress the given file and remove the original.
 */
void file_uncompress(file)
static void file_uncompress(char *file) {
    char  *file;
{
    local char buf[MAX_NAME_LEN];
    char *infile, *outfile;
    FILE  *out;
    gzFile in;
    unsigned len = strlen(file);
    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)
549
550
551
552
553
554
555
556
557
558

559
560
561
562
563
564
565
566
480
481
482
483
484
485
486



487

488
489
490
491
492
493
494







-
-
-
+
-







 *   -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(argc, argv)
    int argc;
    char *argv[];
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 ");

Changes to compat/zlib/treebuild.xml.

1
2
3


4
5
6
7
8
9
10
1


2
3
4
5
6
7
8
9
10

-
-
+
+







<?xml version="1.0" ?>
<package name="zlib" version="1.2.11">
    <library name="zlib" dlversion="1.2.11" dlname="z">
<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" />

Changes to compat/zlib/trees.c.

1
2

3
4
5
6
7
8
9
1

2
3
4
5
6
7
8
9

-
+







/* trees.c -- output deflated data using Huffman coding
 * Copyright (C) 1995-2017 Jean-loup Gailly
 * 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
 *
118
119
120
121
122
123
124






125

126
127
128

129
130
131

132
133
134







135





















































136





137

138

139
140
141
142
143
144













145
146
147
148

149
150

151
152
153



154
155





156
157

158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184

185
186
187
188
189
190
191
192
193
194
195
196

197
198
199
200
201
202
203
118
119
120
121
122
123
124
125
126
127
128
129
130

131
132
133

134
135
136

137
138
139
140
141
142
143
144
145
146
147

148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208

209






210
211
212
213
214
215
216
217
218
219
220
221
222




223


224



225
226
227


228
229
230
231
232
233

234
235
236
237
238
239
240
241
242
243
244
245
246









247
248
249
250
251

252






253
254
255
256
257

258
259
260
261
262
263
264
265







+
+
+
+
+
+
-
+


-
+


-
+



+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+

+
-
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
-
-
+
-
-
-
+
+
+
-
-
+
+
+
+
+

-
+












-
-
-
-
-
-
-
-
-





-
+
-
-
-
-
-
-





-
+







    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 const static_tree_desc  static_l_desc =
local TCONST static_tree_desc static_l_desc =
{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS};

local const static_tree_desc  static_d_desc =
local TCONST static_tree_desc static_d_desc =
{static_dtree, extra_dbits, 0,          D_CODES, MAX_BITS};

local const static_tree_desc  static_bl_desc =
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)); \
}
 * Local (static) routines in this file.

/* ===========================================================================
 * 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
local void tr_static_init OF((void));
     * without bit reversal.
local void init_block     OF((deflate_state *s));
local void pqdownheap     OF((deflate_state *s, ct_data *tree, int k));
local void gen_bitlen     OF((deflate_state *s, tree_desc *desc));
local void gen_codes      OF((ct_data *tree, int max_code, ushf *bl_count));
local void build_tree     OF((deflate_state *s, tree_desc *desc));
local void scan_tree      OF((deflate_state *s, ct_data *tree, int max_code));
     */
    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++) {
local void send_tree      OF((deflate_state *s, ct_data *tree, int max_code));
local int  build_bl_tree  OF((deflate_state *s));
local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes,
                              int blcodes));
        int len = tree[n].Len;
local void compress_block OF((deflate_state *s, const ct_data *ltree,
                              const ct_data *dtree));
        if (len == 0) continue;
local int  detect_data_type OF((deflate_state *s));
local unsigned bi_reverse OF((unsigned value, int length));
local void bi_windup      OF((deflate_state *s));
        /* Now reverse the bits */
        tree[n].Code = (ush)bi_reverse(next_code[len]++, len);

local void bi_flush       OF((deflate_state *s));

        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 OF((void));
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

/* ===========================================================================
 * 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)); \
}

/* ===========================================================================
 * 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      OF((deflate_state *s, int value, int length));
local void send_bits(deflate_state *s, int value, int length) {

local void send_bits(s, value, length)
    deflate_state *s;
    int value;  /* value to send */
    int length; /* number of bits */
{
    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))
     * (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;
225
226
227
228
229
230
231
232

233
234
235
236
237
238
239
240
287
288
289
290
291
292
293

294

295
296
297
298
299
300
301







-
+
-









/* the arguments must not have side effects */

/* ===========================================================================
 * Initialize the various 'constant' tables.
 */
local void tr_static_init()
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 */
252
253
254
255
256
257
258
259

260
261
262
263
264
265
266
267
268

269
270
271
272
273
274

275
276
277
278
279
280
281
282

283
284
285
286

287
288
289
290
291
292
293
313
314
315
316
317
318
319

320
321
322
323
324
325
326
327
328

329
330
331
332
333
334

335
336
337
338
339
340
341
342

343
344
345
346

347
348
349
350
351
352
353
354







-
+








-
+





-
+







-
+



-
+







    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++) {
        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;
    _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++) {
        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++) {
        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");
    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]++;
308
309
310
311
312
313
314
315

316
317
318
319
320
321
322
323
324

325
326

327
328
329
330
331
332
333
334
369
370
371
372
373
374
375

376
377
378
379
380
381
382
383
384

385
386

387

388
389
390
391
392
393
394







-
+








-
+

-
+
-







#  ifdef GEN_TREES_H
    gen_trees_header();
#  endif
#endif /* defined(GEN_TREES_H) || !defined(STDC) */
}

/* ===========================================================================
 * Genererate the file trees.h describing the static trees.
 * 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" : ", "))
       ((i) % (width) == (width) - 1 ? ",\n" : ", "))

void gen_trees_header()
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");

368
369
370
371
372
373
374
















375
376
377
378
379

380
381
382
383
384
385
386
387
388
428
429
430
431
432
433
434
435
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+




-
+
-
-







        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(s)
void ZLIB_INTERNAL _tr_init(deflate_state *s) {
    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;
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
471
472
473
474
475
476
477


















478
479
480
481
482
483
484







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-







    s->bits_sent = 0L;
#endif

    /* Initialize the first block of the first file: */
    init_block(s);
}

/* ===========================================================================
 * Initialize a new block.
 */
local void init_block(s)
    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->last_lit = s->matches = 0;
}

#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.
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
500
501
502
503
504
505
506

507




508
509
510
511
512

513
514
515
516
517
518
519
520







-
+
-
-
-
-





-
+








/* ===========================================================================
 * 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(s, tree, k)
local void pqdownheap(deflate_state *s, ct_data *tree, int k) {
    deflate_state *s;
    ct_data *tree;  /* the tree to restore */
    int k;               /* node to move down */
{
    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)) {
            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;
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
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







-
+
-
-
-




















-
+










-
+











-
+

-
-
+
+







 * 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(s, desc)
local void gen_bitlen(deflate_state *s, tree_desc *desc) {
    deflate_state *s;
    tree_desc *desc;    /* the tree descriptor */
{
    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++) {
    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];
        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;
        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[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);

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
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







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
+
-
-
-
-









-
+
-
-
-








-
+







                tree[m].Len = (ush)bits;
            }
            n--;
        }
    }
}

/* ===========================================================================
 * 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 (tree, max_code, bl_count)
    ct_data *tree;             /* the tree to decorate */
    int max_code;              /* largest code with non zero frequency */
    ushf *bl_count;            /* number of codes at each bit length */
{
    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 */

#ifdef DUMP_BL_TREE
    /* 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));

#  include <stdio.h>
    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);

#endif
        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));
    }
}

/* ===========================================================================
 * 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(s, desc)
local void build_tree(deflate_state *s, tree_desc *desc) {
    deflate_state *s;
    tree_desc *desc; /* the tree descriptor */
{
    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[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;
648
649
650
651
652
653
654
655

656
657
658
659
660
661
662
655
656
657
658
659
660
661

662
663
664
665
666
667
668
669







-
+







        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,
    /* 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.
     */
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
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







-
+
-
-
-
-









-
+


-
+







    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 (s, tree, max_code)
local void scan_tree(deflate_state *s, ct_data *tree, int max_code) {
    deflate_state *s;
    ct_data *tree;   /* the tree to be scanned */
    int max_code;    /* and its largest code of non zero frequency */
{
    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 */
    tree[max_code + 1].Len = (ush)0xffff; /* guard */

    for (n = 0; n <= max_code; n++) {
        curlen = nextlen; nextlen = tree[n+1].Len;
        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++;
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
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







-
+
-
-
-
-








-
+



-
+










-
+


-
+


-
+
















-
+
-
-








-
-
+
+










-
+











-
+
-
-
+
-






-
-
-
+
+
+






-
+


-
+






-
+
-
-
-
+
-
-
-
+



+
-
+





-
+






-
+
-
-







-
+
-
-







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





-
+
-
-
-
+
-
-







    }
}

/* ===========================================================================
 * Send a literal or distance tree in compressed form, using the codes in
 * bl_tree.
 */
local void send_tree (s, tree, max_code)
local void send_tree(deflate_state *s, ct_data *tree, int max_code) {
    deflate_state *s;
    ct_data *tree; /* the tree to be scanned */
    int max_code;       /* and its largest code of non zero frequency */
{
    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 */
    /* 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;
        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);
            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);
            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);
            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(s)
local int build_bl_tree(deflate_state *s) {
    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.
    /* 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;
    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(s, lcodes, dcodes, blcodes)
local void send_all_trees(deflate_state *s, int lcodes, int dcodes,
    deflate_state *s;
    int lcodes, dcodes, blcodes; /* number of codes for each tree */
                          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 */
    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 */
    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 */
    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(s, buf, stored_len, last)
void ZLIB_INTERNAL _tr_stored_block(deflate_state *s, charf *buf,
    deflate_state *s;
    charf *buf;       /* input block */
    ulg stored_len;   /* length of input block */
                                    ulg stored_len, int last) {
    int last;         /* one if this is the last block for a file */
{
    send_bits(s, (STORED_BLOCK<<1)+last, 3);    /* send block type */
    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);
        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;
    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(s)
void ZLIB_INTERNAL _tr_flush_bits(deflate_state *s) {
    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(s)
void ZLIB_INTERNAL _tr_align(deflate_state *s) {
    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(s, buf, stored_len, last)
void ZLIB_INTERNAL _tr_flush_block(deflate_state *s, charf *buf,
    deflate_state *s;
    charf *buf;       /* input block, or NULL if too old */
    ulg stored_len;   /* length of input block */
                                   ulg stored_len, int last) {
    int last;         /* one if this is the last block for a file */
{
    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 */
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
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




































































































































































-
-
+
+



-
+

+
-
+
+
+









-
+










-
-
+
-
-
-
-
+






-
-
-
+
+
+


















-
-
+
+






-
-
+
+
-
-
-
-
-
+
+
+
+
+
+
+











-
+


-
+
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

        /* 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;
        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->last_lit));
                s->sym_next / 3));

#ifndef FORCE_STATIC
        if (static_lenb <= opt_lenb) opt_lenb = static_lenb;
        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) {
    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);

#ifdef FORCE_STATIC
    } else if (static_lenb >= 0) { /* force static trees */
    } else if (static_lenb == opt_lenb) {
#else
    } else if (s->strategy == Z_FIXED || static_lenb == opt_lenb) {
#endif
        send_bits(s, (STATIC_TREES<<1)+last, 3);
        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);
        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));
    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 (s, dist, lc)
    deflate_state *s;
int ZLIB_INTERNAL _tr_tally(deflate_state *s, unsigned dist, unsigned lc) {
#ifdef LIT_MEM
    unsigned dist;  /* distance of matched string */
    unsigned lc;    /* match length-MIN_MATCH or unmatched char (if dist==0) */
{
    s->d_buf[s->last_lit] = (ush)dist;
    s->l_buf[s->last_lit++] = (uch)lc;
    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_ltree[_length_code[lc] + LITERALS + 1].Freq++;
        s->dyn_dtree[d_code(dist)].Freq++;
    }

    return (s->sym_next == s->sym_end);
#ifdef TRUNCATE_BLOCK
    /* Try to guess if it is profitable to stop the current block here */
    if ((s->last_lit & 0x1fff) == 0 && s->level > 2) {
        /* Compute an upper bound for the compressed length */
        ulg out_length = (ulg)s->last_lit*8L;
        ulg in_length = (ulg)((long)s->strstart - s->block_start);
        int dcode;
        for (dcode = 0; dcode < D_CODES; dcode++) {
            out_length += (ulg)s->dyn_dtree[dcode].Freq *
                (5L+extra_dbits[dcode]);
        }
}
        out_length >>= 3;
        Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ",
               s->last_lit, in_length, out_length,
               100L - out_length*100L/in_length));
        if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1;
    }
#endif
    return (s->last_lit == s->lit_bufsize-1);
    /* We avoid equality with lit_bufsize because of wraparound at 64K
     * on 16 bit machines and because stored blocks are restricted to
     * 64K-1 bytes.
     */
}

/* ===========================================================================
 * Send the block data compressed using the given Huffman trees
 */
local void compress_block(s, ltree, dtree)
    deflate_state *s;
    const ct_data *ltree; /* literal tree */
    const ct_data *dtree; /* distance tree */
{
    unsigned dist;      /* distance of matched string */
    int lc;             /* match length or unmatched char (if dist == 0) */
    unsigned lx = 0;    /* running index in l_buf */
    unsigned code;      /* the code to send */
    int extra;          /* number of extra bits to send */

    if (s->last_lit != 0) do {
        dist = s->d_buf[lx];
        lc = s->l_buf[lx++];
        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 the 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 that the overlay between pending_buf and d_buf+l_buf is ok: */
        Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx,
               "pendingBuf overflow");

    } while (lx < s->last_lit);

    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
 *       "black list" (0..6, 14..25, 28..31).
 *    b) There is at least one printable character belonging to the
 *       "white 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(s)
    deflate_state *s;
{
    /* black_mask is the bit mask of black-listed bytes
     * set bits 0..6, 14..25, and 28..31
     * 0xf3ffc07f = binary 11110011111111111100000001111111
     */
    unsigned long black_mask = 0xf3ffc07fUL;
    int n;

    /* Check for non-textual ("black-listed") bytes. */
    for (n = 0; n <= 31; n++, black_mask >>= 1)
        if ((black_mask & 1) && (s->dyn_ltree[n].Freq != 0))
            return Z_BINARY;

    /* Check for textual ("white-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 "black-listed" or "white-listed" bytes:
     * this stream either is empty or has tolerated ("gray-listed") bytes only.
     */
    return Z_BINARY;
}

/* ===========================================================================
 * 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(code, len)
    unsigned code; /* the value to invert */
    int len;       /* its bit length */
{
    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(s)
    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(s)
    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
}

Changes to compat/zlib/uncompr.c.

20
21
22
23
24
25
26
27
28
29
30
31


32
33
34
35
36
37
38
39
20
21
22
23
24
25
26





27
28

29
30
31
32
33
34
35







-
-
-
-
-
+
+
-







   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 (dest, destLen, source, sourceLen)
    Bytef *dest;
    uLongf *destLen;
    const Bytef *source;
    uLong *sourceLen;
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;
79
80
81
82
83
84
85
86
87
88
89
90


91
92
93
75
76
77
78
79
80
81





82
83

84
85







-
-
-
-
-
+
+
-


    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 (dest, destLen, source, sourceLen)
    Bytef *dest;
    uLongf *destLen;
    const Bytef *source;
    uLong sourceLen;
int ZEXPORT uncompress(Bytef *dest, uLongf *destLen, const Bytef *source,
                       uLong sourceLen) {
{
    return uncompress2(dest, destLen, source, &sourceLen);
}

Changes to 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
1
2
3
4
5

6
7
8
9
10
11
12
13

14
15
16




17
18
19
20
21
22
23





-
+







-
+


-
-
-
-








            Frequently Asked Questions about ZLIB1.DLL


This document describes the design, the rationale, and the usage
of the official DLL build of zlib, named ZLIB1.DLL.  If you have
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 official build of zlib as a DLL.
  - ZLIB1.DLL is the common build of zlib as a DLL.
    (Please remark the character '1' in the name.)

    Pointers to a precompiled ZLIB1.DLL can be found in the zlib
    web site at:
      http://www.zlib.net/

    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.
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
371
372
373
374
375
376
377












378
379
380
381







-
-
-
-
-
-
-
-
-
-
-
-




    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.


17. I made my own ZLIB1.DLL build.  Can I test it for compliance?

  - We prefer that you download the official DLL from the zlib
    web site.  If you need something peculiar from this DLL, you
    can send your suggestion to the zlib mailing list.

    However, in case you do rebuild the DLL yourself, you can run
    it with the test programs found in the DLL distribution.
    Running these test programs is not a guarantee of compliance,
    but a failure can imply a detected problem.

**

This document is written and maintained by
Cosmin Truta <cosmint@cs.ubbcluj.ro>

Changes to compat/zlib/win32/Makefile.bor.

1
2
3
4
5
6
7
8
9
10
11
12
13
1
2
3
4
5

6
7
8
9
10
11
12





-







# Makefile for zlib
# Borland C++ for Win32
#
# Usage:
#  make -f win32/Makefile.bor
#  make -f win32/Makefile.bor LOCAL_ZLIB=-DASMV OBJA=match.obj OBJPA=+match.obj

# ------------ 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)

Changes to 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
1
2
3
4
5
6
7
8
9
10
11
12
13




14
15
16
17
18
19
20













-
-
-
-







# 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 use the asm code, type:
#   cp contrib/asm?86/match.S ./match.S
#   make LOC=-DASMV OBJA=match.o -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 :
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
30
31
32
33
34
35
36

37
38
39
40
41
42
43







-







IMPLIB    = libz.dll.a

#
# Set to 1 if shared object needs to be installed
#
SHARED_MODE=0

#LOC = -DASMV
#LOC = -DZLIB_DEBUG -g

PREFIX =
CC = $(PREFIX)gcc
CFLAGS = $(LOC) -O3 -Wall

AS = $(CC)

Changes to compat/zlib/win32/Makefile.msc.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1
2
3
4
5
6




7
8
9
10
11
12
13






-
-
-
-







# 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)
#   nmake -f win32/Makefile.msc LOC="-DASMV -DASMINF" \
#         OBJA="inffas32.obj match686.obj"               (use ASM code, x86)
#   nmake -f win32/Makefile.msc AS=ml64 LOC="-DASMV -DASMINF -I." \
#         OBJA="inffasx64.obj gvmat64.obj inffas8664.obj"  (use ASM code, x64)

# The toplevel directory of the source tree.
#
TOP = .

# optional build flags
LOC =

Changes to 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
1
2

3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18


19
20
21
22
23
24

25
26
27
28
29
30
31
32


-
+















-
-
+
+




-
+







ZLIB DATA COMPRESSION LIBRARY

zlib 1.2.11 is a general purpose data compression library.  All the code is
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 the zlib FAQ http://zlib.net/zlib_faq.html
before asking for help.
PLEASE read DLL_FAQ.txt, and the zlib FAQ http://zlib.net/zlib_faq.html before
asking for help.


Manifest:

The package zlib-1.2.11-win32-x86.zip will contain the following files:
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)

Changes to compat/zlib/win32/zlib.def.

65
66
67
68
69
70
71

72
73
74
75
76
77
78


79
80
81
82
83
84
85
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88







+







+
+







; 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_

Changes to compat/zlib/win32/zlib1.rc.

22
23
24
25
26
27
28
29

30
31
32
33
34
35
36
22
23
24
25
26
27
28

29
30
31
32
33
34
35
36







-
+







  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-2017 Jean-loup Gailly & Mark Adler\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"

Changes to compat/zlib/zconf.h.

1
2

3
4
5
6
7
8
9
1

2
3
4
5
6
7
8
9

-
+







/* zconf.h -- configuration of the zlib compression library
 * Copyright (C) 1995-2016 Jean-loup Gailly, Mark Adler
 * 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
34
35
36
37
38
39
40



41
42
43
44
45
46
47
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50







+
+
+







#    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
234
235
236
237
238
239
240



241


242
243
244
245
246
247
248
237
238
239
240
241
242
243
244
245
246

247
248
249
250
251
252
253
254
255







+
+
+
-
+
+







#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;
     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;
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
296
297
298
299
300
301
302








303
304
305
306
307
308
309







-
-
-
-
-
-
-
-







#  ifdef STDC
#    define OF(args)  args
#  else
#    define OF(args)  ()
#  endif
#endif

#ifndef Z_ARG /* function prototypes for stdarg */
#  if defined(STDC) || defined(Z_HAVE_STDARG_H)
#    define Z_ARG(args)  args
#  else
#    define Z_ARG(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
345
346
347
348
349
350
351



352
353
354
355
356
357
358
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360







+
+
+







    * 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
463
464
465
466
467
468
469
470
471




472





473
474

475
476
477
478
479
480
481
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







-
-
+
+
+
+

+
+
+
+
+

-
+







 * 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

#if defined(__WATCOMC__) && !defined(Z_HAVE_UNISTD_H)
#  define Z_HAVE_UNISTD_H
#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) || defined(_LARGEFILE64_SOURCE)
#  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
503
504
505
506
507
508
509
510

511
512
513
514
515
516
517
512
513
514
515
516
517
518

519
520
521
522
523
524
525
526







-
+







#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__) && !defined(Z_SOLO)
#  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 */

Changes to compat/zlib/zconf.h.cmakein.

1
2

3
4
5
6
7
8
9
1

2
3
4
5
6
7
8
9

-
+







/* zconf.h -- configuration of the zlib compression library
 * Copyright (C) 1995-2016 Jean-loup Gailly, Mark Adler
 * 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
36
37
38
39
40
41
42



43
44
45
46
47
48
49
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52







+
+
+







#    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
236
237
238
239
240
241
242



243


244
245
246
247
248
249
250
239
240
241
242
243
244
245
246
247
248

249
250
251
252
253
254
255
256
257







+
+
+
-
+
+







#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;
     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;
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
298
299
300
301
302
303
304








305
306
307
308
309
310
311







-
-
-
-
-
-
-
-







#  ifdef STDC
#    define OF(args)  args
#  else
#    define OF(args)  ()
#  endif
#endif

#ifndef Z_ARG /* function prototypes for stdarg */
#  if defined(STDC) || defined(Z_HAVE_STDARG_H)
#    define Z_ARG(args)  args
#  else
#    define Z_ARG(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
347
348
349
350
351
352
353



354
355
356
357
358
359
360
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362







+
+
+







    * 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
465
466
467
468
469
470
471
472
473




474





475
476

477
478
479
480
481
482
483
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







-
-
+
+
+
+

+
+
+
+
+

-
+







 * 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

#if defined(__WATCOMC__) && !defined(Z_HAVE_UNISTD_H)
#  define Z_HAVE_UNISTD_H
#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) || defined(_LARGEFILE64_SOURCE)
#  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
505
506
507
508
509
510
511
512

513
514
515
516
517
518
519
514
515
516
517
518
519
520

521
522
523
524
525
526
527
528







-
+







#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__) && !defined(Z_SOLO)
#  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 */

Changes to compat/zlib/zconf.h.in.

1
2

3
4
5
6
7
8
9
1

2
3
4
5
6
7
8
9

-
+







/* zconf.h -- configuration of the zlib compression library
 * Copyright (C) 1995-2016 Jean-loup Gailly, Mark Adler
 * 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
34
35
36
37
38
39
40



41
42
43
44
45
46
47
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50







+
+
+







#    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
234
235
236
237
238
239
240



241


242
243
244
245
246
247
248
237
238
239
240
241
242
243
244
245
246

247
248
249
250
251
252
253
254
255







+
+
+
-
+
+







#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;
     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;
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
296
297
298
299
300
301
302








303
304
305
306
307
308
309







-
-
-
-
-
-
-
-







#  ifdef STDC
#    define OF(args)  args
#  else
#    define OF(args)  ()
#  endif
#endif

#ifndef Z_ARG /* function prototypes for stdarg */
#  if defined(STDC) || defined(Z_HAVE_STDARG_H)
#    define Z_ARG(args)  args
#  else
#    define Z_ARG(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
345
346
347
348
349
350
351



352
353
354
355
356
357
358
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360







+
+
+







    * 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
463
464
465
466
467
468
469
470
471




472





473
474

475
476
477
478
479
480
481
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







-
-
+
+
+
+

+
+
+
+
+

-
+







 * 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

#if defined(__WATCOMC__) && !defined(Z_HAVE_UNISTD_H)
#  define Z_HAVE_UNISTD_H
#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) || defined(_LARGEFILE64_SOURCE)
#  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
503
504
505
506
507
508
509
510

511
512
513
514
515
516
517
512
513
514
515
516
517
518

519
520
521
522
523
524
525
526







-
+







#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__) && !defined(Z_SOLO)
#  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 */

Changes to compat/zlib/zlib.3.

1

2
3
4
5
6
7
8

1
2
3
4
5
6
7
8
-
+







.TH ZLIB 3 "15 Jan 2017"
.TH ZLIB 3 "22 Jan 2024"
.SH NAME
zlib \- compression/decompression library
.SH SYNOPSIS
[see
.I zlib.h
for full description]
.SH DESCRIPTION
101
102
103
104
105
106
107
108

109
110

111
112
113
114
115
116
117
101
102
103
104
105
106
107

108
109

110
111
112
113
114
115
116
117







-
+

-
+







.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.2.11
Version 1.3.1
.LP
Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler
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

Changes to compat/zlib/zlib.3.pdf.

cannot compute difference between binary files

Changes to compat/zlib/zlib.h.

1
2

3
4

5
6
7
8
9
10
11
1

2
3

4
5
6
7
8
9
10
11

-
+

-
+







/* zlib.h -- interface of the 'zlib' general purpose compression library
  version 1.2.11, January 15th, 2017
  version 1.3.1, January 22nd, 2024

  Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler
  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
33
34
35
36
37
38
39
40
41


42
43
44


45
46
47
48
49
50
51
33
34
35
36
37
38
39


40
41
42


43
44
45
46
47
48
49
50
51







-
-
+
+

-
-
+
+








#include "zconf.h"

#ifdef __cplusplus
extern "C" {
#endif

#define ZLIB_VERSION "1.2.11"
#define ZLIB_VERNUM 0x12b0
#define ZLIB_VERSION "1.3.1"
#define ZLIB_VERNUM 0x1310
#define ZLIB_VER_MAJOR 1
#define ZLIB_VER_MINOR 2
#define ZLIB_VER_REVISION 11
#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
74
75
76
77
78
79
80
81
82


83
84
85
86
87
88
89
74
75
76
77
78
79
80


81
82
83
84
85
86
87
88
89







-
-
+
+







  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) OF((voidpf opaque, uInt items, uInt size));
typedef void   (*free_func)  OF((voidpf opaque, voidpf address));
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 */
213
214
215
216
217
218
219
220

221
222
223
224
225
226
227
228

229
230
231
232
233

234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250

251
252
253
254
255
256
257
213
214
215
216
217
218
219

220
221
222
223
224
225
226
227

228
229
230
231
232

233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249

250
251
252
253
254
255
256
257







-
+







-
+




-
+
















-
+








#define zlib_version zlibVersion()
/* for compatibility with versions < 1.0.2 */


                        /* basic functions */

ZEXTERN const char * ZEXPORT zlibVersion OF((void));
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 OF((z_streamp strm, int level));
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.
   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 OF((z_streamp strm, int flush));
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
272
273
274
275
276
277
278
279

280
281
282
283
284
285
286
272
273
274
275
276
277
278

279
280
281
282
283
284
285
286







-
+







  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 ouput
  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
316
317
318
319
320
321
322
323
324


325
326
327
328
329
330
331
316
317
318
319
320
321
322


323
324
325
326
327
328
329
330
331







-
-
+
+







  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 to avoid repeated flush markers due to
  avail_out == 0 on return.
  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
356
357
358
359
360
361
362
363

364
365
366
367
368
369
370
371
372
373
374
375
376
377
378

379
380
381
382
383
384
385
386


387
388
389
390
391
392
393
394
395
396
397
398
399
400

401
402
403
404
405
406
407
356
357
358
359
360
361
362

363
364
365
366
367
368
369
370
371
372
373
374
375
376
377

378
379
380
381
382
383
384
385

386
387
388
389
390
391
392
393
394
395
396
397
398
399
400

401
402
403
404
405
406
407
408







-
+














-
+







-
+
+













-
+







  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 OF((z_streamp strm));
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 OF((z_streamp strm));
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.
   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 OF((z_streamp strm, int flush));
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
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
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







-
+

















-
-
-
-
-
-
+
+
+
+
+
+


-
+
-







  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 OF((z_streamp strm));
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 OF((z_streamp strm,
                                     int  level,
                                     int  method,
                                     int  windowBits,
                                     int  memLevel,
                                     int  strategy));
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 next_in, zalloc, zfree and opaque must be initialized before by the
   fields zalloc, zfree and opaque must be initialized before by the caller.
   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
604
605
606
607
608
609
610
611
612
613



614
615
616
617
618
619
620
604
605
606
607
608
609
610



611
612
613
614
615
616
617
618
619
620







-
-
-
+
+
+







   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 OF((z_streamp strm,
                                             const Bytef *dictionary,
                                             uInt  dictLength));
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
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
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







-
-
-
+
+
+






-
+












-
-
+
+
















-
+




-
+





-
-
-
+
+
+






-
-
-
-
-
+
+
+
+
+
+












-
+










-
-
-
-
-
+
+
+
+
+












-
-
+
+













-
-
-
+
+
+












-
-
-
+
+
+














-
-
+
+















-
-
+
+
+






-
-
+
+







     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 OF((z_streamp strm,
                                             Bytef *dictionary,
                                             uInt  *dictLength));
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.
   Similary, if dictLength is Z_NULL, then it is not set.
   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 OF((z_streamp dest,
                                    z_streamp source));
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 OF((z_streamp strm));
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.
   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 OF((z_streamp strm,
                                      int level,
                                      int strategy));
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 any input has been consumed in a previous
   deflate() call, 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().
   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 the data compressed after deflateParams().
   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 OF((z_streamp strm,
                                    int good_length,
                                    int max_lazy,
                                    int nice_length,
                                    int max_chain));
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 OF((z_streamp strm,
                                       uLong sourceLen));
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 OF((z_streamp strm,
                                       unsigned *pending,
                                       int *bits));
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 OF((z_streamp strm,
                                     int bits,
                                     int value));
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 OF((z_streamp strm,
                                         gz_headerp head));
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 255, with no extra, name, or comment
   fields.  The gzip header is returned to the default state by deflateReset().
   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 OF((z_streamp strm,
                                     int  windowBits));
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
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
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







-
-
-
+
+
+
+
+













-
-
-
+
+
+




















-
-
-
+
+
+






-
+





-
+












-
-
-
-
+
+
+
+


-
-
+
+














-
+




+





-
-
+
+












-
-
-
+
+
+


















-
+







   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 streams.
   inflate() will return Z_STREAM_END at the end of the gzip stream.  The state
   would need to be reset to continue decoding a subsequent gzip stream.
   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 OF((z_streamp strm,
                                             const Bytef *dictionary,
                                             uInt  dictLength));
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 OF((z_streamp strm,
                                             Bytef *dictionary,
                                             uInt  *dictLength));
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.
   Similary, if dictLength is Z_NULL, then it is not set.
   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 OF((z_streamp strm));
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 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.
   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 OF((z_streamp dest,
                                    z_streamp source));
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 OF((z_streamp strm));
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 OF((z_streamp strm,
                                      int windowBits));
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 OF((z_streamp strm,
                                     int bits,
                                     int value));
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 OF((z_streamp strm));
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
1023
1024
1025
1026
1027
1028
1029
1030
1031


1032
1033
1034
1035
1036
1037
1038
1028
1029
1030
1031
1032
1033
1034


1035
1036
1037
1038
1039
1040
1041
1042
1043







-
-
+
+







   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 OF((z_streamp strm,
                                         gz_headerp head));
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
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
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







-
-
+
+



















-
-
-
+
+
+

-
-
-
+
+
+







   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 OF((z_streamp strm, int windowBits,
                                        unsigned char FAR *window));
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) OF((void FAR *,
                                z_const unsigned char FAR * FAR *));
typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned));
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 OF((z_streamp strm,
                                    in_func in, void FAR *in_desc,
                                    out_func out, void FAR *out_desc));
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
1159
1160
1161
1162
1163
1164
1165
1166

1167
1168
1169
1170
1171
1172
1173
1174

1175
1176
1177
1178
1179
1180
1181
1164
1165
1166
1167
1168
1169
1170

1171
1172
1173
1174
1175
1176
1177
1178

1179
1180
1181
1182
1183
1184
1185
1186







-
+







-
+







   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 OF((z_streamp strm));
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 OF((void));
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
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
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







-
-
+
+













-
-
-
+
+
+













-
+






-
-
+
+
















-
-
+
+


















-
+

+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-







     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 OF((Bytef *dest,   uLongf *destLen,
                                 const Bytef *source, uLong sourceLen));
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 OF((Bytef *dest,   uLongf *destLen,
                                  const Bytef *source, uLong sourceLen,
                                  int level));
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 OF((uLong sourceLen));
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 OF((Bytef *dest,   uLongf *destLen,
                                   const Bytef *source, uLong sourceLen));
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 OF((Bytef *dest,   uLongf *destLen,
                                    const Bytef *source, uLong *sourceLen));
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 OF((const char *path, const char *mode));
ZEXTERN gzFile ZEXPORT gzopen(const char *path, const char *mode);

     Open the gzip (.gz) file at path for reading and decompressing, or
     Opens a gzip (.gz) file for reading or 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
   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.
   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.
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
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







-
+

-
-
-
+
+
+


















-
+

-
-
-
-
-
-
-
+
+
+
+
+
+
+







-
+

-
-
-
+
+
+






-
+

-
+







     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 OF((int fd, const char *mode));
ZEXTERN gzFile ZEXPORT gzdopen(int fd, const char *mode);
/*
     gzdopen associates 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.
     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 OF((gzFile file, unsigned size));
ZEXTERN int ZEXPORT gzbuffer(gzFile file, unsigned size);
/*
     Set the internal buffer size used by this library's functions.  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).
     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 OF((gzFile file, int level, int strategy));
ZEXTERN int ZEXPORT gzsetparams(gzFile file, int level, int strategy);
/*
     Dynamically update the compression level or strategy.  See the description
   of deflateInit2 for the meaning of these parameters.  Previously provided
   data is flushed before the parameter change.
     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 OF((gzFile file, voidp buf, unsigned len));
ZEXTERN int ZEXPORT gzread(gzFile file, voidp buf, unsigned len);
/*
     Reads the given number of uncompressed bytes from the compressed file.  If
     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,
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
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







-
-
+
+

-
-
-
-
-
+
+
+
+
+










-
+




-
+


-
+
-

-
-
+
+
-


-
-
+
+

-
+










-
+

-
-
+
+






-
+




-
+

-
+





-
+

-
-
-
-
-
+
+
+
+
+
+






-
+

-
+



-
+

-
+






-
+

-
-
+
+








-
+

-
-
-
+
+
+











-
-
+
+

-
-
+
+














-
+

-
+

-
+



-
+

-
-
-
-
+
+
+
+





-
+

-
-
-
-
-
+
+
+
+
+


-
+

-
-
-
-
-
-
-
+
+
+
+
+
+
+






-
+

-
+


















-
+

-
-
+
+









-
-
+
+










-
+

-
-
-
-
+
+
+
+










-
+

-
+














-
+


+
-
-
+
+














-
-
+
+





-
-
+
+









-
+


+
-
-
-
+
+
+











-
-
+
+





-
+





+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+








-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+








     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 OF((voidp buf, z_size_t size, z_size_t nitems,
                                     gzFile file));
ZEXTERN z_size_t ZEXPORT gzfread(voidp buf, z_size_t size, z_size_t nitems,
                                 gzFile file);
/*
     Read up to nitems items of size size from file to 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.
     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 nevetheless read into buf
   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, reseting and retrying on end-of-file, when size is not 1.
   file, resetting and retrying on end-of-file, when size is not 1.
*/

ZEXTERN int ZEXPORT gzwrite OF((gzFile file,
ZEXTERN int ZEXPORT gzwrite(gzFile file, voidpc buf, unsigned len);
                                voidpc buf, unsigned len));
/*
     Writes the given number of uncompressed bytes into the compressed file.
   gzwrite returns the number of uncompressed bytes written or 0 in case of
     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.
   error.
*/

ZEXTERN z_size_t ZEXPORT gzfwrite OF((voidpc buf, z_size_t size,
                                      z_size_t nitems, gzFile file));
ZEXTERN z_size_t ZEXPORT gzfwrite(voidpc buf, z_size_t size,
                                  z_size_t nitems, gzFile file);
/*
     gzfwrite() writes nitems items of size size from buf to file, duplicating
     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 Z_ARG((gzFile file, const char *format, ...));
ZEXTERN int ZEXPORTVA gzprintf(gzFile file, const char *format, ...);
/*
     Converts, formats, and writes the arguments to the compressed file under
   control of the format string, as in fprintf.  gzprintf returns the number of
     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()
   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 OF((gzFile file, const char *s));
ZEXTERN int ZEXPORT gzputs(gzFile file, const char *s);
/*
     Writes the given null-terminated string to the compressed file, excluding
     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 OF((gzFile file, char *buf, int len));
ZEXTERN char * ZEXPORT gzgets(gzFile file, char *buf, int len);
/*
     Reads bytes from the compressed file until len-1 characters are read, or 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 == 1, the
   string is terminated with a null character.  If no characters are read due
   to an end-of-file or len < 1, then the buffer is left untouched.
     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 OF((gzFile file, int c));
ZEXTERN int ZEXPORT gzputc(gzFile file, int c);
/*
     Writes c, converted to an unsigned char, into the compressed file.  gzputc
     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 OF((gzFile file));
ZEXTERN int ZEXPORT gzgetc(gzFile file);
/*
     Reads one byte from the compressed file.  gzgetc returns this byte or -1
     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 OF((int c, gzFile file));
ZEXTERN int ZEXPORT gzungetc(int c, gzFile file);
/*
     Push one character back onto the stream to be read as the first character
   on the next read.  At least one character of push-back is allowed.
     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 OF((gzFile file, int flush));
ZEXTERN int ZEXPORT gzflush(gzFile file, int flush);
/*
     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).  gzflush is only permitted when writing.
     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 OF((gzFile file,
                                   z_off_t offset, int whence));
ZEXTERN z_off_t ZEXPORT gzseek(gzFile file,
                               z_off_t offset, int whence);

     Sets the starting position for the next gzread or gzwrite on the given
   compressed file.  The offset represents a number of bytes in the
     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 OF((gzFile file));
ZEXTERN int ZEXPORT    gzrewind(gzFile file);
/*
     Rewinds the given file. This function is supported only for reading.
     Rewind file. This function is supported only for reading.

     gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET)
     gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET).
*/

/*
ZEXTERN z_off_t ZEXPORT    gztell OF((gzFile file));
ZEXTERN z_off_t ZEXPORT    gztell(gzFile file);

     Returns the starting position for the next gzread or gzwrite on the given
   compressed 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().
     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 OF((gzFile file));
ZEXTERN z_off_t ZEXPORT gzoffset(gzFile file);

     Returns the current offset in the file being read or written.  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.
     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 OF((gzFile file));
ZEXTERN int ZEXPORT gzeof(gzFile file);
/*
     Returns true (1) if the end-of-file indicator 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.
     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 OF((gzFile file));
ZEXTERN int ZEXPORT gzdirect(gzFile file);
/*
     Returns true (1) if file is being copied directly while reading, or false
     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 OF((gzFile file));
ZEXTERN int ZEXPORT    gzclose(gzFile file);
/*
     Flushes all pending output if necessary, closes the compressed file and
   deallocates the (de)compression state.  Note that once file is closed, you
     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 OF((gzFile file));
ZEXTERN int ZEXPORT gzclose_w OF((gzFile file));
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 OF((gzFile file, int *errnum));
ZEXTERN const char * ZEXPORT gzerror(gzFile file, int *errnum);
/*
     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.
     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 OF((gzFile file));
ZEXTERN void ZEXPORT gzclearerr(gzFile file);
/*
     Clears the error and end-of-file flags for file.  This is analogous to the
     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 OF((uLong adler, const Bytef *buf, uInt len));
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
   return the updated checksum.  If buf is Z_NULL, this function returns the
   required initial value for the checksum.
   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 OF((uLong adler, const Bytef *buf,
                                    z_size_t len));
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 OF((uLong adler1, uLong adler2,
                                          z_off_t len2));
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   OF((uLong crc, const Bytef *buf, uInt len));
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.
   updated CRC-32.  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.
   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 OF((uLong adler, const Bytef *buf,
                                  z_size_t len));
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 OF((uLong crc1, uLong crc2, z_off_t len2));
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.
*/
   len2.

/*
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_ OF((z_streamp strm, int level,
                                     const char *version, int stream_size));
ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm,
                                     const char *version, int stream_size));
ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int  level, int  method,
                                      int windowBits, int memLevel,
                                      int strategy, const char *version,
                                      int stream_size));
ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int  windowBits,
                                      const char *version, int stream_size));
ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits,
                                         unsigned char FAR *window,
                                         const char *version,
                                         int stream_size));
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),\
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
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







-
+
















-
-
-
-
-
-
+
+
+
+
+
+
+










+







+


-
-
-
-
-
-
+
+
+
+
+
+
+


-
-
-
-
-
-
+
+
+
+
+
+
+




-
-
+
+
+




-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+



-
-
-
+
+
+








 * 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_ OF((gzFile file));  /* backward compatibility */
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 OF((const char *, const char *));
   ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int));
   ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile));
   ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile));
   ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off64_t));
   ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off64_t));
   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 OF((const char *, const char *));
     ZEXTERN z_off_t ZEXPORT gzseek64 OF((gzFile, z_off_t, int));
     ZEXTERN z_off_t ZEXPORT gztell64 OF((gzFile));
     ZEXTERN z_off_t ZEXPORT gzoffset64 OF((gzFile));
     ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t));
     ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t));
     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 OF((const char *, const char *));
   ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile, z_off_t, int));
   ZEXTERN z_off_t ZEXPORT gztell OF((gzFile));
   ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile));
   ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t));
   ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t));
   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 OF((uLong, uLong, z_off_t));
   ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t));
   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           OF((int));
ZEXTERN int            ZEXPORT inflateSyncPoint OF((z_streamp));
ZEXTERN const z_crc_t FAR * ZEXPORT get_crc_table    OF((void));
ZEXTERN int            ZEXPORT inflateUndermine OF((z_streamp, int));
ZEXTERN int            ZEXPORT inflateValidate OF((z_streamp, int));
ZEXTERN unsigned long  ZEXPORT inflateCodesUsed OF ((z_streamp));
ZEXTERN int            ZEXPORT inflateResetKeep OF((z_streamp));
ZEXTERN int            ZEXPORT deflateResetKeep OF((z_streamp));
#if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(Z_SOLO)
ZEXTERN gzFile         ZEXPORT gzopen_w OF((const wchar_t *path,
                                            const char *mode));
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 Z_ARG((gzFile file,
                                                  const char *format,
                                                  va_list va));
ZEXTERN int            ZEXPORTVA gzvprintf(gzFile file,
                                           const char *format,
                                           va_list va);
#  endif
#endif

#ifdef __cplusplus
}
#endif

#endif /* ZLIB_H */

Changes to compat/zlib/zlib.map.

88
89
90
91
92
93
94






88
89
90
91
92
93
94
95
96
97
98
99
100







+
+
+
+
+
+
    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;

Deleted compat/zlib/zlib2ansi.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
























































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
#!/usr/bin/perl

# Transform K&R C function definitions into ANSI equivalent.
#
# Author: Paul Marquess
# Version: 1.0
# Date: 3 October 2006

# TODO
#
# Asumes no function pointer parameters. unless they are typedefed.
# Assumes no literal strings that look like function definitions
# Assumes functions start at the beginning of a line

use strict;
use warnings;

local $/;
$_ = <>;

my $sp = qr{ \s* (?: /\* .*? \*/ )? \s* }x; # assume no nested comments

my $d1    = qr{ $sp (?: [\w\*\s]+ $sp)* $sp \w+ $sp [\[\]\s]* $sp }x ;
my $decl  = qr{ $sp (?: \w+ $sp )+ $d1 }xo ;
my $dList = qr{ $sp $decl (?: $sp , $d1 )* $sp ; $sp }xo ;


while (s/^
            (                  # Start $1
                (              #   Start $2
                    .*?        #     Minimal eat content
                    ( ^ \w [\w\s\*]+ )    #     $3 -- function name
                    \s*        #     optional whitespace
                )              # $2 - Matched up to before parameter list

                \( \s*         # Literal "(" + optional whitespace
                ( [^\)]+ )     # $4 - one or more anythings except ")"
                \s* \)         # optional whitespace surrounding a Literal ")"

                ( (?: $dList )+ ) # $5

                $sp ^ {        # literal "{" at start of line
            )                  # Remember to $1
        //xsom
      )
{
    my $all = $1 ;
    my $prefix = $2;
    my $param_list = $4 ;
    my $params = $5;

    StripComments($params);
    StripComments($param_list);
    $param_list =~ s/^\s+//;
    $param_list =~ s/\s+$//;

    my $i = 0 ;
    my %pList = map { $_ => $i++ }
                split /\s*,\s*/, $param_list;
    my $pMatch = '(\b' . join('|', keys %pList) . '\b)\W*$' ;

    my @params = split /\s*;\s*/, $params;
    my @outParams = ();
    foreach my $p (@params)
    {
        if ($p =~ /,/)
        {
            my @bits = split /\s*,\s*/, $p;
            my $first = shift @bits;
            $first =~ s/^\s*//;
            push @outParams, $first;
            $first =~ /^(\w+\s*)/;
            my $type = $1 ;
            push @outParams, map { $type . $_ } @bits;
        }
        else
        {
            $p =~ s/^\s+//;
            push @outParams, $p;
        }
    }


    my %tmp = map { /$pMatch/;  $_ => $pList{$1}  }
              @outParams ;

    @outParams = map  { "    $_" }
                 sort { $tmp{$a} <=> $tmp{$b} }
                 @outParams ;

    print $prefix ;
    print "(\n" . join(",\n", @outParams) . ")\n";
    print "{" ;

}

# Output any trailing code.
print ;
exit 0;


sub StripComments
{

  no warnings;

  # Strip C & C++ coments
  # From the perlfaq
  $_[0] =~

    s{
       /\*         ##  Start of /* ... */ comment
       [^*]*\*+    ##  Non-* followed by 1-or-more *'s
       (
         [^/*][^*]*\*+
       )*          ##  0-or-more things which don't start with /
                   ##    but do end with '*'
       /           ##  End of /* ... */ comment

     |         ##     OR  C++ Comment
       //          ## Start of C++ comment //
       [^\n]*      ## followed by 0-or-more non end of line characters

     |         ##     OR  various things which aren't comments:

       (
         "           ##  Start of " ... " string
         (
           \\.           ##  Escaped char
         |               ##    OR
           [^"\\]        ##  Non "\
         )*
         "           ##  End of " ... " string

       |         ##     OR

         '           ##  Start of ' ... ' string
         (
           \\.           ##  Escaped char
         |               ##    OR
           [^'\\]        ##  Non '\
         )*
         '           ##  End of ' ... ' string

       |         ##     OR

         .           ##  Anything other char
         [^/"'\\]*   ##  Chars which doesn't start a comment, string or escape
       )
     }{$2}gxs;

}

Changes to compat/zlib/zutil.c.

20
21
22
23
24
25
26
27

28
29
30
31
32

33
34
35
36
37
38
39
40
20
21
22
23
24
25
26

27

28
29
30

31

32
33
34
35
36
37
38







-
+
-



-
+
-







    (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()
const char * ZEXPORT zlibVersion(void) {
{
    return ZLIB_VERSION;
}

uLong ZEXPORT zlibCompileFlags()
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;
57
58
59
60
61
62
63

64
65
66

67
68
69
70
71
72
73
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73







+



+







    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
115
116
117
118
119
120
121
122

123
124
125
126
127
128
129
130
131
132
133

134
135
136
137
138
139
140


141
142
143
144
145
146
147
148
149

150
151
152
153
154
155
156
157
158
159
160

161
162
163
164
165
166
167
168
169
170
171
172
173

174
175
176
177
178
179
180
181
182
183
115
116
117
118
119
120
121

122


123
124
125
126
127
128
129
130

131


132
133
134


135
136
137
138
139
140
141
142
143
144

145




146
147
148
149
150
151

152




153
154
155
156
157
158
159
160

161



162
163
164
165
166
167
168







-
+
-
-








-
+
-
-



-
-
+
+








-
+
-
-
-
-






-
+
-
-
-
-








-
+
-
-
-







#ifdef ZLIB_DEBUG
#include <stdlib.h>
#  ifndef verbose
#    define verbose 0
#  endif
int ZLIB_INTERNAL z_verbose = verbose;

void ZLIB_INTERNAL z_error (m)
void ZLIB_INTERNAL z_error(char *m) {
    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(err)
const char * ZEXPORT zError(int err) {
    int err;
{
    return ERR_MSG(err);
}

#if defined(_WIN32_WCE)
    /* The Microsoft C Run-Time Library for Windows CE doesn't have
#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(dest, source, len)
void ZLIB_INTERNAL zmemcpy(Bytef* dest, const Bytef* source, uInt len) {
    Bytef* dest;
    const Bytef* source;
    uInt  len;
{
    if (len == 0) return;
    do {
        *dest++ = *source++; /* ??? to be unrolled */
    } while (--len != 0);
}

int ZLIB_INTERNAL zmemcmp(s1, s2, len)
int ZLIB_INTERNAL zmemcmp(const Bytef* s1, const Bytef* s2, uInt len) {
    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(dest, len)
void ZLIB_INTERNAL zmemzero(Bytef* dest, uInt len) {
    Bytef* dest;
    uInt  len;
{
    if (len == 0) return;
    do {
        *dest++ = 0;  /* ??? to be unrolled */
    } while (--len != 0);
}
#endif

210
211
212
213
214
215
216
217

218
219
220
221
222
223
224
225
195
196
197
198
199
200
201

202

203
204
205
206
207
208
209







-
+
-







/* 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 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.
236
237
238
239
240
241
242
243

244
245
246
247
248
249
250
251
220
221
222
223
224
225
226

227

228
229
230
231
232
233
234







-
+
-







    /* 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)
void ZLIB_INTERNAL zcfree(voidpf opaque, voidpf ptr) {
{
    int n;

    (void)opaque;

    if (*(ush*)&ptr != 0) { /* object < 64K */
        farfree(ptr);
        return;
273
274
275
276
277
278
279
280

281
282
283
284
285
286

287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302



303
304
305

306
307
308
309
310
311
312
313
314
315

316
317
318
319
320
321
322
323
324
325
256
257
258
259
260
261
262

263

264
265
266
267

268

269
270
271
272
273
274
275
276
277
278
279
280



281
282
283
284
285

286




287
288
289
290
291

292



293
294
295
296
297
298
299







-
+
-




-
+
-












-
-
-
+
+
+


-
+
-
-
-
-





-
+
-
-
-







#  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)
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 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 OF((uInt size));
extern voidp  calloc OF((uInt items, uInt size));
extern void   free   OF((voidpf ptr));
extern voidp malloc(uInt size);
extern voidp calloc(uInt items, uInt size);
extern void free(voidpf ptr);
#endif

voidpf ZLIB_INTERNAL zcalloc (opaque, items, size)
voidpf ZLIB_INTERNAL zcalloc(voidpf opaque, unsigned items, unsigned size) {
    voidpf opaque;
    unsigned items;
    unsigned size;
{
    (void)opaque;
    return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) :
                              (voidpf)calloc(items, size);
}

void ZLIB_INTERNAL zcfree (opaque, ptr)
void ZLIB_INTERNAL zcfree(voidpf opaque, voidpf ptr) {
    voidpf opaque;
    voidpf ptr;
{
    (void)opaque;
    free(ptr);
}

#endif /* MY_ZCALLOC */

#endif /* !Z_SOLO */

Changes to compat/zlib/zutil.h.

1
2

3
4
5
6
7
8
9
1

2
3
4
5
6
7
8
9

-
+







/* zutil.h -- internal interface and configuration of the compression library
 * Copyright (C) 1995-2016 Jean-loup Gailly, Mark Adler
 * 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.
 */
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47











48
49
50
51
52

53
54
55
56
57
58
59
25
26
27
28
29
30
31




32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

59
60
61
62
63
64
65
66







-
-
-
-












+
+
+
+
+
+
+
+
+
+
+




-
+







#  if !(defined(_WIN32_WCE) && defined(_MSC_VER))
#    include <stddef.h>
#  endif
#  include <string.h>
#  include <stdlib.h>
#endif

#ifdef Z_SOLO
   typedef long ptrdiff_t;  /* guess -- will be caught if guess is wrong */
#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[Z_NEED_DICT-(err)]
#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 */

126
127
128
129
130
131
132
133

134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
133
134
135
136
137
138
139

140
141









142
143
144
145
146
147
148







-
+

-
-
-
-
-
-
-
-
-







#ifdef OS2
#  define OS_CODE  6
#  if defined(M_I86) && !defined(Z_SOLO)
#    include <malloc.h>
#  endif
#endif

#if defined(MACOS) || defined(TARGET_OS_MAC)
#if defined(MACOS)
#  define OS_CODE  7
#  ifndef Z_SOLO
#    if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os
#      include <unix.h> /* for fdopen */
#    else
#      ifndef fdopen
#        define fdopen(fd,mode) NULL /* No fdopen() */
#      endif
#    endif
#  endif
#endif

#ifdef __acorn
#  define OS_CODE 13
#endif

#if defined(WIN32) && !defined(__CYGWIN__)
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192



193
194
195
196
197
198
199
157
158
159
160
161
162
163
















164
165
166
167
168
169
170
171
172


173
174
175
176
177
178
179
180
181
182







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-









-
-
+
+
+







#  define OS_CODE 18
#endif

#ifdef __APPLE__
#  define OS_CODE 19
#endif

#if defined(_BEOS_) || defined(RISCOS)
#  define fdopen(fd,mode) NULL /* No fdopen() */
#endif

#if (defined(_MSC_VER) && (_MSC_VER > 600)) && !defined __INTERIX
#  if defined(_WIN32_WCE)
#    define fdopen(fd,mode) NULL /* No fdopen() */
#    ifndef _PTRDIFF_T_DEFINED
       typedef int ptrdiff_t;
#      define _PTRDIFF_T_DEFINED
#    endif
#  else
#    define fdopen(fd,type)  _fdopen(fd,type)
#  endif
#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 OF((uLong, uLong, z_off_t));
    ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t));
    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
224
225
226
227
228
229
230
231
232
233



234
235
236
237
238
239
240

241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259



260
261
262
263
264
265
266
267
268
269
270
271
207
208
209
210
211
212
213



214
215
216
217
218
219
220
221
222

223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239



240
241
242
243
244
245
246
247
248
249
250
251
252
253
254







-
-
-
+
+
+






-
+
















-
-
-
+
+
+












#    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 OF((Bytef* dest, const Bytef* source, uInt len));
   int ZLIB_INTERNAL zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len));
   void ZLIB_INTERNAL zmemzero OF((Bytef* dest, uInt len));
   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 OF((char *m));
   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 OF((voidpf opaque, unsigned items,
                                    unsigned size));
   void ZLIB_INTERNAL zcfree  OF((voidpf opaque, voidpf ptr));
   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 */

Changes to configure.

1
2
3

1
2

3


-
+
#!/bin/sh
dir="`dirname "$0"`/autosetup"
WRAPPER="$0"; export WRAPPER; exec "`$dir/find-tclsh`" "$dir/autosetup" "$@"
WRAPPER="$0"; export WRAPPER; exec "`"$dir/autosetup-find-tclsh"`" "$dir/autosetup" "$@"

Changes to debian/makedeb.sh.

55
56
57
58
59
60
61
62

63
64
65
66
67
68
69
55
56
57
58
59
60
61

62
63
64
65
66
67
68
69







-
+







    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://www.fossil-scm.org/
http://fossil-scm.org/

fossil is released under the terms of the 2-clause BSD License.

EOF
}

true && {

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 &nbsp; as that is an HTML-ism and is not valid in XML.
**
**   *  The "&" character is changed into "&amp;" 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, "&lt;", 4);  break;  }
      case '>': {  pik_append(p, "&gt;", 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, "&amp;", 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, "&#92;", -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
** "&lt;" 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,  "&rarr;"           },   /* Same as -> */
         { 12, T_RARROW,  "&rightarrow;"     },   /* Same as -> */
         { 6,  T_LARROW,  "&larr;"           },   /* Same as <- */
         { 11, T_LARROW,  "&leftarrow;"      },   /* Same as <- */
         { 16, T_LRARROW, "&leftrightarrow;" },   /* 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("&lt;");
    }else if( c=='>' ){
      printf("&gt;");
    }else if( c=='&' ){
      printf("&amp;");
    }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

Changes to 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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

19
20

21
22

23
24

25
26

27
28

29
30

31
32

33
34





35
36
37
38
39
40
41
-
+

















-
+

-
+

-
+

-
+

-
+

-
+

-
+

-
+

-
-
-
-
-







.TH FOSSIL "1" "September 2018" "http://fossil-scm.org" "User Commands"
.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            clean          help           push           timeline
add          cat          diff         ls           revert       timeline
.br
addremove      clone          import         rebuild        ui
addremove    changes      extras       merge        rm           ui
.br
all            commit         info           remote-url     undo
all          chat         finfo        mv           settings     undo
.br
amend          delete         init           revert         unpublished
amend        clean        gdiff        open         sql          unversioned
.br
annotate       diff           ls             rm             unversioned
annotate     clone        grep         pull         stash        update
.br
bisect         export         merge          settings       update
bisect       commit       help         push         status       version
.br
blame          extras         mv             sql            version
blame        dbstat       info         rebuild      sync
.br
branch         finfo          open           stash
branch       delete       init         remote       tag
.br
bundle         fusefs         praise         status
.br
cat            gdiff          publish        sync
.br
changes        grep           pull           tag

.SH FEATURES

Features as described on the fossil home page.

.HP
1.
97
98
99
100
101
102
103
104

105
106
107
92
93
94
95
96
97
98

99
100
101
102







-
+




.HP
8.
.B Free and Open-Source
- Uses the 2-clause BSD license.

.SH DOCUMENTATION
http://www.fossil-scm.org/
https://fossil-scm.org/
.br
.B fossil
\fIui\fR

Changes to setup/fossil.iss.

17
18
19
20
21
22
23
24
25
26



27
28
29
30

31
32
33
34
35
36
37
17
18
19
20
21
22
23



24
25
26
27
28
29

30
31
32
33
34
35
36
37







-
-
-
+
+
+



-
+







[Setup]
ArchitecturesAllowed=x86 x64
AlwaysShowComponentsList=false
AppCopyright=Copyright (c) D. Richard Hipp.  All rights reserved.
AppID={{f1c25a1f-3954-4e1a-ac36-4314c52f057c}
AppName=Fossil
AppPublisher=Fossil Development Team
AppPublisherURL=https://www.fossil-scm.org/
AppSupportURL=https://www.fossil-scm.org/
AppUpdatesURL=https://www.fossil-scm.org/
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://www.fossil-scm.org/index.html/doc/tip/www/quickstart.wiki
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}

Changes to 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


1
2
3
4


5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22


23
24
25

26
27
28
29
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 four files in each subdirectory for the CSS, the "details"
file, the footer, and the header for that skin.
There are five key files in each subdirectory:

  * `css.txt`	&rarr; The CSS for the skin
  * `details.txt` &rarr; Skin-specific settings
  * `footer.txt` &rarr; Text of the Content Footer for each page
  * `header.txt` &rarr; Text of the Content Header for each page
  * `js.txt` &rarr; 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 and skins/newskin/header.txt.
        Be sure to "fossil add" these files.
        skins/newskin/footer.txt, skins/newskin/header.txt, and
        skins/newskin/js.txt. Be sure to "fossil add" these files.

   3.   Go to the src/ directory and rerun "tclsh makemake.tcl".  This
   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, and header.txt) into a working directory $WORKDIR
then launch Fossil with a command-line option "--skin $WORKDIR".  Example:
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/css.txt,
So after launching fossil as shown above, you can edit the newskin/*.txt
newskin/details.txt, newskin/footer.txt, and newskin/header.txt files using
your favorite text editor, then press Reload on your browser to see
immediate results.
files using your favorite text editor, then press Reload on your browser
to see immediate results.

Deleted skins/aht/details.txt.

1
2
3
4




-
-
-
-
timeline-arrowheads:        1
timeline-circle-nodes:      0
timeline-color-graph-lines: 0
white-foreground:           0

Changes to skins/ardoise/css.txt.

1
2
3
4
5
6
7
8
9
10
11
12
13

14
15
16
17
18
19
20
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21













+







@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
}
46
47
48
49
50
51
52
53

54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
47
48
49
50
51
52
53

54
55
56
57
58
59
60
61
62


63
64
65
66
67
68
69







-
+








-
-







.filetree ul ul,
.mainmenu ul,
sub,
sup {
  position: relative
}
.filetree .dir > div.filetreeline > a,
ul.browser li.dir {
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;
  -ms-text-size-adjust: 100%;
  -webkit-text-size-adjust: 100%
}
audio,
canvas,
progress,
video {
  display: inline-block;
  vertical-align: baseline
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
81
82
83
84
85
86
87




88
89
90
91
92
93
94







-
-
-
-







a {
  background-color: transparent;
  color: #ff8000;
  text-decoration: unset
}
a:active,
a:hover,
pre.udiff:focus,
table.sbsdiffcols:focus {
  outline: 0
}
abbr[title] {
  border-bottom: 1px dotted
}
b,
optgroup,
strong,
td.usetupEditLabel {
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
155
156
157
158
159
160
161

162
163
164
165
166
167
168







-







select {
  text-transform: none
}
button,
html input[type=button],
input[type=reset],
input[type=submit] {
  -webkit-appearance: button;
  cursor: pointer
}
button[disabled],
html input[disabled] {
  cursor: default
}
button::-moz-focus-inner,
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
179
180
181
182
183
184
185



186



187
188
189
190
191
192
193







-
-
-

-
-
-







  padding: 0;
  display: inline
}
input[type=number]::-webkit-inner-spin-button,
input[type=number]::-webkit-outer-spin-button {
  height: auto
}
input[type=search] {
  -webkit-appearance: textfield
}
input[type=search]::-webkit-search-cancel-button,
input[type=search]::-webkit-search-decoration {
  -webkit-appearance: none
}
fieldset {
  border: 1px solid silver;
  margin: 0 2px
}
legend {
  padding: 0
}
289
290
291
292
293
294
295
296

297
298
299
300
301
302
303
277
278
279
280
281
282
283

284
285
286
287
288
289
290
291







-
+







  max-width: 1200px;
  margin: 0 auto;
  box-sizing: border-box
}
.column,
.columns {
  width: 100%;
  float: left;
  /*float: left; can break README.md in /dir view*/
  box-sizing: border-box
}
@media (min-width:400px) {
  .container {
    width: 95%;
    padding: 0
  }
317
318
319
320
321
322
323





324
325
326
327
328
329
330
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323







+
+
+
+
+







  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%
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
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







-
-
-


















+
+
+















-
-
-







input[type=password],
input[type=search],
input[type=tel],
input[type=text],
input[type=url] {
  box-shadow: none;
  box-sizing: border-box;
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none
}
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;
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  min-height: 65px;
  padding-top: 6px;
  padding-bottom: 6px
}
input[type=email]:focus,
input[type=number]:focus,
input[type=password]:focus,
582
583
584
585
586
587
588









589
590
591
592
593
594
595
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594







+
+
+
+
+
+
+
+
+







  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;
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
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







-
+












-
-
+
+


-
+



-
+


-
+




-
+


-
-
-
+
+
+













+



+















-
+




-
+




-
+







+



-





-










-












+
+
+
+
+
+
+
+







ol,
p,
pre,
table,
ul {
  margin-bottom: 1.5rem
}
.header {
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 {
header .logo,
header .logo h1 {
  display: inline-block
}
.header .login {
header .login {
  padding-top: 2px;
  text-align: right
}
.header .login .button {
header .login .button {
  margin: 0
}
.header h1 {
header h1 {
  margin: 0;
  color: #888;
  display: inline-block
}
.header .title h1 {
header .title h1 {
  padding-bottom: 10px
}
.header .login,
.header h1 small,
.header h2 small {
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 {
footer {
  padding: 10px 0 60px;
  border-top: 0;
  color: #888
}
.footer a {
footer a {
  color: #527b8f;
  background-repeat: no-repeat;
  background-position: center top 10px
}
.footer a:hover {
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;
  display: block;
  border-top: 1px solid transparent;
  padding: 0
}
.mainmenu li {
  outline: 0;
  display: block;
  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;
  display: block;
  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
}
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
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







-
+













+
+
+
+
+
+







  list-style: none;
  line-height: 1.6
}
ul.browser li.dir {
  background-repeat: no-repeat
}
.filetree a,
ul.browser li.file {
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 {
945
946
947
948
949
950
951






























952
953
954
955
956
957
958
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







.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;
987
988
989
990
991
992
993








994
995
996
997
998
999
1000
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051







+
+
+
+
+
+
+
+







  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;
1028
1029
1030
1031
1032
1033
1034























1035
1036
1037
1038
1039
1040
1041
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







}
.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"],
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
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







-
-
+


-


-
+

-
-
+
-
-
+
+

-
-
+
+

-
+

-

-
-
-
+
+
+
+

-
-
-
+
+
+
+

-
+
+
+
+
+

-
+
+

-
+
-
-
+
+

-
+
-
-
-
+
+
+

-
-
+
+
+

+
+
+
+







.tl-line.warp {
  background: #600000
}
table.login_out .login_out_label {
  font-weight: 700;
  text-align: right
}
pre.udiff,
table.sbsdiffcols {
table.diff {
  width: 100%;
  overflow: auto;
  padding: 0 5px;
  font-size: 1rem;
  background: #000;
  border-radius: 5px
  border-radius: 5px;
}
pre.udiff,
pre.udiff pre,
table.diff pre {
table.sbsdiffcols pre {
  font-size: 1.15rem
  font-size: 1.15rem;
  scrollbar-color: black #999;
}
pre.udiff {
  padding: 10px 0
table.udiff pre {
  padding: 0 0
}
div.difftxtcol {
td.difftxt {
  width: 52rem;
  overflow-x: auto
}
span.diffchng {
  background-color: #8080e8;
  color: #000
td.diffln ins {
  background-color: #559855;
  color: #000;
  text-decoration: none;
}
span.diffadd {
  background-color: #559855;
  color: #000
td.diffln del {
  background-color: #c55;
  color: #000;
  text-decoration: none;
}
span.diffrm {
td.difftxt del {
  background-color: inherit;
  text-decoration: none;
}
td.difftxt del > del {
  background-color: #c55;
  color: #000
  color: #000;
  text-decoration: none;
}
div.diffmkrcol {
td.difftxt ins {
  padding: 0 1em;
  background: #111
  background-color: inherit;
  text-decoration: none;
}
span.diffhr {
td.difftxt ins > ins {
  display: inline-block;
  margin: .5em 0 1em;
  color: #555
  background-color: #559855;
  color: #000;
  text-decoration: none;
}
span.diffln {
  color: #666

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 {
1311
1312
1313
1314
1315
1316
1317

1318





1319
1320
1321
1322
1323
1324
1325
1393
1394
1395
1396
1397
1398
1399
1400

1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412







+
-
+
+
+
+
+







.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
  background-color: #ff8000;
}
.statistics-report-graph-extra {
  border: 2px dashed #446979;
  border-left-style: none;
}
mark,
p.noMoreShun,
p.shunned,
span.modpending {
  color: #ff8000
}
1333
1334
1335
1336
1337
1338
1339
























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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
.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;
}

Changes to skins/ardoise/details.txt.

1
2
3
4

1
2
3
4
5




+
timeline-arrowheads:        0
timeline-circle-nodes:      1
timeline-color-graph-lines: 1
white-foreground:           1
pikchr-background:          0x1d2021

Changes to skins/ardoise/footer.txt.

1
2
3
4
5





6
7
8

9
10
11

12
13
14
15






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>
    <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 -->
<div class="footer">
<footer>
  <div class="container">
    <div class="pull-right">
      <a href="https://www.fossil-scm.org/">Fossil $release_version $manifest_version $manifest_date</a>
      <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>
</div>
</footer>

Changes to skins/ardoise/header.txt.

1

2
3
4
5
6
7
8

1
2
3
4
5
6
7
8
-
+







<div class="header">
<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"
16
17
18
19
20
21
22
23

24
25

26
27
28
29
30
31







32
33
34
35





36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75













16
17
18
19
20
21
22

23
24
25
26






27
28
29
30
31
32
33




34
35
36
37
38








































39
40
41
42
43
44
45
46
47
48
49
50
51







-
+


+
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
        html "<a class='rss' href='$home/timeline.rss'></a>"
      }
      </th1>
      <small> &nbsp;$<title></small></h1>
    </div>

    <!-- Main Menu -->
    <div class="mainmenu">
    <nav class="mainmenu" title="Main Menu">
      <ul>
        <th1>
          html "<li><a id='hbbtn' href='$home/sitemap' aria-label='Site Map'>&#9776;</a></li>\n"
proc menulink {url name} {
  upvar current_page current
  upvar home home
  if {[string range $url 0 [string length $current]] eq "/$current"} {
    html "<li class='active'>"
  } else {
          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>"
  }
  html "<a href='$home$url'>$name</a></li>\n"
}
            }
            html "<li class='$class'>"
            if {[string match /* $url]} {set url $home$url}
            html "<a href='$url'>$name</a></li>\n"
          }
menulink $index_page Home
if {[anycap jor]} {
  menulink /timeline Timeline
}
if {[hascap oh]} {
  menulink /dir?ci=tip Files
}
if {[hascap o]} {
  menulink  /brlist Branches
  menulink  /taglist Tags
}
if {[anycap 23456] || [anoncap 2] || [anoncap 3]} {
  menulink /forum Forum
}
if {[hascap r]} {
  menulink /ticket Tickets
}
if {[hascap j]} {
  menulink /wiki Wiki
}
if {[hascap o]} {
  menulink /help Help
  }
if {[hascap s]} {
  menulink /setup Admin
} elseif {[hascap a]} {
  menulink /setup_ulist Users
}
            </th1>
          </ul>
        </div> <!-- end div mainmenu -->
      </div> <!-- end div container -->
    </div> <!-- end div header -->
    <div class="middle max-full-width">
      <div class="container">
        <th1>
          if {[string first artifact $current_page] == 0 || [string first hexdump $current_page] == 0} {
            html "<div class=\"artifact_content\">"
          }
        </th1>
        </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>

Changes to skins/black_and_white/css.txt.

1
2
3
4
5
6
7
8

9
10
11
12
13
14
15
16
17
1
2
3
4
5
6
7

8


9
10
11
12
13
14
15







-
+
-
-







/* General settings for the entire page */
body {
    margin:0px 0px 0px 0px;
    padding:0px;
    font-family:verdana, arial, helvetica, "sans serif";
    color:#333;
    background-color:white;
    -moz-text-size-adjust: none;
    text-size-adjust: none;
    -webkit-text-size-adjust: none;
    -mx-text-size-adjust: none;
}

/* consistent colours */
h2 {
  color: #333;
}
h3 {
49
50
51
52
53
54
55
56

57
58
59
60
61
62
63
64
65
66
67

68
69
70

71
72
73
74
75
76

77
78
79
80

81
82
83
84

85
86
87
88
89

90
91
92
93
94
95
96
47
48
49
50
51
52
53

54
55
56
57
58
59
60
61
62
63
64

65
66
67

68
69
70
71
72
73

74
75
76
77

78
79
80
81

82
83
84
85
86

87
88
89
90
91
92
93
94







-
+










-
+


-
+





-
+



-
+



-
+




-
+







  color: #333;
  font-size: 0.8em;
  font-weight: bold;
  white-space: nowrap;
}

/* The header across the top of the page */
div.header {
header {
    margin:10px 0px 10px 0px;
    padding:1px 0px 0px 20px;
    border-style:solid;
    border-color:black;
    border-width:1px 0px;
    background-color:#eee;
}

/* The main menu bar that appears at the top left of the page beneath
** the header. Width must be co-ordinated with the container below */
div.mainmenu {
nav.mainmenu {
  float: left;
  margin-left: 10px;
  margin-right: 10px;
  margin-right: 20px;
  font-size: 0.9em;
  font-weight: bold;
  padding:5px;
  background-color:#eee;
  border:1px solid #999;
  width:8em;
  width:6em;
}

/* Main menu is now a list */
div.mainmenu ul {
nav.mainmenu ul {
  padding: 0;
  list-style:none;
}
div.mainmenu a, div.mainmenu a:visited{
nav.mainmenu a, nav.mainmenu a:visited{
  padding: 1px 10px 1px 10px;
  color: #333;
  text-decoration: none;
}
div.mainmenu a:hover {
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 {
149
150
151
152
153
154
155
156

157
158
159
160
161
162
163
147
148
149
150
151
152
153

154
155
156
157
158
159
160
161







-
+







  float: left;
  clear: left;
  color: #333;
  white-space: nowrap;
}

/* The footer at the very bottom of the page */
div.footer {
footer {
  font-size: 0.8em;
  margin-top: 12px;
  padding: 5px 10px 5px 10px;
  text-align: right;
  background-color: #eee;
  color: #555;
}

Changes to skins/black_and_white/footer.txt.

1

2
3


1
2

3
-
+

-
+
<div class="footer">
<footer>
Fossil $release_version $manifest_version $manifest_date
</div>
</footer>

Changes to skins/black_and_white/header.txt.

1

2
3
4
5
6
7
8
9
10
11
12





13
14
15
16



17
18

19
20
21


22
23
24

25
26

27
28
29
30
31

32
33

34
35
36
37
38


39
40
41

42
43
44
45
46
47



1
2
3
4
5
6
7





8
9
10
11
12
13



14
15
16


17



18
19



20


21





22


23





24
25



26






27
28
-
+






-
-
-
-
-
+
+
+
+
+

-
-
-
+
+
+
-
-
+
-
-
-
+
+
-
-
-
+
-
-
+
-
-
-
-
-
+
-
-
+
-
-
-
-
-
+
+
-
-
-
+
-
-
-
-
-
-
+
+
<div class="header">
<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"
     }
    if {[info exists login]} {
      puts "Logged in as $login"
    } else {
      puts "Not logged in"
    }
  </th1></div>
</div>
<div class="mainmenu">
<th1>
</header>
<nav class="mainmenu" title="Main Menu">
  <th1>
html "<a href='$home$index_page'>Home</a>\n"
if {[anycap jor]} {
    set sitemap 0
  html "<a href='$home/timeline'>Timeline</a>\n"
}
if {[anoncap oh]} {
    foreach {name url expr class} $mainmenu {
      if {![capexpr $expr]} continue
  html "<a href='$home/tree?ci=tip'>Files</a>\n"
}
if {[anoncap o]} {
      if {[string match /* $url]} {set url $home$url}
  html "<a href='$home/brlist'>Branches</a>\n"
  html "<a href='$home/taglist'>Tags</a>\n"
      html "<a href='$url'>$name</a><br/>\n"
}
if {[anycap 23456] || [anoncap 2] || [anoncap 3]} {
  html "<a href='$home/forum'>Forum</a>\n"
}
if {[anoncap r]} {
      if {[string match /sitemap $url]} {set sitemap 1}
  html "<a href='$home/ticket'>Tickets</a>\n"
}
    }
if {[anoncap j]} {
  html "<a href='$home/wiki'>Wiki</a>\n"
}
if {[hascap s]} {
  html "<a href='$home/setup'>Admin</a>\n"
    if {!$sitemap} {
      html "<a href='$home/sitemap'>Sitemap</a>\n"
} elseif {[hascap a]} {
  html "<a href='$home/setup_ulist'>Users</a>\n"
}
    }
if {[info exists login]} {
  html "<a href='$home/login'>Logout</a>\n"
} else {
  html "<a href='$home/login'>Login</a>\n"
}
</th1></ul></div>
  </th1>
</nav>

Changes to skins/blitz/css.txt.

561
562
563
564
565
566
567





568
569
570
571
572
573
574
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579







+
+
+
+
+







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"],
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
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







-
+








-
+



-
+




-
+



-
+





-
+



-
+



-
+



-
+







  box-sizing: border-box;
}


/* Header
 * Div displayed at the top of every page.
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */
.header {
header {
  color: #666;
  font-weight: 400;
  padding-top: 10px;
  border-width: 0px;
  border-top: 4px solid #446979;
  border-bottom: 1px solid #ccc;
}

.header .logo {
header .logo {
  display: inline-block;
}

.header .login {
header .login {
  padding-top: 2px;
  text-align: right;
}

.header .login .button {
header .login .button {
  margin: 0;
}

.header h1 {
header h1 {
  margin: 0px;
  color: #666;
  display: inline-block;
}

.header .logo h1 {
header .logo h1 {
  display: inline-block;
}

.header .title h1 {
header .title h1 {
  padding-bottom: 10px;
}

.header h1 small, .header h2 small {
header h1 small, header h2 small {
  color: #888;
}

.header a.rss {
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;
}

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
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







-
+








-
+













-







-










-
+
+

-







-
+
+


+
+
+
+
+
+
+
+
+
+







  color: #002060;
}


/* Footer
 * Displayed after the middle div and forms the page bottom.
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */
.footer {
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 {
footer a {
  color: #3b5c6b;
}


/* Main Menu
 * Displayed in header, contains repository links.
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */
.mainmenu {
    clear:both;
}

.mainmenu ul {
  list-style: none outside;
  display: block;
  position: relative;
  border-top: 1px solid #ccc;
  padding: 0;
}

.mainmenu li {
  outline: 0;
  display: block;
  float: left;
  margin: 0;
}

.mainmenu li.active {
  background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAJCAYAAADU6McMAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEQAACxEBf2RfkQAAAAd0SU1FB90FDxEXAZ2XRzAAAABJSURBVCjPY2CgBzhz5sx/QmoYiTXAxMSEkWRDsLkAl0GMpHoBm0EoAlu3bmUQFxcnGAboBjEhc4gxAJtLGUmJBVwuYiTXAGSDAIx5IBObnuVxAAAAAElFTkSuQmCC);
  background-repeat: no-repeat;
  background-position: center bottom;
}

.mainmenu li a {
.mainmenu li a,
nav#hbdrop a {
  color: #3b5c6b;
  display: block;
  padding: 10px 15px;
}

.mainmenu li.active a {
  font-weight: bold;
}

.mainmenu li:hover {
.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;
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
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







+
+
+
+




-



+
+
+
+




-







 * 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;
  padding-top: 2px;
}

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;
  padding-top: 2px;
}

div.filetreeline {
  display: table;
  width: 100%;
  white-space: nowrap;
}
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







-
+





+
+
+







tr.timelineCurrent {
  border-left: 2px solid orange;
  background-color: #ffc;
  border-bottom: 1px solid #ddd;
  border-right: 1px solid #ddd;
}

tr.timelineSelected {
.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;
}
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
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







-
+
















-
+







-
+







}

span.timelineComment {
  padding: 0px 5px;
}


/* Login/Loguot
/* 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
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */
pre.udiff, table.sbsdiffcols {
table.diff {
  width: 100%;
  overflow: auto;
  border: 1px solid #ccc;
  padding: 5px;
  font-size: 1rem;
}

pre.udiff:focus, table.sbsdiffcols:focus {
table.diff:focus {
  outline: none;
}


/* Ticket Reports
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */
table.report {
1252
1253
1254
1255
1256
1257
1258




1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285







+
+
+
+
.mainmenu:after,
.row:after,
.u-cf {
  content: "";
  display: table;
  clear: both;
}

body.forum .forumPosts.fileage a:visited {
  color: #648999;
}

Changes to skins/blitz/footer.txt.

1
2
3

4
5
6
7
8
9
10

1
2

3
4
5
6
7
8
9

10


-
+






-
+
  </div> <!-- end div container -->
</div> <!-- end div middle max-full-width -->
<div class="footer">
<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>
</div>
</footer>

Changes to skins/blitz/header.txt.

1

2
3
4
5
6
7
8
9
10
11
12
13
14
15

16
17
18

19
20
21
22
23

24
25
26
27

28
29

30
31
32
33
34
35







36
37
38
39





40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74









1
2
3
4
5
6
7
8
9
10
11
12



13

14

15
16
17
18
19
20
21
22
23
24

25
26
27
28






29
30
31
32
33
34
35




36
37
38
39
40



































41
42
43
44
45
46
47
48
-
+











-
-
-
+
-

-
+





+



-
+


+
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
<div class="header">
<header>
  <div class="container">

    <!-- Header -->
    <div class="login pull-right">
      <th1>
        if {[info exists login]} {
          html "<b>$login</b> — <a class='button' href='$home/login'>Logout</a>\n"
        } else {
          html "<a class='button' href='$home/login'>Login</a>\n"
        }
      </th1>
      <div>
        <h2><small>$title</small></h2>
      </div>
    </div>
    </div>
    <div class='logo'>
      <img src='$logo_image_url' />
      <h1>$<project_name>
      <th1>
      if {[anycap jor]} {
        html "<a class='rss' href='$home/timeline.rss'></a>"
      }
      </th1>
      <small> &nbsp;$<title></small></h1>
    </div>

    <!-- Main Menu -->
    <div class="mainmenu">
    <nav class="mainmenu" title="Main Menu">
      <ul>
        <th1>
          html "<li><a id='hbbtn' href='$home/sitemap' aria-label='Site Map'>&#9776;</a></li>\n"
proc menulink {url name} {
  upvar current_page current
  upvar home home
  if {[string range $url 0 [string length $current]] eq "/$current"} {
    html "<li class='active'>"
  } else {
          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>"
  }
  html "<a href='$home$url'>$name</a></li>\n"
}
            }
            html "<li class='$class'>"
            if {[string match /* $url]} {set url $home$url}
            html "<a href='$url'>$name</a></li>\n"
          }
menulink $index_page Home
if {[anycap jor]} {
  menulink /timeline Timeline
}
if {[hascap oh]} {
  menulink /dir?ci=tip Files
}
if {[hascap o]} {
  menulink  /brlist Branches
  menulink  /taglist Tags
}
if {[anycap 23456] || [anoncap 2] || [anoncap 3]} {
  menulink /forum Forum
}
if {[hascap r]} {
  menulink /ticket Tickets
}
if {[hascap j]} {
  menulink /wiki Wiki
}
if {[hascap o]} {
  menulink /help Help
}
if {[hascap s]} {
  menulink /setup Admin
} elseif {[hascap a]} {
  menulink /setup_ulist Users
}
            </th1>
          </ul>
        </div> <!-- end div mainmenu -->
      </div> <!-- end div container -->
    </div> <!-- end div header -->
    <div class="middle max-full-width">
      <div class="container">
        </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">

Changes to skins/blitz/ticket.txt.

1
2
3

4
5
6
7
8
9
10
1
2

3
4
5
6
7
8
9
10


-
+







<h4>$<title></h4>
<table class="tktDsp">
<tr><td class="tktDspLabel">Ticket&nbsp;UUID</td>
<tr><td class="tktDspLabel">Ticket&nbsp;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"

Deleted skins/blitz_no_logo/README.md.

1
2
3
4
5
6
7
8
9
10
11
12
13













-
-
-
-
-
-
-
-
-
-
-
-
-
## Blitz Theme (no logo)

Contributed by James Moger (james.moger@gitblit.com)

This theme is inspired by my own project, [Gitblit](http://gitblit.com), and offered to the Fossil project.

This theme embeds & uses an unmodified copy of [Normalize 3.0.2](https://necolas.github.io/normalize.css/) which is distributed under an [MIT license](https://github.com/necolas/normalize.css/blob/master/LICENSE.md).

This theme uses half of a heavily-modified version of [Skeleton](http://getskeleton.com) which is distributed under an [MIT license](https://github.com/dhg/Skeleton/blob/master/LICENSE.md).  None of the responsive elements (media queries) are included at this time.

The font used in the included Fossil logo image is [Trillium Web Light](http://www.google.com/fonts/specimen/Titillium+Web) @ 48px HTML color code #456a7a.

The RSS feed icon is sourced from [Font-Awesome](https://fortawesome.github.io/Font-Awesome/icons) by Dave Gandy and is distributed under the [SIL OFL 1.1 ](http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL) license.

Deleted skins/blitz_no_logo/css.txt.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258










































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
/*! normalize.css v3.0.2 | MIT License | git.io/normalize */

/**
 * 1. Set default font family to sans-serif.
 * 2. Prevent iOS text size adjust after orientation change, without disabling
 *    user zoom.
 */

html {
  font-family: sans-serif; /* 1 */
  -ms-text-size-adjust: 100%; /* 2 */
  -webkit-text-size-adjust: 100%; /* 2 */
}

/**
 * Remove default margin.
 */

body {
  margin: 0;
}

/* HTML5 display definitions
   ========================================================================== */

/**
 * Correct `block` display not defined for any HTML5 element in IE 8/9.
 * Correct `block` display not defined for `details` or `summary` in IE 10/11
 * and Firefox.
 * Correct `block` display not defined for `main` in IE 11.
 */

article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
main,
menu,
nav,
section,
summary {
  display: block;
}

/**
 * 1. Correct `inline-block` display not defined in IE 8/9.
 * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.
 */

audio,
canvas,
progress,
video {
  display: inline-block; /* 1 */
  vertical-align: baseline; /* 2 */
}

/**
 * Prevent modern browsers from displaying `audio` without controls.
 * Remove excess height in iOS 5 devices.
 */

audio:not([controls]) {
  display: none;
  height: 0;
}

/**
 * Address `[hidden]` styling not present in IE 8/9/10.
 * Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22.
 */

[hidden],
template {
  display: none;
}

/* Links
   ========================================================================== */

/**
 * Remove the gray background color from active links in IE 10.
 */

a {
  background-color: transparent;
}

/**
 * Improve readability when focused and also mouse hovered in all browsers.
 */

a:active,
a:hover {
  outline: 0;
}

/* Text-level semantics
   ========================================================================== */

/**
 * Address styling not present in IE 8/9/10/11, Safari, and Chrome.
 */

abbr[title] {
  border-bottom: 1px dotted;
}

/**
 * Address style set to `bolder` in Firefox 4+, Safari, and Chrome.
 */

b,
strong {
  font-weight: bold;
}

/**
 * Address styling not present in Safari and Chrome.
 */

dfn {
  font-style: italic;
}

/**
 * Address variable `h1` font-size and margin within `section` and `article`
 * contexts in Firefox 4+, Safari, and Chrome.
 */

h1 {
  font-size: 2em;
  margin: 0.67em 0;
}

/**
 * Address styling not present in IE 8/9.
 */

mark {
  background: #ff0;
  color: #000;
}

/**
 * Address inconsistent and variable font size in all browsers.
 */

small {
  font-size: 80%;
}

/**
 * Prevent `sub` and `sup` affecting `line-height` in all browsers.
 */

sub,
sup {
  font-size: 75%;
  line-height: 0;
  position: relative;
  vertical-align: baseline;
}

sup {
  top: -0.5em;
}

sub {
  bottom: -0.25em;
}

/* Embedded content
   ========================================================================== */

/**
 * Remove border when inside `a` element in IE 8/9/10.
 */

img {
  border: 0;
}

/**
 * Correct overflow not hidden in IE 9/10/11.
 */

svg:not(:root) {
  overflow: hidden;
}

/* Grouping content
   ========================================================================== */

/**
 * Address margin not present in IE 8/9 and Safari.
 */

figure {
  margin: 1em 40px;
}

/**
 * Address differences between Firefox and other browsers.
 */

hr {
  -moz-box-sizing: content-box;
  box-sizing: content-box;
  height: 0;
}

/**
 * Contain overflow in all browsers.
 */

pre {
  overflow: auto;
}

/**
 * Address odd `em`-unit font size rendering in all browsers.
 */

code,
kbd,
pre,
samp {
  font-family: monospace, monospace;
  font-size: 1em;
}

/* Forms
   ========================================================================== */

/**
 * Known limitation: by default, Chrome and Safari on OS X allow very limited
 * styling of `select`, unless a `border` property is set.
 */

/**
 * 1. Correct color not being inherited.
 *    Known issue: affects color of disabled elements.
 * 2. Correct font properties not being inherited.
 * 3. Address margins set differently in Firefox 4+, Safari, and Chrome.
 */

button,
input,
optgroup,
select,
textarea {
  color: inherit; /* 1 */
  font: inherit; /* 2 */
  margin: 0; /* 3 */
}

/**
 * Address `overflow` set to `hidden` in IE 8/9/10/11.
 */

button {
  overflow: visible;
}

/**
 * Address inconsistent `text-transform` inheritance for `button` and `select`.
 * All other form control elements do not inherit `text-transform` values.
 * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.
 * Correct `select` style inheritance in Firefox.
 */

button,
select {
  text-transform: none;
}

/**
 * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
 *    and `video` controls.
 * 2. Correct inability to style clickable `input` types in iOS.
 * 3. Improve usability and consistency of cursor style between image-type
 *    `input` and others.
 */

button,
html input[type="button"], /* 1 */
input[type="reset"],
input[type="submit"] {
  -webkit-appearance: button; /* 2 */
  cursor: pointer; /* 3 */
}

/**
 * Re-set default cursor for disabled elements.
 */

button[disabled],
html input[disabled] {
  cursor: default;
}

/**
 * Remove inner padding and border in Firefox 4+.
 */

button::-moz-focus-inner,
input::-moz-focus-inner {
  border: 0;
  padding: 0;
}

/**
 * Address Firefox 4+ setting `line-height` on `input` using `!important` in
 * the UA stylesheet.
 */

input {
  line-height: normal;
}

/**
 * It's recommended that you don't attempt to style these elements.
 * Firefox's implementation doesn't respect box-sizing, padding, or width.
 *
 * 1. Address box sizing set to `content-box` in IE 8/9/10.
 * 2. Remove excess padding in IE 8/9/10.
 */

input[type="checkbox"],
input[type="radio"] {
  box-sizing: border-box; /* 1 */
  padding: 0; /* 2 */
}

/**
 * Fix the cursor style for Chrome's increment/decrement buttons. For certain
 * `font-size` values of the `input`, it causes the cursor style of the
 * decrement button to change from `default` to `text`.
 */

input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
  height: auto;
}

/**
 * 1. Address `appearance` set to `searchfield` in Safari and Chrome.
 * 2. Address `box-sizing` set to `border-box` in Safari and Chrome
 *    (include `-moz` to future-proof).
 */

input[type="search"] {
  -webkit-appearance: textfield; /* 1 */
  -moz-box-sizing: content-box;
  -webkit-box-sizing: content-box; /* 2 */
  box-sizing: content-box;
}

/**
 * Remove inner padding and search cancel button in Safari and Chrome on OS X.
 * Safari (but not Chrome) clips the cancel button when the search input has
 * padding (and `textfield` appearance).
 */

input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-decoration {
  -webkit-appearance: none;
}

/**
 * Define consistent border, margin, and padding.
 */

fieldset {
  border: 1px solid #c0c0c0;
  margin: 0 2px;
  padding: 0.35em 0.625em 0.75em;
}

/**
 * 1. Correct `color` not being inherited in IE 8/9/10/11.
 * 2. Remove padding so people aren't caught out if they zero out fieldsets.
 */

legend {
  border: 0; /* 1 */
  padding: 0; /* 2 */
}

/**
 * Remove default vertical scrollbar in IE 8/9/10/11.
 */

textarea {
  overflow: auto;
}

/**
 * Don't inherit the `font-weight` (applied by a rule above).
 * NOTE: the default cannot safely be changed in Chrome and Safari on OS X.
 */

optgroup {
  font-weight: bold;
}

/* Tables
   ========================================================================== */

/**
 * Remove most spacing between table cells.
 */

table {
  border-collapse: collapse;
  border-spacing: 0;
}

td,
th {
  padding: 0;
}


/*
 * Blitz
 *
 * Skin inspired by Gitblit with heavily-modified excerpts from Skeleton 2.0.4.
 * Blitz is authored by james.moger@gitblit.com.
 *
 * Skeleton is authored by Dave Gamache and is distributed under the MIT license.
 * http://getskeleton.com
 *
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */

html {
  /* 62.5% so that the REM values are base 10px. */
  /* 1.5rem = 15px */
  font-size: 62.5%;
}

/* Typography
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */
h1, h2, h3, h4, h5, h6 {
  margin: 0;
  margin-bottom: 1rem;
  font-weight: 700;
}

h1 { font-size: 3.0rem; line-height: 1.2;  }
h2 { font-size: 2.6rem; line-height: 1.25; }
h3 { font-size: 2.4rem; line-height: 1.3;  }
h4 { font-size: 2.0rem; line-height: 1.35; }
h5 { font-size: 1.6rem; line-height: 1.5;  }
h6 { font-size: 1.4rem; line-height: 1.6;  }

h1 small, h2 small, h3 small, h4 small, h5 small, h6 small {
  font-size: 0.75em;
  font-weight: 400;
  color: #ccc;
}

pre, code {
  font-size: 1.2rem;
}

body {
  font-size: 1.4em; /* currently ems cause chrome bug misinterpreting rems on body element */
  line-height: 1.5;
  font-weight: 400;
  font-family: "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif;
  color: #333;
  background-color: #f8f8f8;
}

/* Spacing
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */
button,
.button {
  margin-bottom: 1rem;
}

input,
textarea,
select,
fieldset,
pre,
blockquote,
dl,
figure,
table,
p,
ul,
ol {
  margin-bottom: 1rem;
}

p {
  margin-top: 0;
}

hr {
  margin-top: 3rem;
  margin-bottom: 3.5rem;
  border-width: 0;
  border-top: 1px solid #ccc;
}


/* Buttons
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */
.button,
button,
input[type="button"],
input[type="reset"],
input[type="submit"] {
  display: inline-block;
  height: 3.3rem;
  padding: 0 2.2rem;
  color: #555 !important;
  text-align: center;
  font-size: 1.1rem;
  font-weight: 700;
  line-height: 3.3rem;
  letter-spacing: .08rem;
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
  background-color: transparent;
  border-radius: 4px;
  border: 1px solid #ccc;
  cursor: pointer;
  box-sizing: border-box;
}

.button:hover,
button:hover,
input[type="button"]:hover,
input[type="reset"]:hover,
.button:focus,
button:focus,
input[type="button"]:focus,
input[type="reset"]:focus {
  color: #444 !important;
  background-color: #eee;
  border-color: #aaa;
  outline: 0;
}

input[type="submit"] {
  color: white !important;
  background-color: #446979;
  border-color: #446979;
}

input[type="submit"]:hover,
input[type="submit"]:focus {
  color: white !important;
  background-color: #648898;
  border-color: #648898;
}


/* Forms
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */
input[type="email"],
input[type="number"],
input[type="search"],
input[type="text"],
input[type="tel"],
input[type="url"],
input[type="password"],
textarea,
select {
  height: 3.3rem;
  padding: 6px 10px;
  background-color: #fff;
  border: 1px solid #ddd;
  border-radius: 4px;
  box-shadow: none;
  box-sizing: border-box;
}

/* Removes awkward default styles on some inputs for iOS */
input[type="email"],
input[type="number"],
input[type="search"],
input[type="text"],
input[type="tel"],
input[type="url"],
input[type="password"],
textarea {
  -webkit-appearance: none;
     -moz-appearance: none;
          appearance: none;
}

textarea {
  height: inherit;
  min-height: 65px;
  padding-top: 6px;
  padding-bottom: 6px;
}

input[type="email"]:focus,
input[type="number"]:focus,
input[type="search"]:focus,
input[type="text"]:focus,
input[type="tel"]:focus,
input[type="url"]:focus,
input[type="password"]:focus,
textarea:focus,
select:focus {
  border: 1px solid #aaa;
  outline: 0;
}

label,
legend {
  display: block;
  margin-bottom: .5rem;
  font-weight: 700;
}

fieldset {
  padding: 0;
  border-width: 0;
}

input[type="checkbox"],
input[type="radio"] {
  display: inline;
}


/* Links
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */
a {
    color: #446979;
    text-decoration: none;
}

a:hover {
    text-decoration: underline;
}


/* Lists
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */
ul {
  list-style: square;
}

ol {
  list-style: decimal;
}

ol, ul {
  padding-left: 3rem;
  margin-top: 0;
}

li {
  margin-bottom: 0.5rem;
}


/* Nested Lists
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */
ul ul,
ul ol,
ol ol,
ol ul {
  margin: 1rem 0 1rem 2rem;
}


/* Code
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */
code, kbd {
  padding: .2rem .5rem;
  margin: 0 .2rem;
  white-space: nowrap;
  background: #f8f8f8;
  border: 1px solid #ccc;
  border-radius: 4px;
}

pre > code {
  display: block;
  padding: 1rem 1.5rem;
  white-space: pre;
}

pre.verbatim {
  background-color: inherit;
  white-space: pre-wrap;
}


/* Blockquote
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */
blockquote {
  padding: 0px 20px;
  margin: 0 0 20px;
  border-left: 4px solid #ccc;
}


/* Tables
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */
th,
td {
  padding: 6px 5px;
  text-align: left;
  border-bottom: 1px solid #ddd; }
th:first-child,
td:first-child {
  padding-left: 0; }
th:last-child,
td:last-child {
  padding-right: 0; }


/*
 * Blitz Page Layout Design
 *
 * html > body > header > container > mainmenu
 *               middle > container > submenu & content
 *               footer > container > generation stats, fossil logo, version
 *
 ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */


/* Container
 * Represents the usable layout space for header, middle, and footer.
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */
.container {
  position: relative;
  width: 100%;
  max-width: 900px;
  margin: 0 auto;
  box-sizing: border-box;
}


/* Header
 * Div displayed at the top of every page.
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */
.header {
  color: #666;
  font-weight: 400;
  padding-top: 10px;
  border-width: 0px;
  border-top: 4px solid #446979;
  border-bottom: 1px solid #ccc;
}

.header .logo {
  display: inline-block;
}

.header .login {
  padding-top: 2px;
  text-align: right;
}

.header .login .button {
  margin: 0;
}

.header h1 {
  margin: 0px;
  color: #666;
  display: inline-block;
}

.header .logo h1 {
  display: inline-block;
}

.header .title h1 {
  padding-bottom: 10px;
}

.header h1 small, .header h2 small, .header .login {
  color: #888;
}

.header a.rss {
  display: inline-block;
  padding: 10px 15px;
  background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3wMNDhwn05VjawAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAGlSURBVDjLrdPfb8xREAXwT7tIl+paVNaPJghCKC8kXv0XXvyNXsRfwYPQJqVKiqykWFVZXd12vcxNJtduUtJJvrm7984998ycMxxwNGI9jPs4j7nY+/U/gIdiPYO71dk21rCE7r8ybOHGmMfmcRNnsbEf1gXwNzqYSXs5WljEMXzAaBLg1Ji9Js7hOi6OeeAznqC/X8AcMyHWYpX7E4/Rm1QyHMdefCWGeI/VcMDR2D8S7Fci5y/AeTzCPVyLi1sYJAut4BTaiX0n9kc14MmkcjPY3I5LXezGtxqKtyJ3Lir6VAM2AmCq6m8Hl6PsQTB5hyvxmMhZxk4G3MZLfAwLtdNZM9rwOs528TVVNB3ga7UoQ2wGmyWciFaU0VwIJiP8iL6Xfp7GK+w0JthliDep8UKonTSGvbBTaU8f3QzYxgPcCsBvWK9E6OBFCNGPVjTTqC430p+H6fLVGLGtmIw7SbwevqT+XkgVPJ9Otpmtyl6I9XswLXEp/d6oPN0ugJu14xMLob4kgPRYjtkCOMDTUG+AZ3ibEtfDLorfEmAB3UuTdXDxBzUUZV+B82aLAAAAAElFTkSuQmCC);
  background-position: center center;
  background-repeat: no-repeat;
}


/* Middle
 * Center div displayed between header and footer. Contains per-page content.
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */
.middle {
  background-color: white;
  padding-bottom: 20px;
  max-width: 100%;
  box-sizing: border-box;
}


/* Content
 * Displayed below submenu within the middle div.
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */
.content {
    padding-top: 15px;
}

.content a {
  color: #002060;
}


/* Footer
 * Displayed after the middle div and forms the page bottom.
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */
.footer {
  padding: 10px 0 60px;
  border-top: 1px solid #ccc;
  background-color: #f8f8f8;
  background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABGCAQAAADxl9ugAAAAAmJLR0QA/4ePzL8AAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQffAwkQBRPw+yfrAAAAPHRFWHRDb21tZW50ACBJbWFnZSBnZW5lcmF0ZWQgYnkgRVNQIEdob3N0c2NyaXB0IChkZXZpY2U9cG5tcmF3KQqV01S1AAANg0lEQVRo3s2aa3RVRZbH/3tXnftIbhJCeJMXyEsNCsgrSBzAYXyt0YVj2zSOonbLqNNO+25FB2kdoBVdrp6l09P2NNpOL/ExSrtUdGRU5JEQgggCggIhQIBAIIGQ5N57TlXt+ZAICY+AGCT76zlV91dVe+/a+38uDQ7dYmrxw41QocJuni28vWTedEa7Gd9iatplos7Y2mfDRED6A2G0I2At6AdPIiA84HgbLhx1o9QX9Zoh7QjYHpOkYJYrfNyOKH0O/f2FJpfkSRmMCNqDk9rDXwTAuxK7h3uFf7vXxC6QVO8w19taNOT4Y0wu1sCdW0AA6IZ73bCUaI/GHuqnsJqcEqVrzSo+wNtLdgAPsz6jeVURtQ9gA8bQT8xztW7X2u3I4G6UdA0cohxcSAN7nt939/zDQzl0LnfwaLpxqFT/i7w7XLYXhoUQ+YFW3pJlC0/nt6jZZb4zjXY2AaGXvQOz/gMY09NM4C6ixRL7OU3PTm4ORdiIjyLsJvq1Z28Hj5rBM82xMTqPuhHznmU72vq12W5M3+LykQWUW9pirzXOmmk8wjE87kb8HPmSKlY+wY62R9jhI69xf/AmYiEfiXs+e4CCAZiDUc9wP6cRIJn8tq23PQClbwap9Ivk7usiGj/CDs5xl3Qe/EiQCEWsgXLlq8sfamM73osMna43ef/pfm731Hsxv+Z00ozFzaJpF+gMrsNB2BwL3cGpSsNRWHbmvTQNiTbev9q4xXszMLzqpdgDNjxv/YjmvHlSQIc5brl8nrqT5pllMpHM9wRc4gU3oC8JyDLt8P78ZLyM2lrmAeTg5YrKdf1S1N9QTs6At1YM49DJo1gwWy7uE5kKBQHbler9K8R+L8API2qmFoDY7ejxpxGJxtMctyZcfYvJDYX4i5LXp/MJAS2edkM6edNUDuLNOdMDmz913bIw8RidKO/1w+3NQfePbHQjdbd/NKOjbq7UqQOmYtXb/+BdbGc4AJjPX53CXe6UGweYWxUR8N8rNkzn4wAFc9yYPvGbI+mttoycpo14fWbD0hZv+njOAcBl6Yl8jiEMbSKhqGPypZ5N0B2kDxrjOjF0QuI2oRqj2z6rAebx5pNiRrEo0jhV9wFkP//xpkPHAU6UGdnBLyR8jMOQs6xwcMAznVys+Uqa5YCCnilFkuVSkeKlGKfDED8JaI+VifMWR9LPC/u+UkoZA6edOezVu83pny4yD6qQnCxhDzk/MhUED+8PWHJMkAgWROvvQoxOcEdayynVY9/5tIgAwsfIvaD3XaFx6M6ddMx3WlurCGICiAIhMLxB1VM+wQRMDOeYjHCUMum85OW909/acOlJgqaI3qrulU9dYGlgzVI1tsVrETzlOt3MeUGCudVoCgJmAhFU78F+6Usu0V39iylSHCRZMROM0do5pUDWQamEPZTy5oqyXVt77bG94HGEyTkCE8BsjLOcl11Uvf7V+qvIP86jgWH8zhc9LpKYU7qRhH5NTRknwFw3+jpMQCIItG69PCLfZ1YKAMlS/7PQDCc2qbRzSjET4slINBAvhsPxjarKKy/9FhjZG8Ol4tINi/uFzkcfl8sEnwExATGRY73Ze2VpAHxHoPFki7r2iljN3a6LqqOxnZ+vXUBNwTF6uL0e0Nr3tSYWpqNhQtaIaA0A1oZftuf5Y1xmCMRO2IM1VXqf3aYr5IDXODTx7+43PAjPFcit1MANeHfFxuFpSFW9TbbORpaNEqt6/5tIoxuBOO049NHXNTN5lFwlE3o09JUMlWKdSmJ/UC8TvRwqjNzoVwNIw3upcluyq4hSAq3Idyt5HOyRIDGuGdA42un+6wv/Cq7sFA1F/B4N/5MEgL/yOsSPlFSMOzD5JhkMEQ8l8u41Job7hVoExsiRNMU1Biac4oqD92Np8Xt0BgUiEOeIHLyobbQb9U/8fSAItsFdJv1wECASB5L92xb2yefcE3Q+jgsof//m3+PO2i7YhgV0HisAK48UnE03EaT67axMyjMNXpFkf/za8r3VPP2I46Ri+aYDW0z3SMT6NJb62wPiJEFhEoFCYIhNoyS4VF1CBKArPu6MKWysBZhFVFrD7yri2VkuR4QZAIkTMAOAiDIY9KvFQ3gVPqXVFD9JSbSS3vP7b/QzKY8SlO6G5e2fv/dozvDxciKvCqOYrIURT/XU2+0SKrdVqtpVojz4Sq1Vi1duV0UE+PSEy33AhRRZAzDDc4vXrB1xKU0gRUR0LCCRi/be8XZ12/0M41Ka5K/5JjA80PlQKMjfeVVN9ZG0NZbmH8xf5Y8nJxIKmYC7SmLlexeVp2wKb+y++f6KZ3c/03g+qSISPONGDqHRZOk7QGde6XsrjTP+kXTTCpCVGErZteZUDRdhD3Ls65tz64J85ZGyQ0tXDAzckadFlOtv+ZQLoYMgHHaGcrJ7fb56inR3WVJJRVTdVM3kQSh5r1hjlbIGIKVX6ammE1vnmImh4Y4BZHGcvmvx5XQ6BcRIfnPHxV82ZnFUYsh+bWXLzMuwquGwCnvdBUTkqHcPmb+l5cJZcKervlwc8N04sbYwSJC1tsnneemJqh32LksZflr1SUh+pT6q+/ov/krztitDq1s4hP1p7mbfuFfkEKJgSejLh/Wa7Y5GJsdQmEYDoVocDCvnLCvnwPDMzOK/uiVoPS8IxoQi409D2xAU4Hd2xE2DZ3gjvcpVZY+0iqgAbFh7F9grp84xf5D6QFOE/rYo1vto4z6ZVg/COHKAiGJrACaljNHaiaqTp0NeXlr37Ydz/ahSLY9YzLIPMzlxSrxMzM/ochfnO8UHvJJpgTnm+UE5GKMeOu3LrLJPd32+Z1FamfczKli36jzTtHplvPg46o2WgNwEaOqoTk8Jxsu4+vGUruDkCCCJkg171g2ntqMkjrmuYUBwG7LglIq/U7Zr1HEjJrtiay9imIzsqt37gb4/RSZlSsP88iZf5T3kX2xOuBEck2yp13F32NbDuZbHqSjwFrzJbeP5eN6NHOJuoQyXRIZ7Y+3Gh08wohjpW/VOkFMyqagHILnOSAOuflh1bqKggew524Zs1ex+R6cmz1W7Z6clvzzF8T7rLsvnSfBgJVO9cN+ax4/IRwazZLZrqmOSWJjAR0hCdFc/F2AtToTos34POQHAkVw2Sp2m7EKwOEyLVj59w6EtpyjdFYSCCYhaC0n9fcmmr5pb8QBrQl0wqn/hNc82J8Rfc8lWVwVGwLeNDVPTdW30+KZN0Sab5JTBqOBBowIHzeZVJcBjXHdKXfQp93+DgwuIgy9QtqJ8evPhOtRRzZi30iKfBZPxQVNhofDP6sUXh05zA5MH+WKdpAgAJ/lAChqhyQmd5HgFChFoMvar0AaKq8qSGmA6o03lVFCEqx1Q0Ce0075hkgvW93EPNeMZ3Cl93eRl5Q8GVjoBs1tUN1l/rvkn1VOuNXUIAyATv6zz3bXPEo3t7N/L6bBirfV0MimktOclgnDUJHiTWxo9oJOZQT/TDUk0nIYfFODFaMMN4WFqbnEl8BzXtlBX++ATr2KW3eC9kbg/9d2l6/qmdxrq9bdRkBwMtrgd+lpkpxwyWSwAkvRKSfl0phf4l+6acGVeak6QQbCJYH/Gwci+zPi/+oMFEJpLdTDfQ8IpTTl8r/lkQuniX3oR98mjX65sJQZ0wYNu1NU0wZ8RjSUf0Qo22aCU1oAoCazTFe5bdZ1pVIqcv2B12XSm6awxCFNaichCr1I1auDOQFvKxzQHFD4mexGXNfds/PoYB+iMhxzwIC8vcJe7HKVIrBEoLRQkuVL+Ii70lDlMUOR/uHrJdNaAwXo82orksR8gfVUAGFkgy5JfhKdEKncelwhq8SinwMeidX+303VHuvOgCGSRRF2o6vb6FyKmQTGRNTraQt1qJ6EaAPA4V22MYH20wXx+aCyfKFvFAcymiYfCh/YhSQZACFHpBB/l6On2l0s/AgTBWdIHHbpIulzfWDbvAW4r3gPUIwWZ0lW6SoaE0AgDYG6Six0R4OjgWRMwGUAVHuUz+SS2iNQ+XSMAvKqzrLCemdtUo3ifWhpoTi7b/RSfVRH9zBc2y43umXVgtImf3R08c7zCIeYpVoPOtoh+ZjYEhd3kWtV4IHSldEjAV7U/ATHxig89Tx0QUFCtvUJisxnNknuHArSY4+xtNuk4ZQWai4wOBThJRqZJvhYXtptnNTVAHQnQYbT4k7QI6/Vd6w6f/U9h39cUgEg+BCG3rk/AHQ9wlhsz1IUCI3tRPrDVtdlhzO9rxPNke+nuXR0TUIdAMOrDlkJshwEM4QId98C8vKTmUUJHAwww03VNT8ny9hd/cJ9qCaU7QnrpjXvcsKuT6W7VdcvHc+sq95wDCuZILxp9t+TZJWVL/56PPdJzDhjDhG75UyUrsXXtBw9z/PjAObd3729leA++mTOldO07dyl9ghbmnALmYpjybndhO3/VV/dx9IQdlj53YQEAo2din3p1cv1VfDIF8EfvSdJQSZvU3ljGJJOvNuH94kOPs22jwWq3P5edTryG8G/OdtsxOPiZd6O1ppTeK0n43Hb/9yPuYBhPuFFXumEq3231S+ibL/e+yhtP2Zz+iD74hCu83vblr+W1yJ5LgguxmzedRu/8//AUSaqMTR1xAAAAAElFTkSuQmCC);
  background-repeat: no-repeat;
  background-position: center top 10px;
}

.footer a {
  color: #3b5c6b;
}


/* Main Menu
 * Displayed in header, contains repository links.
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */
.mainmenu {
    clear:both;
}

.mainmenu ul {
  list-style: none outside;
  display: block;
  position: relative;
  border-top: 1px solid #ccc;
  padding: 0;
}

.mainmenu li {
  outline: 0;
  display: block;
  float: left;
  margin: 0;
}

.mainmenu li.active {
  background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAJCAYAAADU6McMAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEQAACxEBf2RfkQAAAAd0SU1FB90FDxEXAZ2XRzAAAABJSURBVCjPY2CgBzhz5sx/QmoYiTXAxMSEkWRDsLkAl0GMpHoBm0EoAlu3bmUQFxcnGAboBjEhc4gxAJtLGUmJBVwuYiTXAGSDAIx5IBObnuVxAAAAAElFTkSuQmCC);
  background-repeat: no-repeat;
  background-position: center bottom;
}

.mainmenu li a {
  color: #3b5c6b;
  display: block;
  padding: 10px 15px;
}

.mainmenu li.active a {
  font-weight: bold;
}

.mainmenu li:hover {
    background-color: #eee;
}


/* Submenu
 * Displayed in the middle div.  Contains page-specific form controls.
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */
.submenu {
    padding: 10px 0px;
    border-bottom: 1px solid #ddd;
}

.submenu input, .submenu select {
  margin: 0 0 0 5px;
}

.submenu a, .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 {
  background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QAVQBVAFV4xrLkAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3wMLExABnLjGZQAAAEFJREFUOMtjYKAQMIaGhv4npGj16tWMuORYGBgYGOZW+eDUnNy2Ba/hLMQ4E58rCRpAyHVMlAbiqAGjBhCdmWgKAHp4Dh0ZusP3AAAAAElFTkSuQmCC);
  background-repeat: no-repeat;
  background-position: 0px center;
  padding-left: 22px;
  padding-top: 2px;
}

ul.browser li.file {
  background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QAVQBVAFV4xrLkAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3wMLExMaPfBcSgAAAFNJREFUOMvtkzEOwDAIA02VL5pHwiOTJZFQmkqFOTex+CwPCCYkAaDjB+4u65ZdYGafQVV9SR4kWQUke0mwS1o2HGcAQKs0R1lpQuQKruD4jVnBAG/cGRqf0U66AAAAAElFTkSuQmCC);
  background-repeat: no-repeat;
  background-position: 0px center;
  padding-left: 22px;
  padding-top: 2px;
}

div.filetreeline {
  display: table;
  width: 100%;
  white-space: nowrap;
}

/* tree-view top-level list */
.filetree > ul {
  display: inline-block;
}

/* tree-view lists */
.filetree ul {
  margin: 0;
  padding: 0;
  list-style: none;
}
/* tree-view collapsed list */
.filetree ul.collapsed {
  display: none;
}
/* tree-view lists below the root */
.filetree ul ul {
  position: relative;
  margin: 0 0 0 21px;
}
/* tree-view lists items */
.filetree li {
  position: relative;
  margin: 0;
  padding: 0;
}
/* tree-view node lines */
.filetree li li:before {
  content: '';
  position: absolute;
  top: -.8em;
  left: -14px;
  width: 16px;
  height: 1.5em;
  border-left: 1px solid #ccc;
  border-bottom: 1px solid #ccc;
}
/* tree-view directory lines */
.filetree li > ul:before {
  content: '';
  position: absolute;
  top: -1.5em;
  bottom: 0;
  left: -35px;
  border-left: 1px solid #ccc;
}
/* hide lines for last-child directories */
.filetree li.last > ul:before {
  display: none;
}

.filetree a {
  position: relative;
  z-index: 1;
  display: table-cell;
  min-height: 16px;
  padding-left: 22px;
  background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QAVQBVAFV4xrLkAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3wMLExMaPfBcSgAAAFNJREFUOMvtkzEOwDAIA02VL5pHwiOTJZFQmkqFOTex+CwPCCYkAaDjB+4u65ZdYGafQVV9SR4kWQUke0mwS1o2HGcAQKs0R1lpQuQKruD4jVnBAG/cGRqf0U66AAAAAElFTkSuQmCC);
  background-position: center left;
  background-repeat: no-repeat;
}

.filetree .dir > div.filetreeline > a {
  background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QAVQBVAFV4xrLkAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3wMLExABnLjGZQAAAEFJREFUOMtjYKAQMIaGhv4npGj16tWMuORYGBgYGOZW+eDUnNy2Ba/hLMQ4E58rCRpAyHVMlAbiqAGjBhCdmWgKAHp4Dh0ZusP3AAAAAElFTkSuQmCC);
}


/* Label-Value table
 * Displayed on the Check-in & Admin pages.
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */
table.label-value th {
  vertical-align: middle;
}


/* Branches table
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */
.brlist table td {
  padding: 5px;
}


/* Timeline
 * Displays chronologically-ordered check-ins with a branch graph.
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */
tr.timelineCurrent {
  border-left: 2px solid orange;
  background-color: #ffc;
  border-bottom: 1px solid #ddd;
  border-right: 1px solid #ddd;
}

tr.timelineSelected {
  border-left: 2px solid orange;
  background-color: #ffffe8;
  border-bottom: 1px solid #ddd;
  border-right: 1px solid #ddd;
}

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/Loguot
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */
table.login_out {
}

table.login_out .login_out_label {
  font-weight: 700;
  text-align: right;
}

table.login_out td {
  border: 0;
}


/* Diff displays
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */
pre.udiff, table.sbsdiffcols {
  width: 100%;
  overflow: auto;
  border: 1px solid #ccc;
  padding: 0px 5px;
  font-size: 1rem;
}

pre.udiff:focus, table.sbsdiffcols:focus {
  outline: none;
}


/* Ticket Reports
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */
table.report {
  width: 100%;
  cursor: auto;
  border-radius: 4px;
  border: 1px solid #ccc;
  margin: 0 0 1em 0;
}

.report td, .report th {
  border: 0;
  font-size: .9em;
  padding: 5px;
}

.report th {
  cursor: pointer;
}

.report thead+tbody tr:hover {
  background-color: #f5f9fc !important;
}


/* Ticket page
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */
table.tktDsp {
  border-top: 1px solid #ccc;
  border-left: 1px solid #ccc;
  width: 100%;
  margin: 15px 0px 10px 0px;
}

td.tktDspLabel, td.tktDescLabel {
  width: 70px;
  text-align: right;
  overflow: hidden;
  font-weight: 700;
  padding: 10px;
  background-color: #f8f8f8;
}

td.tktDescLabel {
  vertical-align: top;
}

td.tktDspValue, td.tktDescValue {
  text-align: left;
  vertical-align: top;
  border: 1px solid #ccc;
  padding: 10px;
}

td.tktDspValue pre, td.tktDescValue pre,
td.tktDspValue code, td.tktDescValue code {
  white-space: pre-wrap;
}

div.tktComments {
  width: 100%;
  margin: 30px 0px 10px 0px;
}

div.tktComment {
}

div.tktCommentHeader {
  border: 1px solid #ccc;
  background-color: #f8f8f8;
  padding: 10px 10px;
  margin-bottom: 10px;
}

span.tktCommentLogin {
  display: inline-block;
  font-weight: 700;
  color: #002060;
}

div.tktCommentBody {
  margin: 10px 40px 30px;
}


/* User setup table
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */
td.usetupEditLabel {
  font-weight: 700;
}


/* Utilities
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */
.full-width {
  width: 100%;
  box-sizing: border-box;
}

.max-full-width {
  max-width: 100%;
  box-sizing: border-box;
}

.pull-right {
  float: right;
}

.pull-left {
  float: left;
}

/* Clearing
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */
.container:after,
.mainmenu:after,
.row:after,
.u-cf {
  content: "";
  display: table;
  clear: both;
}

Deleted skins/blitz_no_logo/details.txt.

1
2
3
4




-
-
-
-
timeline-arrowheads:        0
timeline-circle-nodes:      1
timeline-color-graph-lines: 1
white-foreground:           0

Deleted skins/blitz_no_logo/footer.txt.

1
2
3
4
5
6
7
8
9
10










-
-
-
-
-
-
-
-
-
-
  </div> <!-- end div container -->
</div> <!-- end div middle max-full-width -->
<div class="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>
</div>

Deleted skins/blitz_no_logo/header.txt.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71







































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
<div class="header">
  <div class="container">

    <div class="login pull-right">
      <th1>
        if {[info exists login]} {
          html "<b>$login</b> — <a class='button' href='$home/login'>Logout</a>\n"
        } else {
          html "<a class='button' href='$home/login'>Login</a>\n"
        }
      </th1>
    </div>
    <div class='title'>
      <h1>$<project_name>
      <th1>
      if {[anycap jor]} {
        html "<a class='rss' href='$home/timeline.rss'></a>"
      }
      </th1>
      <small> &nbsp;$<title></small></h1>
    </div>

    <!-- Main Menu -->
    <div class="mainmenu">
      <ul>
        <th1>
proc menulink {url name} {
  upvar current_page current
  upvar home home
  if {[string range $url 0 [string length $current]] eq "/$current"} {
    html "<li class='active'>"
  } else {
    html "<li>"
  }
  html "<a href='$home$url'>$name</a></li>\n"
}
menulink $index_page Home
if {[anycap jor]} {
  menulink /timeline Timeline
}
if {[hascap oh]} {
  menulink /dir?ci=tip Files
}
if {[hascap o]} {
  menulink  /brlist Branches
  menulink  /taglist Tags
}
if {[anycap 23456] || [anoncap 2] || [anoncap 3]} {
  menulink /forum Forum
}
if {[hascap r]} {
  menulink /ticket Tickets
}
if {[hascap j]} {
  menulink /wiki Wiki
}
if {[hascap o]} {
  menulink /help Help
  }
if {[hascap s]} {
  menulink /setup Admin
} elseif {[hascap a]} {
  menulink /setup_ulist Users
}
            </th1>
          </ul>
        </div> <!-- end div mainmenu -->
      </div> <!-- end div container -->
    </div> <!-- end div header -->
    <div class="middle max-full-width">
      <div class="container">

Deleted skins/blitz_no_logo/ticket.txt.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115



















































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
<h4>$<title></h4>
<table class="tktDsp">
<tr><td class="tktDspLabel">Ticket&nbsp;UUID</td>
<th1>
if {[info exists tkt_uuid]} {
  if {[hascap s]} {
    html "<td class='tktDspValue' colspan='3'>$tkt_uuid "
    html "($tkt_id)</td></tr>\n"
  } else {
    html "<td class='tktDspValue' colspan='3'>$tkt_uuid</td></tr>\n"
  }
} else {
  if {[hascap s]} {
    html "<td class='tktDspValue' colspan='3'>Deleted "
    html "(0)</td></tr>\n"
  } else {
    html "<td class='tktDspValue' colspan='3'>Deleted</td></tr>\n"
  }
}
</th1>
<tr><td class="tktDspLabel">Status</td><td class="tktDspValue">
$<status>
</td>
<td class="tktDspLabel">Type</td><td class="tktDspValue">
$<type>
</td></tr>
<tr><td class="tktDspLabel">Severity</td><td class="tktDspValue">
$<severity>
</td>
<td class="tktDspLabel">Priority</td><td class="tktDspValue">
$<priority>
</td></tr>
<tr><td class="tktDspLabel">Subsystem</td><td class="tktDspValue">
$<subsystem>
</td>
<td class="tktDspLabel">Resolution</td><td class="tktDspValue">
$<resolution>
</td></tr>
<tr><td class="tktDspLabel">Last&nbsp;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&nbsp;Found&nbsp;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>

Deleted skins/bootstrap/css.txt.

cannot compute difference between binary files

Deleted skins/bootstrap/details.txt.

1
2
3
4




-
-
-
-
timeline-arrowheads:        1
timeline-circle-nodes:      1
timeline-color-graph-lines: 1
white-foreground:           0

Deleted skins/bootstrap/footer.txt.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38






































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
<th1>
  if {! $is_index && ! $is_home} {
    html "</div>"
  }
</th1>
</div>
<div id="push"></div>
</div>
<footer id="footer">
  <p>&#169; Copyright $<project_name>. All right reserved. Fossil $release_version &#183; <a href="$home/timeline.rss">RSS</a></p>
</footer>
<script>
var tables = document.querySelectorAll('table');
for (var i = 0; i < tables.length; i++) {
  if (tables[i].id !== "timelineTable")
  tables[i].classList.add('table');
};
var submenus = document.querySelectorAll('.submenu');
for (var i = 0; i < submenus.length; i++) {
  submenus[i].classList.add('btn-group');
  var labels = submenus[i].querySelectorAll('.label');
  for (var j = 0; j < labels.length; j++) {
    labels[j].classList.remove('label');
    labels[j].classList.add('btn');
    labels[j].classList.add('btn-default');
    labels[j].classList.add('btn-sm');
  }
};
//Handle the collapsible navbar
var collapse = document.querySelector('[data-toggle="collapse"]');
collapse.onclick = function(){
  var target = document.querySelector(
    collapse.getAttribute('data-target')
  );
  target.classList.toggle('collapse');
  target.classList.toggle('collapsed');
};
</script>

Deleted skins/bootstrap/header.txt.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123



























































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
<html lang="en">
<head>
  <meta charset="utf-8">
  <base href="$baseurl/$current_page" />
  <title>$<project_name>: $<title></title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="Content-Security-Policy" content="default-src 'self' data:; script-src 'self' 'nonce-$<nonce>'; style-src 'self' 'unsafe-inline'"/>
    <link rel="alternate" type="application/rss+xml" title="RSS Feed" href="$home/timeline.rss" />
    <link rel="stylesheet" href="$home/style.css?default" type="text/css" media="screen" />
    <script>
    function gebi(x){
      if(/^#/.test(x)) x = x.substr(1);
      var e = document.getElementById(x);
      if(!e) throw new Error("Expecting element with ID "+x);
      else return e;
    }
    </script>
  </head>
  <body data-spy="scroll" data-target=".sidebar">
    <div id="wrap">
      <div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
        <div class="container">
          <div class="navbar-header">
            <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target=".navbar-collapse">
              <span class="sr-only">Toggle navigation</span>
              <span class="icon-bar"></span>
              <span class="icon-bar"></span>
              <span class="icon-bar"></span>
            </button>
            <th1>html "<a class='navbar-brand' href='$home$index_page'>$project_name</a>"</th1>
          </div>
          <div class="collapse navbar-collapse">
            <p class="navbar-text pull-right"><th1>
              if {[info exists login]} {
                puts "Logged in as $login"
                html " &middot; <a href='$home/login'>Logout</a>"
              } else {
                puts "Not logged in"
                html " &middot; <a href='$home/login'>Login</a>"
              }
            </th1></p>
            <ul class="nav navbar-nav">
              <th1>
                set is_index [expr [string compare [string range $current_page 0 4] "index"]==0]
                set is_home [expr [string compare [string range $current_page 0 [expr [string length $index_page]-1] ] $index_page]==0]
                if {$is_index || $is_home} {
                  html "<li class='active'><a href='$home$index_page'>Home</a></li>\n"
                } else {
                  html "<li><a href='$home$index_page'>Home</a></li>\n"
                }
                if {[hascap j]} {
                  if {[string compare [string range $current_page 0 3] "wiki"] == 0} {
                    html "<li class='active'><a href='$home/wiki'>Wiki</a></li>\n"
                  } else {
                    html "<li><a href='$home/wiki'>Wiki</a></li>\n"
                  }
                }
                if {[anycap jor]} {
                  if {[string compare $current_page "timeline"] == 0} {
                    html "<li class='active'><a href='$home/timeline'>Timeline</a></li>\n"
                  } else {
                    html "<li><a href='$home/timeline'>Timeline</a></li>\n"
                  }
                }
                if {[hascap oh]} {
                  if {[string compare [string range $current_page 0 2] "dir"] == 0} {
                    html "<li class='active'><a href='$home/dir?ci=tip'>Files</a></li>\n"
                  } else {
                    html "<li><a href='$home/dir?ci=tip'>Files</a></li>\n"
                  }
                }
                if {[hascap o]} {
                  if {[string compare $current_page "brlist"] == 0} {
                    html "<li class='active'><a href='$home/brlist'>Branches</a></li>\n"
                  } else {
                    html "<li><a href='$home/brlist'>Branches</a></li>\n"
                  }
                  if {[string compare $current_page "taglist"] == 0} {
                    html "<li class='active'><a href='$home/taglist'>Tags</a></li>\n"
                  } else {
                    html "<li><a href='$home/taglist'>Tags</a></li>\n"
                  }
                }
                if {[anycap 23456] || [anoncap 2] || [anoncap 3]} {
                  if {[string compare $current_page "forum"] == 0} {
                    html "<li class='active'><a href='$home/forum'>Forum</a></li>\n"
                  } else {
                    html "<li><a href='$home/forum'>Forum</a></li>\n"
                  }
                }
                if {[hascap r]} {
                  if {[string compare $current_page "reportlist"] == 0} {
                    html "<li class='active'><a href='$home/reportlist'>Tickets</a></li>\n"
                  } else {
                    html "<li><a href='$home/reportlist'>Tickets</a></li>\n"
                  }
                }
                if {[hascap s]} {
                  if {[string compare [string range $current_page 0 4] "setup"] == 0} {
                    html "<li class='active'><a href='$home/setup'>Admin</a></li>\n"
                  } else {
                    html "<li><a href='$home/setup'>Admin</a></li>\n"
                  }
                } elseif {[hascap a]} {
                  if {[string compare [string range $current_page 0 4] "setup"] == 0} {
                    html "<li class='active'><a href='$home/setup_ulist'>Users</a></li>\n"
                  } else {
                    html "<li><a href='$home/setup_ulist'>Users</a></li>\n"
                  }
                }
              </th1>
            </ul>
          </div><!--/.nav-collapse -->
        </div>
      </div>
      <div class="content">
        <th1>
          html "<div class='container'>"
            html "<ul class='breadcrumb'>"
              html "<li><a href='$home$index_page'>Home</a></li>"
              html "<li><a href='$home/$current_page'>[htmlize $title]</a></li>"
              html "</ul>"
            </th1>

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'>&#9776;</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>

Changes to skins/default/README.md.

1


2
3
4
5








1
2
3



4
5
6
7
8
9
10
-
+
+

-
-
-
+
+
+
+
+
+
+
This skin was contributed by Étienne Deparis.
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:

On 2015-03-14 this skin was promoted from an option to the default, which
involved moving it from its original home in the skins/etienne1 directory
into skins/default.
   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.

Changes to 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
1
2
3
4



5
6
7




8
9
10
11
12
13
14
15
16
17
18
19
20
21


22
23
24
25


26
27
28



29
30



31
32
33


34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

55
56
57
58



59
60
61
62



63
64
65


66



67


68

69


70
71






72

73




74



75
76



77
78
79
80




81
82
83
84









85
86
87
88
89
90
91
92
93

94



95
96
97
98
99
100


101
102
103













104
105
106
107
108
109
110
111
112
113
114
115
116
117
118



119
120
121
122

123



124
125
126
127
128
129

130
131




132
133
134
135






















































































136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
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;
  margin: 0 auto;
  background-color: white;
  font-family: sans-serif;
    font-size:14pt;
    -moz-text-size-adjust: none;
    -webkit-text-size-adjust: none;
    -mx-text-size-adjust: none;
}

a {
  /* 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: #4183C4;
    text-decoration: none;
  color: #3779BF;
  text-decoration: none;
}
a:hover {
    color: #4183C4;
    text-decoration: underline;
  color: #4183C4;
  text-decoration: underline;
}
div.forumPosts a:visited {
    color: #6A7F94;
}


hr {
    color: #eee;
}
/* Page title, above menu bars */

.title {
    color: #4183C4;
    float:left;
  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;
  display: inline;
  font-size: 2.20em;
}
.title h1:after {
    content: " / ";
    color: #777;
    font-weight: normal;
  content: " / ";
  color: #777;
  font-weight: normal;
}

.content h1 {
    font-size: 1.25em;
.artifact .title h1:after,
.dir      .title h1:after,
.doc      .title h1:after,
}
.content h2 {
.wiki     .title h1:after {
    font-size: 1.15em;
}
.content h3 {
  content: "";    /* hide solidus for docs along with title h1 */
    font-size: 1.05em;
    font-weight: bold;
}

.status {
.section {
    font-size: 1em;
  float: right;
  font-size: 0.8em;
    font-weight: bold;
    background-color: #f5f5f5;
    border: 1px solid #d8d8d8;
    border-radius: 3px 3px 0 0;
    padding: 9px 10px 10px;
    margin: 10px 0;
}

div.logo {
.sectionmenu {
    border: 1px solid #d8d8d8;
    border-radius: 0 0 3px 3px;
    border-top: 0;
  float: left;
    margin-top: -10px;
    margin-bottom: 10px;
    padding: 10px;
  padding-right: 10px;
}
.sectionmenu a {
    display: inline-block;
    margin-right: 1em;
div.logo img {
  max-height: 2em;    /* smaller than title to keep it above the baseline */
}

.status {
    float:right;
    font-size:.7em;
}

/* Main menu and optional sub-menu */

.mainmenu {
    font-size:.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 */
  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;
  text-decoration: none;
  color: #777;
  border-right: 1px solid #eaeaea;
}
.mainmenu a.active,
.mainmenu a:hover {
    color: #000;
    border-bottom:2px solid #D26911;
  color: #000;
  border-bottom: 2px solid #D26911;
}

div#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 */
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;
  font-size: 0.8em;
  padding: 10px;
  border-bottom: 1px solid #ccc;
}

.submenu a, .submenu label {
    padding: 10px 11px;
    text-decoration:none;
    color: #777;
  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;
  padding: 6px 10px;
  border: 1px solid #ccc;
  border-radius: 5px;
  color: #000;
}

.content {
    padding-top: 10px;
    font-size:.8em;
    color: #444;
}

.udiff, .sbsdiff {
    font-size: .85em !important;
    overflow: auto;
    border: 1px solid #ccc;
    border-radius: 5px;
}
.content blockquote {
    padding: 0 15px;
}
div.forumHierRoot blockquote, div.forumHier blockquote {
    background-color: rgba(65, 131, 196, 0.1);
    border-left: 3px solid #254769;
    padding: .1em 1em;
}

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;
}

span.timelineDetail {
    font-size: 90%;
}

.footer {
    border-top: 1px solid #ccc;
    padding: 10px;
    font-size:.7em;
    margin-top: 10px;
    color: #ccc;
}
div.timelineDate {
    font-weight: bold;
    white-space: nowrap;
}
span.submenuctrl, span.submenuctrl input, select.submenuctrl {
  color: #777;
}
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;
}

div.submenu label {
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: 10px 10px;
    padding: 8px 10px;
  }
  .mainmenu {
    padding: 10px;
  }
  .desktoponly {
    display: none;
  }
}
@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: 10px 20px;
    padding: 8px 20px;
  }
  .mainmenu {
    padding: 10px;
  }
}
@media screen and (max-width: 1200px) {
  /* Special declarations for narrow desktop or wide mobile */
  .wideonly {
    display: none;

  /* 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;
  }
}

Changes to skins/default/details.txt.



1
2
3
4
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

Changes to skins/default/footer.txt.

1

2
3
4
5

6
7
8

1
2
3
4

5



-
+



-
+
-
-
-
<div class="footer">
<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
</div>
</footer>
<script nonce="$nonce">
<th1>styleScript</th1>
</script>

Changes to 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







1





2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18


19
20





21


22



23
24
25






26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42


43
44



45
46



47
48
49
50
51
52
53


54
55





56
57
58
59



60
61







62
63
64
65
66
67
-
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
-
-
-
-
-
+
-
-
+
-
-
-
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
-
-
-
+
+
-
-
-
+
+
+
+
+
+
+
-
-
+
+
-
-
-
-
-
+
+
+
+
-
-
-
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
<div class="header">
<header>
  <div class="title"><h1>$<project_name></h1>$<title></div>
    <div class="status"><th1>
 if {[info exists login]} {
   html "$login — <a href='$home/login'>Logout</a>\n"
 } else {
  <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 {
   html "<a href='$home/login'>Login</a>\n"
 }
            set scheme https://
          }
    </th1></div>
</div>
<div class="mainmenu">
<th1>
proc menulink {url name cls} {
          set logourl $scheme$host/
  upvar current_page current
  upvar home home
        } else {
  if {[string range $url 0 [string length $current]] eq "/$current"} {
    html "<a href='$home$url' class='active $cls'>$name</a>\n"
  } else {
          set logourl $baseurl
        }
      } else {
    html "<a href='$home$url' class='$cls'>$name</a>\n"
  }
}
html "<a id='hbbtn' href='#'>&#9776;</a>"
menulink $index_page Home {}
if {[anycap jor]} {
        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]} {
  menulink /timeline Timeline {}
}
        html "<a href='$home/login'>$login</a>\n"
      } else {
if {[hascap oh]} {
  menulink /dir?ci=tip Files desktoponly
}
        html "<a href='$home/login'>Login</a>\n"
      }
if {[hascap o]} {
  menulink  /brlist Branches desktoponly
  menulink  /taglist Tags wideonly
    </th1>
  </div>
</header>
<nav class="mainmenu" title="Main Menu">
  <th1>
    html "<a id='hbbtn' href='$home/sitemap' aria-label='Site Map'>&#9776;</a>"
    builtin_request_js hbmenu.js
}
if {[anycap 23456] || [anoncap 2] || [anoncap 3]} {
    foreach {name url expr class} $mainmenu {
      if {![capexpr $expr]} continue
  menulink /forum Forum wideonly
}
if {[hascap r]} {
  menulink /ticket Tickets wideonly
}
      if {[string match /* $url]} {
        if {[string match $url\[/?#\]* /$current_page/]} {
          set class "active $class"
        }
if {[hascap j]} {
  menulink /wiki Wiki wideonly
}
        set url $home$url
      }
if {[hascap s]} {
  menulink /setup Admin {}
} elseif {[hascap a]} {
  menulink /setup_ulist Users {}
}
</th1></div>
<div id='hbdrop'></div>
      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>

Deleted skins/default/js.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










































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
/*
** 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/
**
*******************************************************************************
**
** This file contains the JS code specific to the Fossil default skin.
** Currently, the only thing this does is handle clicks on its hamburger
** menu button.
*/
(function() {
  var hbButton = document.getElementById("hbbtn");
  if (!hbButton) return;   // no hamburger button
  if (!document.addEventListener) {
    // Turn the button into a link to the sitemap for incompatible browsers.
    hbButton.href = "$home/sitemap";
    return;
  }
  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
        }
        xhr.open("GET", "$home/sitemap?popup");   // note the TH1 substitution!
        xhr.responseType = "document";
        xhr.send();
      }
      else {
        showPanel();   // just show what we built above
      }
    }
  }
})();

Changes to 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
1
2
3
4
5
6
7



8
9
10
11
12
13
14
15
16

17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32







-
-
-









-
+

+
+
+
+
+
+
+







/* General settings for the entire page */
body {
  margin: 0ex 1ex;
  padding: 0px;
  background-color: #485D7B;
  font-family: sans-serif;
  color: white;
  -moz-text-size-adjust: none;
  -webkit-text-size-adjust: none;
  -mx-text-size-adjust: none;
}

/* The project logo in the upper left-hand corner of each page */
div.logo {
  display: table-cell;
  text-align: center;
  vertical-align: bottom;
  font-weight: bold;
  color: white;
  padding: 5 0 5 0em;
  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;
37
38
39
40
41
42
43
44
45
46
47
48
49

50
51
52
53
54
55
56

57
58
59
60
61
62
63
64
65
66












67
68
69
70
71
72
73
74
75
76
77

78
79
80
81
82
83
84

85
86
87
88
89
90
91
41
42
43
44
45
46
47

48
49
50
51

52
53
54
55
56
57
58

59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91

92
93
94
95
96
97
98

99
100
101
102
103
104
105
106







-




-
+






-
+










+
+
+
+
+
+
+
+
+
+
+
+










-
+






-
+







div.status {
  display: table-cell;
  text-align: right;
  vertical-align: bottom;
  color: white;
  font-size: 0.8em;
  font-weight: bold;
  min-width: 200px;
  white-space: nowrap;
}

/* The header across the top of the page */
div.header {
header {
  display: table;
  width: 100%;
}

/* The main menu bar that appears at the top of the page beneath
** the header */
div.mainmenu {
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;
}
div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited,
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;
}
div.mainmenu a:hover, div.submenu a:hover, div.sectionmenu>a.button:hover,
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 {
114
115
116
117
118
119
120
121

122
123
124
125
126
127
128
129
130
131
132
133
134
135

136
137
138
139
140
141
142
143







-
+







  margin: .2em 0 .2em 0;
  float: left;
  clear: left;
  white-space: nowrap;
}

/* The footer at the very bottom of the page */
div.footer {
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;
166
167
168
169
170
171
172
173

174
175



176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195










196
197
198
199
200
201
202
181
182
183
184
185
186
187

188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230







-
+


+
+
+




















+
+
+
+
+
+
+
+
+
+







  border: 0;
  cellpadding: 0;
  font-family: "courier new";
  border-spacing: 0px 2px;
  // border-collapse: collapse;
}

tr.timelineSelected {
.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;
}
217
218
219
220
221
222
223
224
























225
226

227
228
229
230
231
232
233
234
235
236
237
238
239
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277

278
279
280
281
282
283

284
285
286
287
288
289
290








+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+





-







  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.sbsdiff {
table.splitdiff {
  background-color: #485D7B;
  font-family: fixed, Dejavu Sans Mono, Monaco, Lucida Console, monospace;
  font-size: 8pt;
  border-collapse:collapse;
  white-space: pre;
  width: 98%;
  border: 1px #000 dashed;
  margin-left: auto;
  margin-right: auto;
}

/* format for the layout table, used for the captcha display */
table.captcha {
259
260
261
262
263
264
265








266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281

282
283
284
285
286
287
288

289
290
291
292
293
294
295



296
297

298
299
300

301
302
303
304

305
306


307

308
309

310
311
312

313
314

315
316
317

318
319
320
321








































322
323
324
325
326
327
328
329
330
331
332

333
334
335
336

337




338
339
340
341
342















310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339

340
341
342
343
344
345
346

347
348
349
350
351



352
353
354
355

356



357
358
359
360

361
362
363
364
365

366


367



368
369

370


371
372
373
374
375

376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425

426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456







+
+
+
+
+
+
+
+















-
+






-
+




-
-
-
+
+
+

-
+
-
-
-
+



-
+


+
+
-
+
-
-
+
-
-
-
+

-
+
-
-

+



-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+










-
+




+

+
+
+
+





+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

/* 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.sbsdiff {
div.splitdiff {
  font-family: monospace;
  font-size: smaller;
  white-space: pre;
}

/* context diff display */
div.udiff {
table.udiff {
  font-family: monospace;
  white-space: pre;
}

/* changes in a diff */
span.diffchng {
  background-color: rgb(170, 170, 140);
/* added code in a diff */
td.difftxt ins > ins, td.diffln ins {
  background-color: rgb(100, 200, 100);
}

td.difftxt ins {
/* added code in a diff */
span.diffadd {
  background-color: rgb(100, 200, 100);
  background-color: inherit;
}

/* deleted in a diff */
span.diffrm {
td.difftxt del > del, td.diffln del {
  background-color: rgb(230, 110, 110);
}
td.difftxt del {
  background-color: inherit;

}
/* suppressed lines in a diff */
span.diffhr {
tr.diffskip.jchunk {
  display: inline-block;
  margin: .5em 0 1em;
  color: rgb(150, 150, 140);
  background-color: #7EA2D9;
}

tr.diffskip > td.chunkctrl .jcbutton{
/* line numbers in a diff */
span.diffln {
  color: white;
  background-color: #485D7B;
}

.fileage tr:hover {
  background-color: #7EA2D9;
  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;
}

div.selectedText {
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;
}

Changes to skins/eagle/details.txt.

1
2
3
4

1
2
3
4
5




+
timeline-arrowheads:        1
timeline-circle-nodes:      0
timeline-color-graph-lines: 0
white-foreground:           1
pikchr-background:          0x485d7b

Changes to skins/eagle/footer.txt.

1

2
3
4
5
6
7
8

1
2
3
4
5
6
7
8
-
+







<div class="footer">
<footer>
  <th1>
  proc getTclVersion {} {
    if {[catch {tclEval info patchlevel} tclVersion] == 0} {
      return "<a href=\"https://www.tcl.tk/\">Tcl</a> version $tclVersion"
    }
    return ""
  }
17
18
19
20
21
22
23
24

17
18
19
20
21
22
23

24







-
+
  </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&amp;y=ci">$manifest_date</a>
</div>
</footer>

Changes to skins/eagle/header.txt.

1

2
3
4
5
6
7
8

1
2
3
4
5
6
7
8
-
+







<div class="header">
<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).
    ##
54
55
56
57
58
59
60


61





62
63
64
65
66
67
68
69
70
71
72
73
74
75

76
77
78
79
80
81
82







83

84
85
86
87
88




89
90

91
92
93
94
95


96
97
98

99
100
101

102
103
104
105
106
107
108
109
110
111
112
113

114
115

116
117
118

119
120


121
122
123
124
125
126
127
128
129
130
131
132



54
55
56
57
58
59
60
61
62

63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80

81
82






83
84
85
86
87
88
89
90
91
92




93
94
95
96
97

98


99


100
101



102



103












104


105



106


107
108












109
110
111







+
+
-
+
+
+
+
+













-
+

-
-
-
-
-
-
+
+
+
+
+
+
+

+

-
-
-
-
+
+
+
+

-
+
-
-

-
-
+
+
-
-
-
+
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
+
-
-
-
+
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
          set logourl $baseurl
        }
      } else {
        set logourl $baseurl
      }
      return $logourl
    }
    if {1} {
      # Link logo to the top of the current domain
    set logourl [getLogoUrl $baseurl]
      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>
</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;
  (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()+ '-' +
      f(d.getUTCMonth() + 1) + '-' +
      f(d.getUTCDate())      + ' ' +
      f(d.getUTCHours())     + ':' +
      f(d.getUTCMinutes());
      updateClock.fmt(d.getUTCMonth() + 1) + '-' +
      updateClock.fmt(d.getUTCDate())      + ' ' +
      updateClock.fmt(d.getUTCHours())     + ':' +
      updateClock.fmt(d.getUTCMinutes());
    setTimeout(updateClock,(60-d.getUTCSeconds())*1000);
  }
  })();
}
updateClock();
</script>
<div class="mainmenu">
<th1>
<nav class="mainmenu" title="Main Menu">
  <th1>
proc menulink {url name} {
  upvar home home
  html "<a href='$home$url'>$name</a>\n"
    html "<a id='hbbtn' href='$home/sitemap' aria-label='Site Map'>&#9776;</a>\n"
}
menulink $index_page Home
menulink /help Help
    builtin_request_js hbmenu.js
if {[anycap jor]} {
  menulink /timeline Timeline
}
if {[anoncap oh]} {
  menulink /dir?ci=tip Files
}
if {[anoncap o]} {
  menulink /brlist Branches
  menulink /taglist Tags
}
if {[anycap 23456] || [anoncap 2] || [anoncap 3]} {
  menulink /forum Forum
    foreach {name url expr class} $mainmenu {
}
if {[anoncap r]} {
      if {![capexpr $expr]} continue
  menulink /ticket Tickets
}
if {[anoncap j]} {
      if {[string match /* $url]} {set url $home$url}
  menulink /wiki Wiki
}
      html "<a href='$url' class='$class'>$name</a>\n"
    }
menulink /sitemap More...
if {[hascap s]} {
  menulink /setup Admin
} elseif {[hascap a]} {
  menulink /setup_ulist Users
}
if {[info exists login]} {
  menulink /login Logout
} else {
  menulink /login Login
}
</th1></div>
  </th1>
</nav>
<nav id="hbdrop" class='hbdrop' title="sitemap"></nav>

Deleted skins/enhanced1/css.txt.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150






















































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
/* General settings for the entire page */
body {
  margin: 0ex 1ex;
  padding: 0px;
  background-color: white;
  font-family: sans-serif;
  -moz-text-size-adjust: none;
  -webkit-text-size-adjust: none;
  -mx-text-size-adjust: none;
}

/* The project logo in the upper left-hand corner of each page */
div.logo {
  display: table-cell;
  text-align: center;
  vertical-align: bottom;
  font-weight: bold;
  color: #558195;
  min-width: 200px;
  white-space: nowrap;
}

/* The page title centered at the top of each page */
div.title {
  display: table-cell;
  font-size: 2em;
  font-weight: bold;
  text-align: center;
  padding: 0 0 0 1em;
  color: #558195;
  vertical-align: bottom;
  width: 100%;
}

/* The login status message in the top right-hand corner */
div.status {
  display: table-cell;
  text-align: right;
  vertical-align: bottom;
  color: #558195;
  font-size: 0.8em;
  font-weight: bold;
  min-width: 200px;
  white-space: nowrap;
}

/* The header across the top of the page */
div.header {
  display: table;
  width: 100%;
}

/* The main menu bar that appears at the top of the page beneath
** the header */
div.mainmenu {
  padding: 5px 10px 5px 10px;
  font-size: 0.9em;
  font-weight: bold;
  text-align: center;
  letter-spacing: 1px;
  background-color: #558195;
  border-top-left-radius: 8px;
  border-top-right-radius: 8px;
  color: white;
}

/* The submenu bar that *sometimes* appears below the main menu */
div.submenu, div.sectionmenu {
  padding: 3px 10px 3px 0px;
  font-size: 0.9em;
  text-align: center;
  background-color: #456878;
  color: white;
}
div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited,
div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited,
div.submenu label {
  padding: 3px 10px 3px 10px;
  color: white;
  text-decoration: none;
}
div.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 */
div.footer {
  clear: both;
  font-size: 0.8em;
  padding: 5px 10px 5px 10px;
  text-align: right;
  background-color: #558195;
  border-bottom-left-radius: 8px;
  border-bottom-right-radius: 8px;
  color: white;
}

/* Hyperlink colors in the footer */
div.footer a { color: white; }
div.footer a:link { color: white; }
div.footer a:visited { color: white; }
div.footer a:hover { background-color: white; color: #558195; }

/* verbatim blocks */
pre.verbatim {
  background-color: #f5f5f5;
  padding: 0.5em;
  white-space: pre-wrap;
}

/* The label/value pairs on (for example) the ci page */
table.label-value th {
  vertical-align: top;
  text-align: right;
  padding: 0.2ex 2ex;
}

Deleted skins/enhanced1/details.txt.

1
2
3
4




-
-
-
-
timeline-arrowheads:        1
timeline-circle-nodes:      0
timeline-color-graph-lines: 0
white-foreground:           0

Deleted skins/enhanced1/footer.txt.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
























-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
<div class="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&amp;y=ci">$manifest_date</a>
</div>

Deleted skins/enhanced1/header.txt.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131



































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
<div class="header">
  <div class="logo">
    <th1>
    ##
    ## NOTE: The purpose of this procedure is to take the base URL of the
    ##       Fossil project and return the root of the entire web site using
    ##       the same URI scheme as the base URL (e.g. http or https).
    ##
    proc getLogoUrl { baseurl } {
      set idx(first) [string first // $baseurl]
      if {$idx(first) != -1} {
        ##
        ## NOTE: Skip second slash.
        ##
        set idx(first+1) [expr {$idx(first) + 2}]
        ##
        ## NOTE: (part 1) The [string first] command does NOT actually
        ##       support the optional startIndex argument as specified
        ##       in the TH1 support manual; therefore, we fake it by
        ##       using the [string range] command and then adding the
        ##       necessary offset to the resulting index manually
        ##       (below).  In Tcl, we could use the following instead:
        ##
        ##       set idx(next) [string first / $baseurl $idx(first+1)]
        ##
        set idx(nextRange) [string range $baseurl $idx(first+1) end]
        set idx(next) [string first / $idx(nextRange)]
        if {$idx(next) != -1} {
          ##
          ## NOTE: (part 2) Add the necessary offset to the result of
          ##       the search for the next slash (i.e. the one after
          ##       the initial search for the two slashes).
          ##
          set idx(next) [expr {$idx(next) + $idx(first+1)}]
          ##
          ## NOTE: Back up one character from the next slash.
          ##
          set idx(next-1) [expr {$idx(next) - 1}]
          ##
          ## NOTE: Extract the URI scheme and host from the base URL.
          ##
          set scheme [string range $baseurl 0 $idx(first)]
          set host [string range $baseurl $idx(first+1) $idx(next-1)]
          ##
          ## NOTE: Try to stay in SSL mode if we are there now.
          ##
          if {[string compare $scheme http:/] == 0} {
            set scheme http://
          } else {
            set scheme https://
          }
          set logourl $scheme$host/
        } else {
          set logourl $baseurl
        }
      } else {
        set logourl $baseurl
      }
      return $logourl
    }
    set logourl [getLogoUrl $baseurl]
    </th1>
    <a href="$logourl">
      <img src="$logo_image_url" border="0" alt="$project_name">
    </a>
  </div>
  <div class="title">$<title></div>
  <div class="status"><th1>
     if {[info exists login]} {
       puts "Logged in as $login"
     } else {
       puts "Not logged in"
     }
  </th1></nobr><small><div id="clock"></div></small></div>
</div>
<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>
<div class="mainmenu">
<th1>
proc menulink {url name} {
  upvar home home
  html "<a href='$home$url'>$name</a>\n"
}
menulink $index_page Home
menulink /help Help
if {[anycap jor]} {
  menulink /timeline Timeline
}
if {[anoncap oh]} {
  menulink /dir?ci=tip Files
}
if {[anoncap o]} {
  menulink /brlist Branches
  menulink /taglist Tags
}
if {[anycap 23456] || [anoncap 2] || [anoncap 3]} {
  menulink /forum Forum
}
if {[anoncap r]} {
  menulink /ticket Tickets
}
if {[anoncap j]} {
  menulink /wiki Wiki
}
if {[hascap s]} {
  menulink /setup Admin
} elseif {[hascap a]} {
  menulink /setup_ulist Users
}
if {[info exists login]} {
  menulink /login Logout
} else {
  menulink /login Login
}
</th1></div>

Added skins/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'>&#9776;</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>

Changes to skins/khaki/css.txt.

1
2
3
4
5
6
7

8
9
10
11
12
13
14
15
16
1
2
3
4
5
6

7


8
9
10
11
12
13
14






-
+
-
-







/* General settings for the entire page */
body {
  margin: 0ex 0ex;
  padding: 0px;
  background-color: #fef3bc;
  font-family: sans-serif;
  -moz-text-size-adjust: none;
  text-size-adjust: none;
  -webkit-text-size-adjust: none;
  -mx-text-size-adjust: none;
}

/* The project logo in the upper left-hand corner of each page */
div.logo {
  display: inline;
  text-align: center;
  vertical-align: bottom;
41
42
43
44
45
46
47
48

49
50
51
52
53
54
55

56
57
58
59
60
61
62

63












64
65
66
67
68
69
70
71
72
73

74
75
76
77
78
79
80
81


82
83
84
85
86
87
88
89
90
91
92
93
94










95
96
97
98
99
100
101
39
40
41
42
43
44
45

46
47
48
49
50
51
52

53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83

84
85
86
87
88
89
90


91
92
93
94
95
96
97
98
99
100
101




102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118







-
+






-
+







+

+
+
+
+
+
+
+
+
+
+
+
+









-
+






-
-
+
+









-
-
-
-
+
+
+
+
+
+
+
+
+
+







  padding: 5px 5px 0 0;
  font-size: 0.8em;
  font-weight: bold;
  white-space: nowrap;
}

/* The header across the top of the page */
div.header {
header {
  display: table;
  width: 100%;
}

/* The main menu bar that appears at the top of the page beneath
** the header */
div.mainmenu {
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;
}
div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited,
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;
}
div.mainmenu a:hover, div.submenu a:hover, div.sectionmenu>a.button:hover,
div.submenu label:hover {
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 { color: #706532; }
div.content a:link { color: #706532; }
div.content a:visited { color: #704032; }
div.content a:hover { background-color: white; color: #706532; }
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;
114
115
116
117
118
119
120
121

122
123
124
125
126
127
128
129
130
131
132
133
134




135
136
137
138
139
140
141
142
143
144
145
146
147
148
















131
132
133
134
135
136
137

138
139
140
141
142
143
144
145
146
147




148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181







-
+









-
-
-
-
+
+
+
+














+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
  margin: .2em 0 .2em 0;
  float: left;
  clear: left;
  white-space: nowrap;
}

/* The footer at the very bottom of the page */
div.footer {
footer {
  font-size: 0.8em;
  margin-top: 12px;
  padding: 5px 10px 5px 10px;
  text-align: right;
  background-color: #a09048;
  color: white;
}

/* Hyperlink colors */
div.footer a { color: white; }
div.footer a:link { color: white; }
div.footer a:visited { color: white; }
div.footer a:hover { background-color: white; color: #558195; }
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;
}

Changes to skins/khaki/details.txt.

1
2
3


4
1


2
3
4

-
-
+
+

timeline-arrowheads:        1
timeline-circle-nodes:      0
timeline-color-graph-lines: 0
timeline-circle-nodes:      1
timeline-color-graph-lines: 1
white-foreground:           0

Changes to skins/khaki/footer.txt.

1

2
3


1
2

3
-
+

-
+
<div class="footer">
<footer>
Fossil $release_version $manifest_version $manifest_date
</div>
</footer>

Changes to skins/khaki/header.txt.

1

2
3
4
5
6
7
8
9
10
11
12
13
14
15











16
17
18
19



20
21
22

23
24
25
26
27
28
29
30
31
32
33
34


35
36
37
38
39
40
41
42
43
44
45




1
2
3
4
5










6
7
8
9
10
11
12
13
14
15
16




17
18
19



20












21
22











23
24
25
-
+




-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
<div class="header">
<header>
  <div class="title">$<title></div>
  <div class="status">
    <div class="logo">$<project_name></div><br/>
    <th1>
     if {[info exists login]} {
       puts "Logged in as $login"
     } else {
       puts "Not logged in"
     }
  </th1></div>
</div>
<div class="mainmenu">
<th1>
html "<a href='$home$index_page'>Home</a>\n"
      if {[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'>&#9776;</a>"
if {[anycap jor]} {
  html "<a href='$home/timeline'>Timeline</a>\n"
}
if {[anoncap oh]} {
    builtin_request_js hbmenu.js
    foreach {name url expr class} $mainmenu {
      if {![capexpr $expr]} continue
  html "<a href='$home/tree?ci=tip'>Files</a>\n"
}
if {[anoncap o]} {
      if {[string match /* $url]} {set url $home$url}
  html "<a href='$home/brlist'>Branches</a>\n"
  html "<a href='$home/taglist'>Tags</a>\n"
}
if {[anycap 23456] || [anoncap 2] || [anoncap 3]} {
  html "<a href='$home/forum'>Forum</a>\n"
}
if {[anoncap r]} {
  html "<a href='$home/ticket'>Tickets</a>\n"
}
if {[anoncap j]} {
  html "<a href='$home/wiki'>Wiki</a>\n"
}
      html "<a href='$url' class='$class'>$name</a>\n"
    }
if {[hascap s]} {
  html "<a href='$home/setup'>Admin</a>\n"
} elseif {[hascap a]} {
  html "<a href='$home/setup_ulist'>Users</a>\n"
}
if {[info exists login]} {
  html "<a href='$home/login'>Logout</a>\n"
} else {
  html "<a href='$home/login'>Login</a>\n"
}
</th1></div>
  </th1>
</nav>
<nav id="hbdrop" class='hbdrop' title="sitemap"></nav>

Changes to skins/original/css.txt.

12
13
14
15
16
17
18
19

20
21
22
23
24
25
26
12
13
14
15
16
17
18

19
20
21
22
23
24
25
26







-
+







/* The project logo in the upper left-hand corner of each page */
div.logo {
  display: table-cell;
  text-align: center;
  vertical-align: bottom;
  font-weight: bold;
  color: #558195;
  min-width: 200px;
  min-width: 50px;
  white-space: nowrap;
}

/* The page title centered at the top of each page */
div.title {
  display: table-cell;
  font-size: 2em;
36
37
38
39
40
41
42
43
44
45
46
47
48

49
50
51
52
53
54
55
56


57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75

76
77
78
79
80
81
82

83
84
85
86
87
88
89
36
37
38
39
40
41
42

43
44
45
46

47
48
49
50
51
52
53


54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73

74
75
76
77
78
79
80

81
82
83
84
85
86
87
88







-




-
+






-
-
+
+


















-
+






-
+







div.status {
  display: table-cell;
  text-align: right;
  vertical-align: bottom;
  color: #558195;
  font-size: 0.8em;
  font-weight: bold;
  min-width: 200px;
  white-space: nowrap;
}

/* The header across the top of the page */
div.header {
header {
  display: table;
  width: 100%;
}

/* The main menu bar that appears at the top of the page beneath
** the header */
div.mainmenu {
  padding: 5px 10px 5px 10px;
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;
}
div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited,
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;
}
div.mainmenu a:hover, div.submenu a:hover, div.sectionmenu>a.button:hover,
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 */
114
115
116
117
118
119
120
121

122
123
124
125
126
127
128
129
130
131
132
133
134
135
136




137
138
139
140
141
142
143
113
114
115
116
117
118
119

120
121
122
123
124
125
126
127
128
129
130
131




132
133
134
135
136
137
138
139
140
141
142







-
+











-
-
-
-
+
+
+
+







  margin: .2em 0 .2em 0;
  float: left;
  clear: left;
  white-space: nowrap;
}

/* The footer at the very bottom of the page */
div.footer {
footer {
  clear: both;
  font-size: 0.8em;
  padding: 5px 10px 5px 10px;
  text-align: right;
  background-color: #558195;
  border-bottom-left-radius: 8px;
  border-bottom-right-radius: 8px;
  color: white;
}

/* Hyperlink colors in the footer */
div.footer a { color: white; }
div.footer a:link { color: white; }
div.footer a:visited { color: white; }
div.footer a:hover { background-color: white; color: #558195; }
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;
}

Changes to skins/original/footer.txt.

1
2
3
4
5





























1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
<div class="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
</div>
<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&amp;y=ci">$manifest_date</a>
</footer>

Changes to 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




1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

64
65
66


67
68
69
70
71
72
73


74
75





76
77
78
79
80


81






82
83
84
85
86
87
88
89
90





91
92
93


94
95
96





97
98
99
100
101




102
103
104




105
106
107
-
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+

-
-
+
+





-
-
+
+
-
-
-
-
-
+
+
+
+
+
-
-
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
-
-
+
+
+
-
-
-
-
-
+
+
+
+
+
-
-
-
-
+
+
+
-
-
-
-
+
+
+
<div class="header">
<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" alt="logo" />
      <img src="$logo_image_url" border="0" alt="$project_name">
    </a>
  </div>
  <div class="title"><small>$<project_name></small><br />$<title></div>
  <div class="status"><th1>
  <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></div>
</div>
  </th1></nobr><small><div id="clock"></div></small></div>
</header>
<div class="mainmenu">
<th1>
html "<a href='$home$index_page'>Home</a>\n"
if {[anycap jor]} {
  html "<a href='$home/timeline'>Timeline</a>\n"
<th1>html "<script nonce='$nonce'>"</th1>
  function updateClock(){
    var e = document.getElementById("clock");
    if(e){
      var d = new Date();
}
if {[anoncap oh]} {
      function f(n) {
  html "<a href='$home/tree?ci=tip'>Files</a>\n"
}
if {[anoncap o]} {
  html "<a href='$home/brlist'>Branches</a>\n"
  html "<a href='$home/taglist'>Tags</a>\n"
}
        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);
    }
if {[anycap 23456] || [anoncap 2] || [anoncap 3]} {
  html "<a href='$home/forum'>Forum</a>\n"
}
if {[anoncap r]} {
  html "<a href='$home/ticket'>Tickets</a>\n"
  }
  updateClock();
</script>
}
if {[anoncap j]} {
<nav class="mainmenu" title="Main Menu">
  <th1>
    set sitemap 0
  html "<a href='$home/wiki'>Wiki</a>\n"
}
if {[hascap s]} {
  html "<a href='$home/setup'>Admin</a>\n"
} elseif {[hascap a]} {
    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}
  html "<a href='$home/setup_ulist'>Users</a>\n"
}
if {[info exists login]} {
  html "<a href='$home/login'>Logout</a>\n"
    }
    if {!$sitemap} {
      html "<a href='$home/sitemap'>...</a>"
} else {
  html "<a href='$home/login'>Login</a>\n"
}
</th1></div>
    }
  </th1>
</nav>

Changes to 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
1
2
3
4
5
6

7















8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

28

29
30
31
32

33
34
35
36
37
38
39

40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74


75
76
77
78
79
80
81
82
83
84

85
86
87
88
89
90
91
92
93
94
95
96
97
98
99






-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-




















-

-




-
+






-
+







+

+
+
+
+
+
+
+
+
+
+
+
+
+









+
+
+
-
-
+
+
+





+
+
-
+




+
+
+







/* General settings for the entire page */
body {
  margin: 0ex 1ex;
  padding: 0px;
  background-color: white;
  font-family: sans-serif;
  -moz-text-size-adjust: none;
  text-size-adjust: none;
  -webkit-text-size-adjust: none;
  -mx-text-size-adjust: none;
}

/* The project logo in the upper left-hand corner of each page */
div.logo {
  display: table-row;
  text-align: center;
  /* vertical-align: bottom;*/
  font-size: 2em;
  font-weight: bold;
  background-color: #707070;
  color: #ffffff;
  min-width: 200px;
  white-space: nowrap;
}

/* The page title centered at the top of each page */
div.title {
  display: table-cell;
  font-size: 1.5em;
  font-weight: bold;
  text-align: center;
  padding: 0 0 0 10px;
  color: #404040;
  vertical-align: bottom;
  width: 100%;
}

/* The login status message in the top right-hand corner */
div.status {
  display: table-cell;
  text-align: right;
  vertical-align: bottom;
  color: #404040;
  font-size: 0.8em;
  font-weight: bold;
  min-width: 200px;
  white-space: nowrap;
}

/* The header across the top of the page */
div.header {
header {
  display: table;
  width: 100%;
}

/* The main menu bar that appears at the top of the page beneath
** the header */
div.mainmenu {
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.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited,
div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited,
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.mainmenu a:hover, div.submenu a:hover, div.sectionmenu>a.button: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 */
123
124
125
126
127
128
129
130

131
132
133
134
135
136
137
129
130
131
132
133
134
135

136
137
138
139
140
141
142
143







-
+







  margin: .2em 0 .2em 0;
  float: left;
  clear: left;
  white-space: nowrap;
}

/* The footer at the very bottom of the page */
div.footer {
footer {
  font-size: 0.8em;
  margin-top: 12px;
  padding: 5px 10px 5px 10px;
  text-align: right;
  background-color: #404040;
  color: white;
}

Changes to skins/plain_gray/footer.txt.

1

2
3


1
2

3
-
+

-
+
<div class="footer">
<footer>
Fossil $release_version $manifest_version $manifest_date
</div>
</footer>

Changes to skins/plain_gray/header.txt.

1
2


3
4
5
6
7
8
9
10
11
12



13
14
15
16
17
18
19
20
21
22

23
24
25
26
27



28
29
30
31
32



33
34
35
36
37
38
39
40
41
42
43





1
2










3
4
5










6





7
8
9





10
11
12











13
14
15
-
-
+
+
-
-
-
-
-
-
-
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
+
+
+
-
-
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
<div class="header">
  <div class="title"><small>$<project_name></small><br />$<title></div>
<header>
  <div class="title">$<project_name>: $<title></div>
  <div class="status"><th1>
     if {[info exists login]} {
       puts "Logged in as $login"
     } else {
       puts "Not logged in"
     }
  </th1></div>
</div>
<div class="mainmenu">
<th1>
</header>
<nav class="mainmenu" title="Main Menu">
  <th1>
html "<a href='$home$index_page'>Home</a>\n"
if {[anycap jor]} {
  html "<a href='$home/timeline'>Timeline</a>\n"
}
if {[anoncap oh]} {
  html "<a href='$home/tree?ci=tip'>Files</a>\n"
}
if {[anoncap o]} {
  html "<a href='$home/brlist'>Branches</a>\n"
  html "<a href='$home/taglist'>Tags</a>\n"
    html "<a id='hbbtn' href='$home/sitemap' aria-label='Site Map'>&#9776;</a>"
}
if {[anycap 23456] || [anoncap 2] || [anoncap 3]} {
  html "<a href='$home/forum'>Forum</a>\n"
}
if {[anoncap r]} {
    builtin_request_js hbmenu.js
    foreach {name url expr class} $mainmenu {
      if {![capexpr $expr]} continue
  html "<a href='$home/ticket'>Tickets</a>\n"
}
if {[anoncap j]} {
  html "<a href='$home/wiki'>Wiki</a>\n"
}
      if {[string match /* $url]} {set url $home$url}
      html "<a href='$url' class='$class'>$name</a>\n"
    }
if {[hascap s]} {
  html "<a href='$home/setup'>Admin</a>\n"
} elseif {[hascap a]} {
  html "<a href='$home/setup_ulist'>Users</a>\n"
}
if {[info exists login]} {
  html "<a href='$home/login'>Logout</a>\n"
} else {
  html "<a href='$home/login'>Login</a>\n"
}
</th1></div>
  </th1>
</nav>
<nav id="hbdrop" class='hbdrop' title="sitemap"></nav>

Deleted skins/rounded1/css.txt.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197





































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
/* General settings for the entire page */
html {
  min-height: 100%;
}
body {
  margin: 0ex 1ex;
  padding: 0px;
  background-color: white;
  color: #333;
  font-family: Verdana, sans-serif;
  font-size: 0.8em;
  -moz-text-size-adjust: none;
  -webkit-text-size-adjust: none;
  -mx-text-size-adjust: none;
}

/* The project logo in the upper left-hand corner of each page */
div.logo {
  display: table-cell;
  text-align: right;
  vertical-align: bottom;
  font-weight: normal;
  white-space: nowrap;
}

/* Widths */
div.header, div.mainmenu, div.submenu, div.content, div.footer {
  max-width: 900px;
  margin: auto;
  padding: 3px 20px 3px 20px;
  clear: both;
}

/* The page title at the top of each page */
div.title {
  display: table-cell;
  padding-left: 10px;
  font-size: 2em;
  margin: 10px 0 10px -20px;
  vertical-align: bottom;
  text-align: left;
  width: 80%;
  font-family: Verdana, sans-serif;
  font-weight: bold;
  color: #558195;
  text-shadow: 0px 2px 2px #999999;
}

/* The login status message in the top right-hand corner */
div.status {
  display: table-cell;
  text-align: right;
  vertical-align: bottom;
  color: #333;
  margin-right: -20px;
  white-space: nowrap;
}

/* The main menu bar that appears at the top of the page beneath
 ** the header */
div.mainmenu {
  text-align: center;
  color: white;
  border-top-left-radius: 5px;
  border-top-right-radius: 5px;
  vertical-align: middle;
  padding-top: 8px;
  padding-bottom: 8px;
  background-color: #446979;
  box-shadow: 0px 3px 4px #333333;
}

/* The submenu bar that *sometimes* appears below the main menu */
div.submenu {
  padding-top:10px;
  padding-bottom:0;
  text-align: right;
  color: #000;
  background-color: #fff;
  height: 1.5em;
  vertical-align:middle;
  box-shadow: 0px 3px 4px #999;
}
div.mainmenu a, div.mainmenu a:visited {
  padding: 3px 10px 3px 10px;
  color: white;
  text-decoration: none;
}
div.submenu a, div.submenu a:visited, a.button, div.submenu label,
div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited {
  padding: 2px 8px;
  color: #000;
  font-family: Arial;
  text-decoration: none;
  margin:auto;
  border-radius: 5px;
  background-color: #e0e0e0;
  text-shadow: 0px -1px 0px #eee;
  border: 1px solid #000;
}

div.mainmenu a:hover {
  color: #000;
  background-color: white;
}

div.submenu a:hover, div.sectionmenu>a.button:hover, div.submenu label:hover {
  background-color: #c0c0c0;
}

/* All page content from the bottom of the menu or submenu down to
 ** the footer */
div.content {
  background-color: #fff;
  box-shadow: 0px 3px 4px #999;
  border-bottom-right-radius: 5px;
  border-bottom-left-radius: 5px;
  padding-bottom: 1em;
  min-height:40%;
}


/* Some pages have section dividers */
div.section {
  margin-bottom: 0.5em;
  margin-top: 1em;
  margin-right: auto;
  padding: 1px 1px 1px 1px;
  font-size: 1.2em;
  font-weight: bold;
  text-align: center;
  color: white;
  border-radius: 5px;
  background-color: #446979;
  box-shadow: 0px 3px 4px #333333;
  white-space: nowrap;
}

/* The "Date" that occurs on the left hand side of timelines */
div.divider {
  font-size: 1.2em;
  font-family: Georgia, serif;
  font-weight: bold;
  margin-top: 1em;
  white-space: nowrap;
}

/* The footer at the very bottom of the page */
div.footer {
  font-size: 0.9em;
  text-align: right;
  margin-bottom: 1em;
  color: #666;
}

/* Hyperlink colors in the footer */
div.footer a { color: white; }
div.footer a:link { color: white; }
div.footer a:visited { color: white; }
div.footer a:hover { background-color: white; color: #558195; }

/* <verbatim> blocks */
pre.verbatim, blockquote pre {
  font-family: Dejavu Sans Mono, Monaco, Lucida Console, monospace;
  background-color: #f3f3f3;
  padding: 0.5em;
  white-space: pre-wrap;
}

blockquote pre {
  border: 1px #000 dashed;
}

/* The label/value pairs on (for example) the ci page */
table.label-value th {
  vertical-align: top;
  text-align: right;
  padding: 0.2ex 2ex;
}

table.report tr th {
  padding: 3px 5px;
  text-transform: capitalize;
  cursor: pointer;
}

table.report tr td {
  padding: 3px 5px;
}

textarea {
  font-size: 1em;
}

.fullsize-text {
  font-size: 1.25em;
}

Deleted skins/rounded1/details.txt.

1
2
3
4




-
-
-
-
timeline-arrowheads:        1
timeline-circle-nodes:      1
timeline-color-graph-lines: 0
white-foreground:           0

Deleted skins/rounded1/footer.txt.

1
2
3



-
-
-
<div class="footer">
Fossil $release_version $manifest_version $manifest_date
</div>

Deleted skins/rounded1/header.txt.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47















































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
<div class="header">
  <div class="logo">
    <img src="$logo_image_url" alt="logo">
    <br />$<project_name>
  </div>
  <div class="title">$<title></div>
  <div class="status"><th1>
     if {[info exists login]} {
       puts "Logged in as $login"
     } else {
       puts "Not logged in"
     }
  </th1></div>
</div>
<div class="mainmenu">
<th1>
html "<a href='$home$index_page'>Home</a>\n"
if {[anycap jor]} {
  html "<a href='$home/timeline'>Timeline</a>\n"
}
if {[anoncap oh]} {
  html "<a href='$home/tree?ci=tip'>Files</a>\n"
}
if {[anoncap o]} {
  html "<a href='$home/brlist'>Branches</a>\n"
  html "<a href='$home/taglist'>Tags</a>\n"
}
if {[anycap 23456] || [anoncap 2] || [anoncap 3]} {
  html "<a href='$home/forum'>Forum</a>\n"
}
if {[anoncap r]} {
  html "<a href='$home/ticket'>Tickets</a>\n"
}
if {[anoncap j]} {
  html "<a href='$home/wiki'>Wiki</a>\n"
}
if {[hascap s]} {
  html "<a href='$home/setup'>Admin</a>\n"
} elseif {[hascap a]} {
  html "<a href='$home/setup_ulist'>Users</a>\n"
}
if {[info exists login]} {
  html "<a href='$home/login'>Logout</a>\n"
} else {
  html "<a href='$home/login'>Login</a>\n"
}
</th1></div>

Changes to skins/xekri/css.txt.

20
21
22
23
24
25
26
27

28
29
30
31
32
33

34
35
36
37
38
39
40
20
21
22
23
24
25
26

27


28
29
30

31
32
33
34
35
36
37
38







-
+
-
-



-
+







  font-size: 1em;
  min-height: 100%;
}

body {
  margin: 0;
  padding: 0;
  -moz-text-size-adjust: none;
  text-size-adjust: none;
  -ms-text-size-adjust: none;
  -webkit-text-size-adjust: none;
}

a {
  color: #07e;
  color: #40a0ff;
}

a:hover {
  font-weight: bold;
}

blockquote pre {
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82

83
84
85
86
87
88
89
90
91
92
93
94

95










96
97
98
99
100
101
102


103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120

121
122
123
124
125
126

127
128
129
130
131
132
133
134

135
136
137
138
139
140
141
142

143
144
145
146
147
148
149
150
151
152

153
154
155
156
157
158
159
160
161
162
163

164
165
166
167
168
169

170
171
172
173
174
175
176
59
60
61
62
63
64
65









66
67
68
69
70

71
72
73
74
75
76
77
78
79
80
81
82

83
84
85
86
87
88
89
90
91
92
93
94
95
96
97

98


99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117

118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141

142
143
144
145
146
147
148
149
150
151

152
153
154
155
156
157
158
159
160
161
162

163
164
165
166
167
168

169
170
171
172
173
174
175
176







-
-
-
-
-
-
-
-
-





-
+











-
+

+
+
+
+
+
+
+
+
+
+



-

-
-
+
+

















-
+






+








+







-
+









-
+










-
+





-
+







h2 {
  font-size: 1.5rem;
}

h3 {
  font-size: 1.25rem;
}

span[style^=background-color] {
  color: #000;
}

td[style^=background-color] {
  color: #000;
}


/**************************************
 * Main Area
 */

div.header, div.mainmenu, div.submenu, div.content, div.footer {
header, nav.mainmenu, div.submenu, div.content, footer {
  clear: both;
  margin: 0 auto;
  max-width: 90%;
  padding: 0.25rem 1rem;
}


/**************************************
 * Main Area: Header
 */

div.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 {
  float: left;
  padding: 0;
  box-shadow: 3px 3px 1px #000;
  margin: 0 6px 6px 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: #07e;
  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
 */

div.mainmenu, div.submenu {
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;
}

div.mainmenu {
nav.mainmenu {
  padding-top: 0.33rem;
  padding-bottom: 0.25rem;
}

div.submenu {
  border-top: 1px solid #0a0;
  border-radius: 0;
  display: block;
}

div.mainmenu a, div.submenu a, div.submenu label {
nav.mainmenu a, div.submenu a, div.submenu label {
  color: #000;
  padding: 0 0.75rem;
  text-decoration: none;
}

div.mainmenu a:hover, div.submenu a:hover, div.submenu label:hover {
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;
212
213
214
215
216
217
218
219

220
221
222
223
224
225
226
227
228

229
230
231
232
233

234
235
236

237
238
239
240

241
242
243
244

245
246
247
248

249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268







269
270


271
272
273
274

275
276
277

278
279
280
281
282


283
284
285



286
287
288
289
290
291
292
293

294
295
296
297
298
299

300
301

302
303
304
305

306
307
308
309
310
311

312
313
314
315
316
317
318

319
320
321
322
323
324
325
326
327
328
329

330
331
332

333
334
335
336
337
338
339
340
341

342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
212
213
214
215
216
217
218

219
220
221
222
223
224
225
226
227

228
229
230
231
232

233
234
235

236
237
238
239

240
241
242
243

244
245
246
247

248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276

277
278
279
280
281

282



283

284
285
286

287
288
289
290
291
292
293
294
295
296
297
298
299
300
301

302
303
304

305
306

307
308

309
310
311
312

313
314
315
316
317
318

319
320
321
322
323
324
325

326
327
328
329
330
331






332

333

334
335
336
337
338
339
340
341
342

343
344
345
346






347
348
349
350
351
352
353







-
+








-
+




-
+


-
+



-
+



-
+



-
+




















+
+
+
+
+
+
+

-
+
+



-
+
-
-
-
+
-



-
+
+



+
+
+







-
+


-


-
+

-
+



-
+





-
+






-
+





-
-
-
-
-
-
+
-

-
+








-
+



-
-
-
-
-
-







  stroke: white;
}

/**************************************
 * Main Area: Footer
 */

div.footer {
footer {
  color: #ee0;
  font-size: 0.75rem;
  padding: 0;
  text-align: right;
  width: 75%;
}


div.footer div {
footer div {
  background-color: #222;
  box-shadow: 3px 3px 1px #000;
  border-radius: 0 0 1rem 1rem;
  margin: 0 0 10px 0;
  padding: 0.5rem 0.75rem;
  padding: 0.25rem 0.75rem;
}

div.footer div.page-time {
footer div.page-time {
  float: left;
}

div.footer div.fossil-info {
footer div.fossil-info {
  float: right;
}

div.footer a, div.footer a:link, div.footer a:visited {
footer a, footer a:link, footer a:visited {
  color: #ee0;
}

div.footer a:hover {
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 */
span.diffadd {
td.diffln ins,
td.difftxt ins > ins {
  background-color: #7f7;
  color: #000;
}

td.difftxt ins {
/* Code Changed */
span.diffchng {
  background-color: #77f;
  background-color: inherit;
  color: #000;
}

/* Code Deleted */
span.diffrm {
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.sbsdiffcols {
table.splitdiff {
  border-spacing: 0;
  font-size: 0.85rem;
  width: 90%;
}

table.sbsdiffcols pre {
table.splitdiff pre {
  border: 0;
  margin: 0;
  margin: 0 0.5em;
  padding: 0;
}

table.sbsdiffcols td {
table.splitdiff td {
  padding: 0;
  vertical-align: top;
}

/* line number column */
div.difflncol {
td.diffln {
  color: #ee0;
  padding-right: 0.75em;
  text-align: right;
}

/* diff text column */
div.difftxtcol {
td.difftxt {
  background-color: #111;
  overflow-x: auto;
  width: 45em;
}

/* suppressed lines */
span.diffhr {
  display: inline-block;
  margin-bottom: 0.75em;
  color: #ff0;
}


/* diff marker column */
div.diffmkrcol {
td.diffsep {
  padding: 0 0.5em;
}


/**************************************
 * Diffs : Unified
 */

pre.udiff {
table.udiff pre {
  background-color: #111;
}

/* line numbers */
span.diffln {
  background-color: #222;
  color: #ee0;
}


/**************************************
 * File List : Flat
 */

table.browser {
  width: 100%;
506
507
508
509
510
511
512

513




514
515
516
517
518
519
520
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521







+

+
+
+
+









/**************************************
 * 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 {
561
562
563
564
565
566
567
568

569
570
571
572
573
574
575
562
563
564
565
566
567
568

569
570
571
572
573
574
575
576







-
+







  margin: 1.2rem auto 0.75rem auto;
  padding: 0.2rem;
  text-align: center;
}

div.sectionmenu {
  border-radius: 0 0 3rem 3rem;
  margin-top: -0.75rem;
  margin-top: auto;
  width: 75%;
}

div.sectionmenu > a:link, div.sectionmenu > a:visited {
  color: #000;
  text-decoration: none;
}
693
694
695
696
697
698
699





700
701
702
703
704
705
706
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712







+
+
+
+
+








/* 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;
}

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
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







+
+




















+
+
+
+
+
+
+







}

.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  {
984
985
986
987
988
989
990
991

992
993
994
995

996
997
998
999
1000
1001
1002
999
1000
1001
1002
1003
1004
1005

1006
1007
1008
1009

1010
1011
1012
1013
1014
1015
1016
1017







-
+



-
+









/**************************************
 * Did not encounter these
 */

/* selected lines of text within a linenumbered artifact display */
div.selectedText {
table.numbered-lines td.line-numbers span.selected-line {
  font-weight: bold;
  color: #00f;
  background-color: #d5d5ff;
  border: 1px #00f solid;
  border-color: #00f;
}

/* format for missing privileges note on user setup page */
p.missingPriv {
  color: #00f;
}

1058
1059
1060
1061
1062
1063
1064
1065

1066
1067
1068
1069

1070
1071
1072
1073
1074
1075
1076
1073
1074
1075
1076
1077
1078
1079

1080
1081
1082
1083

1084
1085
1086
1087
1088
1089
1090
1091







-
+



-
+







/* format for report configuration errors */
blockquote.reportError {
  color: #f00;
  font-weight: bold;
}
/* format for artifact lines, no longer shunned */
p.noMoreShun {
  color: #00f;
  color: yellow;
}
/* format for artifact lines being shunned */
p.shunned {
  color: #00f;
  color: yellow;
}
/* a broken hyperlink */
span.brokenlink {
  color: #f00;
}
/* List of files in a timeline */
ul.filelist {
1102
1103
1104
1105
1106
1107
1108

















































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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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;
}

Changes to skins/xekri/details.txt.

1
2
3
4

1
2
3

4



-
+
timeline-arrowheads:        1
timeline-circle-nodes:      0
timeline-color-graph-lines: 1
white-foreground:           0
white-foreground:           1

Changes to skins/xekri/footer.txt.

1
2
3
4
5
6
7
8
9








1








2
3
4
5
6
7
8
9

-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
</div>
<div class="footer">
<div class="page-time">
Generated in <th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s
</div>
<div class="fossil-info">
Fossil v$release_version $manifest_version
</div>
</div>
<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>

Changes to skins/xekri/header.txt.

1

2
3
4
5
6
7
8

1
2
3
4
5
6
7
8
-
+







<div class="header">
<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).
    ##
54
55
56
57
58
59
60


61





62
63
64
65
66
67
68
69
70
71
72
73
74
75










76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
















93
94
95


96
97
98
99
100
101
102


103
104
105
106

107
108
109

110
111
112

113
114
115


116
117
118
119




120
121
122


123
124
125

126
127
128
129
130

131
132
133
134
135
136


54
55
56
57
58
59
60
61
62

63
64
65
66
67
68
69
70
71
72
73








74
75
76
77
78
79
80
81
82
83
84
















85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101


102
103







104
105




106



107



108



109
110




111
112
113
114



115
116



117





118






119
120







+
+
-
+
+
+
+
+






-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
+
+
-
-
-
-
-
-
-
+
+
-
-
-
-
+
-
-
-
+
-
-
-
+
-
-
-
+
+
-
-
-
-
+
+
+
+
-
-
-
+
+
-
-
-
+
-
-
-
-
-
+
-
-
-
-
-
-
+
+
          set logourl $baseurl
        }
      } else {
        set logourl $baseurl
      }
      return $logourl
    }
    if {1} {
      # Link logo to the top of the current domain
    set logourl [getLogoUrl $baseurl]
      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>
</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();
  function updateClock(){
    var e = document.getElementById("clock");
    if(e){
      var d = new Date();
      function f(n) {
        return n < 10 ? '0' + n : n;
      }
      e.innerHTML = d.getUTCFullYear()+ '-' +
        f(d.getUTCMonth() + 1) + '-' +
        f(d.getUTCDate())      + ' ' +
        f(d.getUTCHours())     + ':' +
        f(d.getUTCMinutes());
      setTimeout(updateClock,(60-d.getUTCSeconds())*1000);
    }
  }
  updateClock();
</script>
<div class="mainmenu">
<th1>
<nav class="mainmenu" title="Main Menu">
  <th1>
proc menulink {url name} {
  upvar current_page current
  upvar home home
  if {[string range $url 0 [string length $current]] eq "/$current"} {
    html "<a href='$home$url' class='active'>$name</a>\n"
  } else {
    html "<a href='$home$url'>$name</a>\n"
    set sitemap 0
    foreach {name url expr class} $mainmenu {
  }
}
menulink $index_page Home
if {[anycap jor]} {
      if {![capexpr $expr]} continue
  menulink /timeline Timeline
}
if {[anoncap oh]} {
      if {[string match /* $url]} {
  menulink /dir?ci=tip Files
}
if {[anoncap o]} {
        if {[string match $url\[/?#\]* /$current_page/]} {
  menulink  /brlist Branches
  menulink  /taglist Tags
}
          set class "active $class"
        }
if {[anycap 23456] || [anoncap 2] || [anoncap 3]} {
  menulink /forum Forum
}
if {[anoncap r]} {
        set url $home$url
      }
      html "<a href='$url' class='$class'>$name</a>\n"
      if {[string match */sitemap $url]} {set sitemap 1}
  menulink /ticket Tickets
}
if {[anoncap j]} {
    }
    if {!$sitemap} {
  menulink /wiki Wiki
}
  menulink /sitemap More...
      html "<a href='$home/sitemap'>...</a>\n"
if {[hascap s]} {
  menulink /setup Admin
} elseif {[hascap a]} {
  menulink /setup_ulist Users
}
    }
if {[info exists login]} {
  menulink /login Logout
} else {
  menulink /login Login
}
</th1></div>
  </th1>
</nav>

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.

21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

47
48
49
50
51
52
53
54

55
56
57
58
59
60
61
62
63
64
65
66
67

68
69
70
71
72
73
74
21
22
23
24
25
26
27



















28
29
30
31
32
33
34
35

36
37
38
39
40
41
42
43
44
45
46
47
48

49
50
51
52
53
54
55
56







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+







-
+












-
+







#include "config.h"
#include "add.h"
#include <assert.h>
#include <dirent.h>
#include "cygsup.h"

/*
** WARNING: For Fossil version x.x this value was always zero.  For Fossil-NG
**          it will probably always be one.  When this value is zero,
**          files in the checkout will not be moved by the "mv" command and
**          files in the checkout will not be removed by the "rm" command.
**
**          If the FOSSIL_ENABLE_LEGACY_MV_RM compile-time option is used,
**          the "mv-rm-files" setting will be consulted instead of using
**          this value.
**
**          To retain the Fossil version 2.x behavior when using Fossil-NG
**          the FOSSIL_ENABLE_LEGACY_MV_RM compile-time option must be used
**          -AND- the "mv-rm-files" setting must be set to zero.
*/
#ifndef FOSSIL_MV_RM_FILE
#define FOSSIL_MV_RM_FILE                        (0)
#endif

/*
** This routine returns the names of files in a working checkout that
** 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-checkout database file and
  /* 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 checkout database is
     /* 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",
  };
83
84
85
86
87
88
89
90

91
92
93
94
95
96
97
65
66
67
68
69
70
71

72
73
74
75
76
77
78
79







-
+







    { "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 checkout.
  ** 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;

172
173
174
175
176
177
178

179
180
181
182
183
184
185
186
187
188
189
190





191
192
193
194





195
196
197

198
199
200
201
202
203
204
205
206
207
208
209



210
211
212
213
214
215
216
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178




179
180
181
182
183
184
185

186
187
188
189
190
191
192
193
194
195
196
197

198
199
200
201
202
203
204
205
206
207







+












+
+
+
+
+
-
-
-
-
+
+
+
+
+


-
+











-
+
+
+







**
** 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)"
      "VALUES(%d,0,0,0,%Q,%d,%d)",
      vid, zPath, isExe, file_islink(0));
      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() ){
  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.
** 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 */
224
225
226
227
228
229
230
231









232
233
234



235
236
237
238





239
240
241
242
243
244



























































































245
246
247
248
249
250
251
252

253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275


276
277
278
279
280












281




282

283
284
285
286

287
288
289
290
291
292










293
294
295
296
297

298
299
300
301
302
303
304
215
216
217
218
219
220
221

222
223
224
225
226
227
228
229
230
231
232
233
234
235
236




237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345

346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366



367
368
369




370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386

387
388
389
390

391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420







-
+
+
+
+
+
+
+
+
+



+
+
+
-
-
-
-
+
+
+
+
+






+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







-
+




















-
-
-
+
+

-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
-
+



-
+






+
+
+
+
+
+
+
+
+
+





+







    zRepo = blob_str(&repoName);
  }
  if( filenames_are_case_sensitive() ){
    xCmp = fossil_strcmp;
  }else{
    xCmp = fossil_stricmp;
  }
  db_prepare(&loop, "SELECT pathname FROM sfile ORDER BY pathname");
  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;
      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 checkout at the next commit.
** 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 (".")
**    --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 list of glob patterns.
**    --clean <CSG>           Also ignore files matching patterns from
**                            the comma separated list of glob patterns.
**    --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
** See also: [[addremove]], [[rm]]
*/
void add_cmd(void){
  int i;                     /* Loop counter */
  int vid;                   /* Currently checked out version */
  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);
315
316
317
318
319
320
321
322

323
324
325

326
327
328
329
330
331
332
333

334
335
336
337

338
339
340
341
342
343

344
345
346
347
348
349
350
351
352
353
354
355
356

357
358
359
360
361
362




























363
364
365
366
367
368
369
431
432
433
434
435
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







-
+


-
+


-




-
+



-
+






+













+






+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







  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;
    Blob fullName = empty_blob;

    /* file_tree_name() throws a fatal error if g.argv[i] is outside of the
    ** checkout. */
    ** 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);
      vfile_scan(&fullName, nRoot-1, scanFlags, pClean, pIgnore, RepoFILE);
    }else if( isDir==0 ){
      fossil_warning("not found: %s", zName);
    }else{
      char *zTreeName = &zName[nRoot];
      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
383
384
385
386
387
388
389
390

391
392
393
394
395
396
397
528
529
530
531
532
533
534

535
536
537
538
539
540
541
542







-
+







  }
  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 checkout, using the file names
** 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.
434
435
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
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







-
+

-
-
+
+

+
+
+
+
+
+
+

-
+




-
+




+
-
+
+
+
+
+
+
+















-

-
-
-







** 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 checkout.
**   --soft                  Skip removing files from the check-out.
**                           This supersedes the --hard option.
**   --hard                  Remove files from the checkout.
**   --case-sensitive <BOOL> Override the case-sensitive setting.
**   --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
** See also: [[addremove]], [[add]]
*/
void delete_cmd(void){
  int i;
  int removeFiles;
  int dryRunFlag;
  int dryRunFlag = find_option("dry-run","n",0)!=0;
  int softFlag;
  int hardFlag;
  Stmt loop;

  if(0!=find_option("reset",0,0)){
  dryRunFlag = find_option("dry-run","n",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{
#if FOSSIL_ENABLE_LEGACY_MV_RM
    removeFiles = db_get_boolean("mv-rm-files",0);
#else
    removeFiles = FOSSIL_MV_RM_FILE;
#endif
  }
  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;

529
530
531
532
533
534
535
536

537
538
539
540
541
542
543
684
685
686
687
688
689
690

691
692
693
694
695
696
697
698







-
+







**
** 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
** 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 ){
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
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







-
-
+
+

-
+

-
+

-
+

-
+

-
+
















-
+

-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+



-
-
-
+
+
+












+
+
+
+
+
+
+
+
+
+
+
+
+








-
+







}

/*
** COMMAND: addremove
**
** Usage: %fossil addremove ?OPTIONS?
**
** Do all necessary "add" and "rm" commands to synchronize the repository
** with the content of the working checkout:
** Do all necessary "[[add]]" and "[[rm]]" commands to synchronize the
** repository with the content of the working check-out:
**
**  *  All files in the checkout but not in the repository (that is,
**  *  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.
**     if by the "[[add]]" command.
**
**  *  All files in the repository but missing from the checkout (that is,
**  *  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.
**     removed as if by the "[[rm]]" command.
**
** The command does not "commit".  You must run the "commit" separately
** 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.
**   --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 list of glob patterns.
**   --clean <CSG>           Also ignore files matching patterns from
**                           the comma separated list of glob patterns.
**   -n|--dry-run            If given, display instead of run actions.
**   --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
** See also: [[add]], [[rm]]
*/
void addremove_cmd(void){
  Blob path;
  const char *zCleanFlag = find_option("clean",0,1);
  const char *zIgnoreFlag = find_option("ignore",0,1);
  unsigned scanFlags = find_option("dotfiles",0,0)!=0 ? SCAN_ALL : 0;
  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 checkout, no arguments supported.",
        "%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 ){
675
676
677
678
679
680
681
682

683
684
685
686
687
688
689
851
852
853
854
855
856
857

858
859
860
861
862
863
864
865







-
+







  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);
  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"
704
705
706
707
708
709
710


711

712
713
714
715
716
717
718
719
720
721
880
881
882
883
884
885
886
887
888

889
890
891

892
893
894
895
896
897
898







+
+
-
+


-







      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(
776
777
778
779
780
781
782
783

784
785
786
787
788
789
790
953
954
955
956
957
958
959

960
961
962
963
964
965
966
967







-
+







    db_multi_exec("INSERT INTO fmove VALUES('%q','%q');", zOld, zNew);
  }
  blob_reset(&fullNewName);
  blob_reset(&fullOldName);
}

/*
** This function moves files within the checkout, using the file names
** 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.
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
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







-
+












-
-
-
-
-
+
+
+
+
+

-
+







**    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/check-in.
** 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 checkout.
**                           This supersedes the --hard option.
**   --hard                  Move files within the checkout.
**   --case-sensitive <BOOL> Override the case-sensitive setting.
**   -n|--dry-run            If given, display instead of run actions.
**   --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
** See also: [[changes]], [[status]]
*/
void mv_cmd(void){
  int i;
  int vid;
  int moveFiles;
  int dryRunFlag;
  int softFlag;
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
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







-
+













-

-
-
-







  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 checkout in which to rename files");
    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{
#if FOSSIL_ENABLE_LEGACY_MV_RM
    moveFiles = db_get_boolean("mv-rm-files",0);
#else
    moveFiles = FOSSIL_MV_RM_FILE;
#endif
  }
  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);"
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
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







-
+










-
+




















+











      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))"
         "  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( destType==1 ){
        }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();
}

Changes to src/alerts.c.

11
12
13
14
15
16
17
18

19
20
21
22
23
24

25
26
27
28
29
30
31
11
12
13
14
15
16
17

18
19
20
21
22
23

24
25
26
27
28
29
30
31







-
+





-
+







**
** Author contact information:
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*******************************************************************************
**
** Logic for email notification, also known as "alerts".
** 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
44
45
46
47
48
49
50




51
52

53
54
55
56
57
58
59
60
61
62
63

64
65
66
67
68


69
70
71
72

73
74
75
76
77
78
79
80
81
82
83
84
85
86


87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104















105
106
107
108
109
110
111
112
113
114
115

116
117
118







119
120
121
122
123
124
125
126
127
128






































129
130
131


132
133
134
135
136
137
138
139
140
141
142
143
144






































145
146

147
148
149
150
151

152

153
154


155
156
157
158
159
160
161
162
163

164
165
















166
167
168
169
170
171
172
173
174

175
176
177

178
179
180
181
182
183
184
185
186
187
188

189
190
191
192
193
194
195
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67

68
69
70
71
72

73
74
75
76
77

78
79
80
81
82
83
84
85
86
87
88
89
90
91

92
93
94









95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127

128
129
130
131
132
133
134
135
136
137
138










139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178

179
180
181












182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220

221
222
223
224
225

226
227
228


229
230
231
232
233
234
235
236
237
238

239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269

270
271
272
273
274
275
276
277
278
279
280

281
282
283
284
285
286
287
288







+
+
+
+


+










-
+




-
+
+



-
+













-
+
+

-
-
-
-
-
-
-
-
-








+
+
+
+
+
+
+
+
+
+
+
+
+
+
+










-
+



+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


-
+
+

-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+




-
+

+
-
-
+
+








-
+


+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+









+


-
+










-
+







@ -- 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 
@   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
@   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;
@ -- Record bounced emails.  If too many bounces are received within
@ -- some defined time range, then cancel the subscription.  Older
@ -- entries are periodically purged.
@ --
@ CREATE TABLE repository.alert_bounce(
@   subscriberId INTEGER, -- to whom the email was sent.
@   sendTime INTEGER,     -- seconds since 1970 when email was sent
@   rcvdTime INTEGER      -- seconds since 1970 when bounce was received
@ );
;

/*
** 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","off"),"off")==0
     && 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(zAlertInit/*works-like:""*/);
    alert_triggers_enable();
  }else if( !db_table_has_column("repository","pending_alert","sentMod") ){
    db_multi_exec(
      "ALTER TABLE repository.pending_alert"
      " ADD COLUMN sentMod BOOLEAN DEFAULT false;"
    );
  }
}

  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.
** table. (Later:) Also add triggers that automatically relay timeline
** events to chat, if chat is configured for that.
*/
void alert_triggers_enable(void){
  if( !db_table_exists("repository","pending_alert") ) return;
  db_multi_exec(
    "CREATE TRIGGER IF NOT EXISTS repository.alert_trigger1\n"
    "AFTER INSERT ON 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;"
  );
}

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 triggers.
** 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_triggers_disable(void){
void alert_drop_trigger(void){
  db_multi_exec(
    "DROP TRIGGER IF EXISTS temp.alert_trigger1;\n"
    "DROP TRIGGER IF EXISTS repository.alert_trigger1;\n"
    "DROP TRIGGER IF EXISTS repository.email_trigger1;\n" // Legacy
    "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","off"),"off")==0 ) 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_footer();
  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("List Subscribers","%R/subscribers");
      style_submenu_element("Subscribers","%R/subscribers");
    }
    if( fossil_strcmp(g.zPath,"subscribe") ){
      style_submenu_element("Add New Subscriber","%R/subscribe");
    }
  }
}

213
214
215
216
217
218
219

220
221
222
223
224
225
226
227
228
229
230
231
232

233
234
235
236
237
238
239
240
241
242







243
244
245
246
247
248
249
250
251
252
253
254








255
256
257
258
259
260
261
262
263














264
265
266
267
268
269
270
271
272
273
274
275
276
277

278
279
280
281
282

283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309

310
311
312

313
314
315
316
317
318
319
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325

326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399

400
401
402
403
404

405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424








425
426
427

428
429
430
431
432
433
434
435







+












-
+










+
+
+
+
+
+
+












+
+
+
+
+
+
+
+









+
+
+
+
+
+
+
+
+
+
+
+
+
+













-
+




-
+



















-
-
-
-
-
-
-
-
+


-
+







    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>
  @ <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,
  @ recepient addresses, and subject from the header of the piped email
  @ 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 databaes", each email message is
  @ <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>

  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 are send here.
  @ (Property: "email-admin")</p>
  @ <hr>

  @ <p><input type="submit"  name="submit" value="Apply Changes" /></p>
  @ <p><input type="submit"  name="submit" value="Apply Changes"></p>
  @ </div></form>
  db_end_transaction(0);
  style_footer();
  style_finish_page();
}

#if 0
/*
** Encode pMsg as MIME base64 and append it to pOut
*/
static void append_base64(Blob *pOut, Blob *pMsg){
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385

386
387
388
389
390
391
392
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







-
-
-
-
-
-
-












+







      x[2] = "0123456789ABCDEF"[c&0xf];
      blob_append(pOut, x, 3);
      iCol += 3;
    }
  }
}

#if defined(_WIN32) || defined(WIN32)
# undef popen
# define popen _popen
# undef pclose
# define pclose _pclose
#endif

#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() */
};

404
405
406
407
408
409
410

411
412
413
414
415
416
417
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528







+







  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);
}
480
481
482
483
484
485
486
487

488
489
490

491
492
493
494
495
496
497
591
592
593
594
595
596
597

598
599
600
601
602
603
604
605
606
607
608
609







-
+



+







  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","off");
    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",
521
522
523
524
525
526
527
528


529
530
531
532
533
534
535
633
634
635
636
637
638
639

640
641
642
643
644
645
646
647
648







-
+
+







    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(p->zFrom, zRelay, smtpFlags);
      p->pSmtp = smtp_session_new(domain_of_addr(p->zFrom), zRelay,
                                  smtpFlags);
      smtp_client_startup(p->pSmtp);
    }
  }
  return p;
}

/*
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
685
686
687
688
689
690
691
692


693
694
695


696



697


698

699
700
701
702
703
704
705
706







+
-
-
+
+

-
-
+
-
-
-
+
-
-

-
+







      return 1;
    }
  }
  return 0;
}

/*
** Determine whether or not the input string is a valid email address.
** Make a copy of the input string up to but not including the
** first cTerm character.
** Only look at character up to but not including the first \000 or
** the first cTerm character, whichever comes first.
**
** Verify that the string really that is to be copied really is a
** valid email address.  If it is not, then return NULL.
** Return the length of the email addresss string in bytes if the email
**
** This routine is more restrictive than necessary.  It does not
** allow comments, IP address, quoted strings, or certain uncommon
** address is valid.  If the email address is misformed, return 0.
** characters.  The only non-alphanumerics allowed in the local
** part are "_", "+", "-" and "+".
*/
char *email_copy_addr(const char *z, char cTerm ){
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) ){
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
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







+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



-
+
+






+
+
-
-
-
-
+
+
+
+
-
-
+
-




















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







    }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;

  /* If we reach this point, the email address is valid */
  return mprintf("%.*s", i, z);
}

/*
** 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 enclosed in <...>
** 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, ">,:= ")]);
  while( zIn!=0 ){
     zIn = (const char*)strchr(zIn, '<');
     if( zIn==0 ) break;
     zIn++;
    if( zOut!=0 ) break;
    zIn = (const char *)strpbrk(zIn, "<,:= ");
    if( zIn==0 ) break;
    zIn++;
     zOut = email_copy_addr(zIn, '>');
     if( zOut!=0 ) break;
  }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, '@');
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
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







-
+













-
+











-
+












-
-
+
+










-
-
-
-
+
+
+
+
















+






-



+
+
-
+


-
+




+
+
+







*/
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 
** 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 recepient(s) must be specified using  "To:" or "Cc:" or "Bcc:" fields
** 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:
**     X-Fossil-From:
**     
**     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 "X-Fossil-From:" field is inserted with the email-self
** address.  Downstream software might use the X-Fossil-From 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 X-Fossil-From is
** 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{
    blob_init(&all, 0, 0);
    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 */
  if( zFromName ){
  }else if( zFromName ){
    blob_appendf(pOut, "From: %s <%s@%s>\r\n",
       zFromName, alert_mailbox_name(zFromName), alert_hostname(p->zFrom));
    blob_appendf(pOut, "X-Fossil-From: <%s>\r\n", 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",
844
845
846
847
848
849
850
851

852
853
854
855
856
857
858
1002
1003
1004
1005
1006
1007
1008

1009
1010
1011
1012
1013
1014
1015
1016







-
+







      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);
      fclose(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);
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
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+




-
+







-
+






-
+




-
+









+
+
+
+
+
-
+









-
+














-

+

+
+








-
+
+




-
+

+
-
-
-
-
+
+
+
+







    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
** 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 
** 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
** 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
** 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
** 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
** 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:
**
**                            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.
**    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:
**                            configuration.
**
**                            Options:
**                              --body FILENAME
**                              --smtp-trace
**                              --stdout
**                              --subject|-S SUBJECT
**                              --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);
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
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







-
+














+



















-
+





-
+



+
+









-
+





+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


+

-
+
+







          "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_triggers_disable();
      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, g.argv[4], isGlobal);
      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);
      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);
      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 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 ){
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
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







+
+
+
+
+
+
+
+
+











-
+
+
+
+
+







){
  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 ) return 0;
  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!='+' ){
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
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







-
+
-
-
-
+
+

















-
+







  }
  if( n>i-5 ){
    *peErr = 1;
    *pzErr = mprintf("email domain too short");
     return 0;
  }

  /* Verify the captcha */
  if( authorized_subscription_email(zEAddr)==0 ){
  if( needCaptcha && !captcha_is_correct(1) ){
    *peErr = 2;
    *pzErr = mprintf("incorrect security code");
    *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[] = 
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
1255
1256
1257
1258
1259
1260
1261
1262

1263
1264
1265
1266
1267
1268
1269
1503
1504
1505
1506
1507
1508
1509

1510
1511
1512
1513
1514
1515
1516
1517







-
+







**
** 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;
  unsigned int uSeed = 0;
  const char *zDecoded;
  char *zCaptcha = 0;
  char *zErr = 0;
  int eErr = 0;
  int di;

  if( alert_webpages_disabled() ) return;
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
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







+
+
+
+
+



-
+





-








+
+


+

-
+

-
-
+
+
+







-
-
-
-




+
-
+
+
+
+







    }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(1)
   && 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");
    sqlite3_int64 id;   /* New subscriber Id */
    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;
    db_multi_exec(
    zCode = db_text(0,
      "INSERT INTO subscriber(semail,suname,"
      "  sverified,sdonotcall,sdigest,ssub,sctime,mtime,smip)"
      "VALUES(%Q,%Q,%d,0,%d,%Q,now(),now(),%Q)",
      "  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
    );
    id = db_last_insert_rowid();
    zCode = db_text(0,
         "SELECT hex(subscriberCode) FROM subscriber WHERE subscriberId=%lld",
         id);
    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/%s", zCode);
        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);
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
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







-
+



-
+











+
+




-
+











+
+
+
+
+
-
+
+




-
+
+







        @ <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 on in order to activate your
        @ hyperlink that you must click to activate your
        @ subscription.</p>
      }
      alert_sender_free(pSender);
      style_footer();
      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 "Submit" button.</p>
  @ 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&nbsp;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'>&uarr; %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();
      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">
    @  <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'>&uarr; %h(zErr)</span></td></tr>
    }
    @ </tr>
  }
1416
1417
1418
1419
1420
1421
1422
1423







1424
1425
1426
1427
1428
1429
1430
1681
1682
1683
1684
1685
1686
1687

1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701







-
+
+
+
+
+
+
+







  @  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"))> \
    @  Forum Posts</label><br>
    @  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"))> \
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
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







-
+




-
+







-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+




+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+

-
-
-
-
+
+
+
+
+
+
+
+
+
+
+








-
+

-
-
-
+
+
+
+
+
+
+
+
+
+
+



+
+
+
+
+
+


-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
-
+
-
-
-
+


+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+
+

-
+


-
+
-
-
-
+

-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
-
-
+
+
+



-
-
+
+
+
-
-
-



-
-
-
-
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
-
-
-
-
-
-
+
-
-
-
-
-
-
-
+
+
+
-
-
+





-
+
+



+
+










-
-
+
+
+
+
+


+

-
+

-
-
+
+
-
-
-
-
+
+
+
+
+
+
+
+



+
+


+

-



+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+




+
+
-
+



+
+
+
+
+
+
+
+
+
+
-
+
+


+













+
+
+
+
+
+
+
+


-
+
+
+
+
+
+












-
+
+
+
+
+
+
+










+
+
+
+







-
-
-
-




-
+







  }
  @ </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
    @ Enter the 8 characters above in the "Security Code" box<br/>
    @ </td></tr></table></div>
  }
  @ </form>
  fossil_free(zErr);
  style_footer();
  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(const char *zName){
  char *zEmail;
  zEmail = db_text(0, "SELECT semail FROM subscriber"
                      " WHERE subscriberCode=hextoblob(%Q)", zName);
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 subscriberCode=hextoblob(%Q)",
      zName
    );
      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 delisted.
    @ All traces of that email address have been removed</p>
  }
  style_footer();
    @ <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 either of two ways:
** The subscriber is identified in several ways:
**
**    (1)  The name= query parameter contains the subscriberCode.
**         
**    (2)  The user is logged into an account other than "nobody" or
**    *    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 = P("name");
  Stmt q;
  int sa, sc, sf, st, sw;
  int sdigest, sdonotcall, sverified;
  const char *ssub;
  const char *semail;
  const char *smip;
  const char *suname;
  const char *mtime;
  const char *sctime;
  int eErr = 0;
  char *zErr = 0;
  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() ) return;
  if( alert_webpages_disabled() ){
  login_check_credentials();
  if( !g.perm.EmailAlert ){
    login_needed(g.anon.EmailAlert);
    db_commit_transaction();
    return;
  }
  login_check_credentials();
  if( zName==0 && login_is_individual() ){
    zName = db_text(0, "SELECT hex(subscriberCode) FROM subscriber"
                       " WHERE suname=%Q", g.zLogin);
  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( zName==0 || !validate16(zName, -1) ){
  if( sid==0 ){
    db_commit_transaction();
    cgi_redirect("subscribe");
    return;
    /*NOTREACHED*/
  }
  alert_submenu_common();
  if( P("submit")!=0 && cgi_csrf_safe(1) ){
  if( P("submit")!=0 && cgi_csrf_safe(2) ){
    int sdonotcall = PB("sdonotcall");
    int sdigest = PB("sdigest");
    char ssub[10];
    char newSsub[10];
    int nsub = 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.RdTkt && PB("st") )   ssub[nsub++] = 't';
    if( g.perm.RdWiki && PB("sw") )  ssub[nsub++] = 'w';
    ssub[nsub] = 0;
    if( g.perm.Admin ){
    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';
      const char *suname = PT("suname");
      int sverified = PB("sverified");
      if( suname && suname[0]==0 ) suname = 0;
    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;
      db_multi_exec(
        "UPDATE subscriber SET"
    ssub = newSsub;
    blob_init(&update, "UPDATE subscriber SET", -1);
    blob_append_sql(&update,
        " sdonotcall=%d,"
        " sdigest=%d,"
        " ssub=%Q,"
        " mtime=strftime('%%s','now'),"
        " smip=%Q,"
        " mtime=now(),"
        " lastContact=now()/86400,"
        " smip=%Q",
        " suname=%Q,"
        " sverified=%d"
        " WHERE subscriberCode=hextoblob(%Q)",
        sdonotcall,
        sdigest,
        ssub,
        g.zIpAddr,
        suname,
        sverified,
        zName
        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{
      db_multi_exec(
        "UPDATE subscriber SET"
  }else if( keepAlive ){
    db_unprotect(PROTECT_READONLY);
    db_multi_exec(
      "UPDATE subscriber SET lastContact=now()/86400"
        " sdonotcall=%d,"
        " sdigest=%d,"
        " ssub=%Q,"
        " mtime=strftime('%%s','now'),"
        " smip=%Q"
        " WHERE subscriberCode=hextoblob(%Q)",
      " WHERE subscriberId=%d", sid
        sdonotcall,
        sdigest,
        ssub,
        g.zIpAddr,
        zName
      );
    }
    );
    db_protect_pop();
  }
  }
  if( P("delete")!=0 && cgi_csrf_safe(1) ){
  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(zName);
      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 */
    " FROM subscriber WHERE subscriberCode=hextoblob(%Q)", zName);
    "  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");
    return;
    /*NOTREACHED*/
  }
  style_header("Update Subscription");
  semail = db_column_text(&q, 0);
  if( ssub==0 ){
    semail = db_column_text(&q, 0);
  sverified = db_column_int(&q, 1);
  sdonotcall = db_column_int(&q, 2);
  sdigest = db_column_int(&q, 3);
  ssub = db_column_text(&q, 4);
    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);
  suname = db_column_text(&q, 6);
  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);
    @ <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{
      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(zName)">
  @ <input type="hidden" name="name" value="%h(zHalfCode)">
  @ <table class="subscribe">
  @ <tr>
  @  <td class="form_label">Email&nbsp;Address:</td>
  if( isLogin ){
    @  <td><input type="text" name="semail" value="%h(semail)" size="30">\
    if( eErr==8 ){
      @ <span class='loginError'>&larr; not a valid email address!</span>
    }else if( g.perm.Admin ){
      @ &nbsp;&nbsp;<a href="%R/announce?to=%t(semail)">\
      @ (Send a message to %h(semail))</a>\
    }
    @ </td>
  }else{
  @  <td>%h(semail)</td>
    @  <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&nbsp;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)) &larr; \
    @      %,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"></td>
    @  size="30">\
    uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", suname);
    if( uid ){
      @ &nbsp;&nbsp;<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":"")>\
    @  Forum Posts</label><br>
    @  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>&nbsp;&uarr;&nbsp;
    @ 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 0
  @  <label><input type="checkbox" name="sdigest" %s(sdigest?"checked":"")>\
  @  Daily digest only</label><br>
#endif
  if( g.perm.Admin ){
    @ <tr>
    @  <td class="form_label">Admin Options:</td><td>
    @  <label><input type="checkbox" name="sdonotcall" \
    @  %s(sdonotcall?"checked":"")> Do not call</label><br>
    @  %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>
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
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







-
+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+




-
+











+












+
+
+





-
+






+

+
+
-
+
+

-
-
+
+
+
-

+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+










+
+


-
+







  @  <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_footer();
  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[] = 
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;
  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 unsubscribe immediately.
  /* If a valid subscriber code is supplied, then either present the user
  ** with a confirmation, or if already confirmed, unsubscribe immediately.
  */
  if( zName 
   && db_exists("SELECT 1 FROM subscriber WHERE subscriberCode=hextoblob(%Q)",
  if( zName
   && (sid = db_int(0, "SELECT subscriberId FROM subscriber"
                       " WHERE subscriberCode=hextoblob(%Q)", zName))!=0
                zName)
  ){
    char *zUnsubName = mprintf("confirm%04x", sid);
    if( P(zUnsubName)!=0 ){
    alert_unsubscribe(zName);
      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>&larr;</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>&larr;</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(1);
  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;
    }
  }
1825
1826
1827
1828
1829
1830
1831
1832

1833
1834

1835
1836
1837
1838
1839
1840
1841
2330
2331
2332
2333
2334
2335
2336

2337
2338

2339
2340
2341
2342
2343
2344
2345
2346







-
+

-
+







      @ %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_footer();
    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>
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
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







+



















-
+



-
+






-
+








+
+
+
+
+
+
+







+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


-
+






-
+
+
+









+
+

-
+








+






+
+
+
+

-
+



+
+
+
-
+
+


+
+
+
+
+
+
+
+
+






-
+






+
+
+
+
+
+
+
+
+
+
+


-
+




+













+




+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







  @ </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">&larr; %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
  @ Enter the 8 characters above in the "Security Code" box<br/>
  @ </td></tr></table></div>
  @ </form>
  fossil_free(zErr);
  style_footer();
  style_finish_page();
}

/*
** WEBPAGE: subscribers
**
** This page, accessible to administrators only,
** shows a list of email notification email addresses.
** 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 hex(subscriberCode),"          /* 0 */
    "SELECT subscriberId,"                 /* 0 */
    "       semail,"                       /* 1 */
    "       ssub,"                         /* 2 */
    "       suname,"                       /* 3 */
    "       sverified,"                    /* 4 */
    "       sdigest,"                      /* 5 */
    "       mtime,"                        /* 6 */
    "       date(sctime,'unixepoch')"      /* 7 */
    "       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='tttttKt'>
  @ 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/%s(db_column_text(&q,0))'>\
    @ <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(db_column_text(&q,3))</td>
      @ <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_footer();
  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', 'm', 't', 'w' */
  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);
1999
2000
2001
2002
2003
2004
2005



2006

2007
2008
2009
2010
2011
2012
2013
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614

2615
2616
2617
2618
2619
2620
2621
2622







+
+
+
-
+







  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-%%')"
    " blob.uuid,"                /* 0 */
         " 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"
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
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







+
-
+








-
-
-
+
+
+
+
+
+
+
+
+
+
+






-
+







    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( sizeof(EmailEvent) );
    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':  zType = "Forum post";      break;
      case 't':  zType = "Wiki Edit";       break;
      case 'w':  zType = "Ticket Change";   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,
      db_column_text(&q,2),
      zComment,
      zUrl,
      db_column_text(&q,0)
    );
    if( p->needMod ){
      blob_appendf(&p->txt,
        "** Pending moderator approval (%s/modreq) **\n",
        zUrl
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
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







-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+










+
-
+








-
+



+









-
+








  /* 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 */
    " (SELECT uuid FROM blob WHERE rid=forumpost.fpid),"    /* 1 */
    " datetime(event.mtime),"                               /* 2 */
    " substr(comment,instr(comment,':')+2),"                /* 3 */
    " (SELECT uuid FROM blob WHERE rid=forumpost.firt),"    /* 4 */
    " wantalert.needMod,"                                   /* 5 */
    " coalesce(trim(substr(info,1,instr(info,'<')-1)),euser,user)"   /* 6 */
    " 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(db_column_int(&q,0), CFTYPE_FORUM, 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 = 'f';
    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", 
      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));
      }
    }
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
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







-
-
-
-
-
-
-
-
-








-
+
+

-
-
+
+
+
+

+
+
+
-
+

+







  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"));
}

/*
** Append the "unsubscribe" notification and other footer text to
** the end of an email alert being assemblied in pOut.
*/
void alert_footer(Blob *pOut){
  blob_appendf(pOut, "\n-- \nTo unsubscribe: %s/unsubscribe\n",
     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.
** pending_alert table.  The text of the email alerts appears on
** standard output.
**
** This command is intended for testing and debugging the logic
** that generates email alert text.
** 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.
** Options:
** 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;
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
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







-




















-



-



-







    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);
  alert_footer(&out);
  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 ){
2268
2269
2270
2271
2272
2273
2274





























































2275
2276
2277
2278
2279
2280
2281
2282
2283

2284
2285
2286
2287
2288
2289
2290
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+









+







    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
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
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







-
+


+









+



-
+







** 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!
*/
void alert_send_alerts(u32 flags){
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() ) goto send_alert_done;
  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 ){
2370
2371
2372
2373
2374
2375
2376





2377
2378
2379
2380
2381
2382

2383
2384
2385
2386
2387
2388
2389
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







+
+
+
+
+





-
+







      "        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_done;
  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.
  */
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
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







-
+
+

-
-
-
+
+
+
+
+
+






+


-
+
+
+
+
+
+
+
+
+
+
+







+
-
-
-
+
+
+











-
-
-
-
+
+
+
+
+










+
+
-
+
+
+

+
+

+




















+
+
+



+













+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



+













-
+

+
-
-
+
+



-
+

+















+



-
+


-
+



-
+







  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 */
     " fullcap(user.cap),"    /* 3 */
     " suname"                /* 4 */
     " FROM subscriber LEFT JOIN user ON (login=suname)"
     " WHERE sverified AND NOT sdonotcall"
     "  AND sdigest IS %s",
     zDigest/*safe-for-%s*/
     " 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 ) continue;
      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 'f':  xType = '5';  break;
            case 't':  xType = 'q';  break;
            case 'w':  xType = 'l';  break;
            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 'f':  xType = '2';  break;
          case 't':  xType = 'r';  break;
          case 'w':  xType = 'j';  break;
          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(&fbody, "\n-- \nSubscription info: %s/alerts/%s\n",
        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.
*/
void alert_backoffice(u32 mFlags){
int alert_backoffice(u32 mFlags){
  int iJulianDay;
  int nSent = 0;
  if( !alert_tables_exist() ) return;
  alert_send_alerts(mFlags);
  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);
    alert_send_alerts(SENDALERT_DIGEST|mFlags);
    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_footer();
    style_finish_page();
    return;
  }
  if( P("submit")!=0 
  if( P("submit")!=0
   && P("subject")!=0
   && P("msg")!=0
   && P("from")!=0
   && cgi_csrf_safe(1)
   && 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"));
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
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







-
+







+








+







      @ %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_footer();
    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&nbsp;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&nbsp;Email&nbsp;Address:</td>
  @  <td><input type="text" name="from" value="%h(PT("from"))" size="30"></td>
  @ </tr>
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
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







-
+



-
+













+











-
+



+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+







  @   <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
    @ Enter the 8 characters above in the "Security Code" box<br/>
    @ </td></tr></table></div>
  }
  @ </form>
  style_footer();
  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 ){
  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 %s",
                   bAll ? "" : " AND ssub LIKE '%a%'");
      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);
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
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







+
+
+
+
+
+
+





+


+

-
+

-
+









-
+
+

-
+










-
+
+




+


+








-
+
+
+
+














+
+
+
-
+
+



-
+

** 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);
    cgi_print_all(0, 0, 0);
    @ </p>
  }else if( P("submit")!=0 && cgi_csrf_safe(1) ){
  }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.</p>
      @ <p>The announcement has been sent.
      @ <a href="%h(PD("REQUEST_URI","/"))">Send another</a></p>
    }
    style_footer();    
    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">
  @ <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></td>
    @  <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">
    @   <td><input type="submit" name="submit" value="Send Message">
  }
  @ </tr>
  @ </table>
  @ </form>
  style_footer();
  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
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53



54
55

56
57
58
59
60
61
62




63
64
65

66
67
68
69
70
71
72
18
19
20
21
22
23
24


























25


26
27
28
29

30
31
32
33
34
35


36
37
38
39
40
41

42
43
44
45
46
47
48
49







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

-
-
+
+
+

-
+





-
-
+
+
+
+


-
+







** This file contains code to implement the "all" command-line method.
*/
#include "config.h"
#include "allrepo.h"
#include <assert.h>

/*
** The input string is a filename.  Return a new copy of this
** filename if the filename requires quoting due to special characters
** such as spaces in the name.
**
** If the filename cannot be safely quoted, return a NULL pointer.
**
** Space to hold the returned string is obtained from malloc.  A new
** string is returned even if no quoting is needed.
*/
static char *quoteFilename(const char *zFilename){
  int i, c;
  int needQuote = 0;
  for(i=0; (c = zFilename[i])!=0; i++){
    if( c=='"' ) return 0;
    if( fossil_isspace(c) ) needQuote = 1;
    if( c=='\\' && zFilename[i+1]==0 ) return 0;
    if( c=='$' ) return 0;
  }
  if( needQuote ){
    return mprintf("\"%s\"", zFilename);
  }else{
    return mprintf("%s", zFilename);
  }
}

/*
** Build a string that contains all of the command-line options
** specified as arguments.  If the option name begins with "+" then
** it takes an argument.  Without the "+" it does not.
** 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){
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 *zValue = find_option(zArg, 0, 1);
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 %s", zArg, zValue);
      blob_appendf(pExtra, " --%s %$", zArg, zValue);
    }else{
      blob_appendf(pExtra, " --%s \"\"", zArg);
    }
  }
}
static void collect_argv(Blob *pExtra, int iStart){
  int i;
86
87
88
89
90
91
92



93
94
95
96
97

98
99
100

101
102
103

104
105
106
107
108
109
110
111
112
113

114
115
116
117
118




119
120
121
122

123
124
125
126
127
128
129
130


131




132
133

134
135
136
137
138




139


140
141










142
143
144
145
146
147
148
149
150
151
152
153
154

155
156
157

158
159
160
161
162
163
164
165
166
167



168
169
170
171
172
173
174
175
176
177
178
179
180
181

182
183
184


185
186
187
188
189
190
191
192
193
194
195
196
197

198

199
200
201
202
203
204

205
206










207

208
209
210
211


212
213
214
215
216
217
218


219
220
221
222
223
224

225
226
227
228
229
230
231
232
233

234
235
236
237
238

239

240
241
242
243
244
245
246

247
248

249
250
251
252













253

254
255
256

257
258

259

260

261
262
263
264
265

266
267

268
269
270
271
272
273





















274

275
276
277

278
279
280

281
282
283

284

285
286

287


288

289
290

291
292

293
294
295

296
297
298
299
300

301
302
303
304
305
306
307
308
309
310
311
312
313
314
315

316

317
318
319
320
321
322
323
324

325
326
327
328
329
330
331
63
64
65
66
67
68
69
70
71
72
73
74
75
76

77
78
79

80
81
82

83
84
85
86
87
88
89
90
91
92

93
94
95
96
97
98
99
100
101
102
103
104
105

106
107
108
109
110
111
112


113
114
115
116
117
118
119
120

121
122




123
124
125
126
127
128
129


130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151

152
153
154

155
156
157
158
159
160
161
162



163
164
165
166
167

168
169
170


171
172
173
174
175

176
177
178
179
180
181
182
183
184
185
186
187
188
189

190
191
192
193
194

195
196
197
198
199
200

201
202
203
204
205
206
207
208
209
210
211
212
213

214
215
216


217
218
219
220
221
222
223


224
225
226
227
228
229
230

231
232
233
234
235
236
237
238
239

240
241
242
243
244
245
246

247
248
249
250
251
252
253

254
255

256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273

274
275
276

277
278
279
280

281
282
283
284
285
286
287

288
289

290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317

318
319
320

321
322
323

324
325
326

327
328
329
330
331
332

333
334
335
336
337

338
339

340
341
342

343
344
345
346
347

348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373

374
375
376
377
378
379
380
381







+
+
+




-
+


-
+


-
+









-
+





+
+
+
+



-
+






-
-
+
+

+
+
+
+

-
+

-
-
-
-
+
+
+
+

+
+
-
-
+
+
+
+
+
+
+
+
+
+












-
+


-
+







-
-
-
+
+
+


-



-
-





-
+



+
+








-




+
-
+





-
+


+
+
+
+
+
+
+
+
+
+
-
+


-
-
+
+





-
-
+
+





-
+








-
+





+
-
+






-
+

-
+




+
+
+
+
+
+
+
+
+
+
+
+
+
-
+


-
+


+
-
+

+




-
+

-
+






+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+


-
+


-
+


-
+

+


+
-
+
+

+

-
+

-
+


-
+




-
+















+

+







-
+







** 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 checkouts that have uncommitted changes.
**    changes     Shows all local check-outs that have uncommitted changes.
**                This operation has no additional options.
**
**    clean       Delete all "extra" files in all local checkouts.  Extreme
**    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 checkouts to be operated upon
**                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 checkouts.  The command
**    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 option is supported.
**                --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 and
**                --randomize options are not supported.
**                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
**                option is supported.
**                and --unversioned and --share-links options are supported.
**
**    setting     Run the "setting", "set", or "unset" commands on all
**    set         repositories.  These command are particularly useful in
**    unset       conjunction with the "max-loadavg" setting which cannot
**                otherwise be set globally.
**    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 "ui" or "server" commands on all repositories.
**    ui          The root URI gives a listing of all repos.
**    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 checkouts to be ignored instead.
**                local check-outs to be ignored instead.
**
**    list | ls   Display the location of all repositories.  The -c|--ckout
**                option causes all local checkouts to be listed instead.
**                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:
**   --showfile     Show the repository or checkout being operated upon.
**   --dontstop     Continue with other repositories even after an error.
**   --dry-run      If given, display instead of run actions.
**   --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){
  int n;
  Stmt q;
  const char *zCmd;
  char *zSyscmd;
  char *zFossil;
  char *zQFilename;
  Blob extra;
  int useCheckouts = 0;
  int quiet = 0;
  int dryRunFlag = 0;
  int showFile = find_option("showfile",0,0)!=0;
  int stopOnError = find_option("dontstop",0,0)==0;
  int 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 ...");
  }
  n = strlen(g.argv[2]);
  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
  if( strncmp(zCmd, "ui", n)==0 || strncmp(zCmd, "server", n)==0 ){
      || fossil_strcmp(zCmd, "server")==0 ){
    g.argv[1] = g.argv[2];
    g.argv[2] = "/";
    cmd_webserver();
    return;
  }
  if( strncmp(zCmd, "list", n)==0 || strncmp(zCmd,"ls",n)==0 ){
  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( strncmp(zCmd, "clean", n)==0 ){
  }else if( fossil_strcmp(zCmd, "clean")==0 ){
    zCmd = "clean --chdir";
    collect_argument(&extra, "allckouts",0);
    collect_argument_value(&extra, "case-sensitive");
    collect_argument_value(&extra, "clean");
    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");
    collect_argument_value(&extra, "keep");
    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( strncmp(zCmd, "config", n)==0 ){
  }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( strncmp(zCmd, "dbstat", n)==0 ){
  }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( strncmp(zCmd, "extras", n)==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");
    collect_argument_value(&extra, "case-sensitive", 0);
    collect_argument(&extra, "dotfiles",0);
    collect_argument_value(&extra, "ignore");
    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( strncmp(zCmd, "push", n)==0 ){
  }else if( fossil_strcmp(zCmd, "push")==0 ){
    zCmd = "push -autourl -R";
    collect_argument(&extra, "verbose","v");
  }else if( strncmp(zCmd, "pull", n)==0 ){
  }else if( fossil_strcmp(zCmd, "pull")==0 ){
    zCmd = "pull -autourl -R";
    collect_argument(&extra, "verbose","v");
    collect_argument(&extra, "share-links",0);
  }else if( strncmp(zCmd, "rebuild", n)==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");
    collect_argument_value(&extra, "pagesize", 0);
    collect_argument(&extra, "vacuum",0);
    collect_argument(&extra, "deanalyze",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( strncmp(zCmd, "setting", n)==0 ){
  }else if( fossil_strcmp(zCmd, "setting")==0 ){
    zCmd = "setting -R";
    collect_argv(&extra, 3);
  }else if( strncmp(zCmd, "unset", n)==0 ){
  }else if( fossil_strcmp(zCmd, "unset")==0 ){
    zCmd = "unset -R";
    collect_argv(&extra, 3);
  }else if( strncmp(zCmd, "fts-config", n)==0 ){
  }else if( fossil_strcmp(zCmd, "fts-config")==0 ){
    zCmd = "fts-config -R";
    collect_argv(&extra, 3);
  }else if( strncmp(zCmd, "sync", n)==0 ){
  }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( strncmp(zCmd, "test-integrity", n)==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( strncmp(zCmd, "test-orphans", n)==0 ){
  }else if( fossil_strcmp(zCmd, "test-orphans")==0 ){
    zCmd = "test-orphans -R";
  }else if( strncmp(zCmd, "test-missing", n)==0 ){
  }else if( fossil_strcmp(zCmd, "test-missing")==0 ){
    zCmd = "test-missing -q -R";
    collect_argument(&extra, "notshunned",0);
  }else if( strncmp(zCmd, "changes", n)==0 ){
  }else if( fossil_strcmp(zCmd, "changes")==0 ){
    zCmd = "changes --quiet --header --chdir";
    useCheckouts = 1;
    stopOnError = 0;
    quiet = 1;
  }else if( strncmp(zCmd, "ignore", n)==0 ){
  }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( strncmp(zCmd, "add", n)==0 ){
  }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;
344
345
346
347
348
349
350

351

352
353
354
355
356
357
358
359

360
361
362
363

364
365
366






367
368
369
370



371
372
373
374
375
376
377
378
379
380
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410

411
412
413
414

415
416
417
418
419
420
421
422
423
424
425
426


427
428
429
430
431

432
433
434
435
436
437
438







+

+







-
+



-
+



+
+
+
+
+
+


-
-
+
+
+


-







      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( strncmp(zCmd, "info", n)==0 ){
  }else if( fossil_strcmp(zCmd, "info")==0 ){
    zCmd = "info";
    showLabel = 1;
    quiet = 1;
  }else if( strncmp(zCmd, "cache", n)==0 ){
  }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 ignore "
                 "info list ls pull push rebuild server setting sync ui unset");
      "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();
  zFossil = quoteFilename(g.nameOfExe);
  db_multi_exec("CREATE TEMP TABLE repolist(name,tag);");
  if( useCheckouts ){
    db_multi_exec(
       "INSERT INTO repolist "
       "SELECT DISTINCT substr(name, 7), name COLLATE nocase"
       "  FROM global_config"
       " WHERE substr(name, 1, 6)=='ckout:'"
405
406
407
408
409
410
411
412

413
414
415
416
417


418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433






434
435
436
437
438
439
440
441
442
443
444
445
446
447

448

449
450
451
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







-
+


-
-
-
+
+













-
-
-
+
+
+
+
+
+














+

+



      nToDel++;
      continue;
    }
    if( zCmd[0]=='l' ){
      fossil_print("%s\n", zFilename);
      continue;
    }else if( showFile ){
      fossil_print("%s: %s\n", useCheckouts ? "checkout" : "repository",
      fossil_print("%s: %s\n", useCheckouts ? "check-out" : "repository",
                   zFilename);
    }
    zQFilename = quoteFilename(zFilename);
    zSyscmd = mprintf("%s %s %s%s",
                      zFossil, zCmd, zQFilename, blob_str(&extra));
    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);
    free(zQFilename);
    if( stopOnError && rc ){
      break;
    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.

21
22
23
24
25
26
27
28

29

30
31
32
33
34
35
36








37
38
39
40
41
42
43
44
45
46

47
48
49
50
51
52
53
21
22
23
24
25
26
27

28
29
30
31






32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57







-
+

+

-
-
-
-
-
-
+
+
+
+
+
+
+
+










+







#include "attach.h"
#include <assert.h>

/*
** WEBPAGE: attachlist
** List attachments.
**
**    tkt=TICKETUUID
**    tkt=HASH
**    page=WIKIPAGE
**    technote=HASH
**
** At most one of technote=, tkt= or page= are supplied.
** If none is given, all attachments are listed.  If one is given,
** only attachments for the designated technote, ticket or wiki page
** are shown. TECHNOTEUUID and TICKETUUID may be just a prefix of the
** relevant technical note or ticket, in which case all attachments
** of all technical notes or tickets with the prefix will be listed.
** 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"
105
106
107
108
109
110
111
112

113
114
115
116

117
118
119
120
121
122
123
109
110
111
112
113
114
115

116
117
118
119

120
121
122
123
124
125
126
127







-
+



-
+







      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>
    @ <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 />
      @ %!W(zComment)<br>
    }
    if( zPage==0 && zTkt==0 && zTechNote==0 ){
      if( zSrc==0 || zSrc[0]==0 ){
        zSrc = "Deleted from";
      }else {
        zSrc = "Added to";
      }
140
141
142
143
144
145
146
147

148
149
150
151
152
153
154
155
156

157
158
159

160
161

162
163
164
165
166
167
168
169
170
171
172
173
174
175
176

177
178
179
180
181
182
183
144
145
146
147
148
149
150

151
152
153
154
155
156
157
158
159
160
161
162
163

164
165

166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189







-
+









+


-
+

-
+















+







    }
    @ by %h(zDispUser) on
    hyperlink_to_date(zDate, ".");
    free(zUrlTail);
  }
  db_finalize(&q);
  @ </ol>
  style_footer();
  style_finish_page();
  return;
}

/*
** WEBPAGE: attachdownload
** WEBPAGE: attachimage
** WEBPAGE: attachview
**
** Download or display an attachment.
**
** Query parameters:
**
**    tkt=TICKETUUID
**    tkt=HASH
**    page=WIKIPAGE
**    technote=TECHNOTEUUID
**    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 ){
199
200
201
202
203
204
205
206

207
208
209
210
211

212
213
214
215
216
217
218
205
206
207
208
209
210
211

212
213
214
215
216

217
218
219
220
221
222
223
224







-
+




-
+







       " ORDER BY mtime DESC LIMIT 1",
       zTarget, zFile
    );
  }
  if( zUUID==0 || zUUID[0]==0 ){
    style_header("No Such Attachment");
    @ No such attachment....
    style_footer();
    style_finish_page();
    return;
  }else if( zUUID[0]=='x' ){
    style_header("Missing");
    @ Attachment has been deleted
    style_footer();
    style_finish_page();
    return;
  }else{
    g.perm.Read = 1;
    cgi_replace_parameter("name",zUUID);
    if( fossil_strcmp(g.zPath,"attachview")==0 ){
      artifact_page();
    }else{
236
237
238
239
240
241
242
243

244
245
246
247
248
249
250
251
252
253
254
255

256
257
258
259
260
261
262
242
243
244
245
246
247
248

249
250
251
252
253
254
255
256
257
258
259
260

261
262
263
264
265
266
267
268







-
+











-
+







    moderation_table_create();
    db_multi_exec(
      "INSERT INTO modreq(objid,attachRid) VALUES(%d,%d);",
      rid, attachRid
    );
  }else{
    rid = content_put(pAttach);
    db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d);", rid);
    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 uuid to attach to */
  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;
303
304
305
306
307
308
309
310

311
312

313
314
315
316
317
318
319
309
310
311
312
313
314
315

316
317

318
319
320
321
322
323
324
325







-
+

-
+







    db_end_transaction(0);
}

/*
** WEBPAGE: attachadd
** Add a new attachment.
**
**    tkt=TICKETUUID
**    tkt=HASH
**    page=WIKIPAGE
**    technote=TECHNOTEUUID
**    technote=HASH
**    from=URL
**
*/
void attachadd_page(void){
  const char *zPage = P("page");
  const char *zTkt = P("tkt");
  const char *zTechNote = P("technote");
369
370
371
372
373
374
375
376

377
378
379
380
381
382
383
384
385
386

387
388
389
390
391
392
393
394
395
396
397



398
399

400
401

402
403

404
405
406
407



408
409
410
411

412
413
414
415
416
417
418
419
420
421
422
423
424
425

426
427
428

429
430
431
432
433
434
435
375
376
377
378
379
380
381

382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401



402
403
404
405

406
407

408
409

410
411



412
413
414
415
416
417

418
419
420
421
422
423
424
425
426
427
428
429
430
431

432
433
434

435
436
437
438
439
440
441
442







-
+










+








-
-
-
+
+
+

-
+

-
+

-
+

-
-
-
+
+
+



-
+













-
+


-
+







                        " WHERE tagname GLOB 'tkt-%q*'", zTkt);
      if( zTkt==0 ) fossil_redirect_home();
    }
    zTarget = zTkt;
    zTargetType = mprintf("Ticket <a href=\"%R/tktview/%s\">%S</a>",
                          zTkt, zTkt);
  }
  if( zFrom==0 ) zFrom = mprintf("%s/home", g.zTop);
  if( 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 />
  @ <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)" />
    @ <input type="hidden" name="tkt" value="%h(zTkt)">
  }else if( zTechNote ){
    @ <input type="hidden" name="technote" value="%h(zTechNote)" />
    @ <input type="hidden" name="technote" value="%h(zTechNote)">
  }else{
    @ <input type="hidden" name="page" value="%h(zPage)" />
    @ <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" />
  @ <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_footer();
  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;             /* UUID of the control artifact */
  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;              /* UUID of the attached file */
  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 */
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
450
451
452
453
454
455
456













457
458
459
460
461
462
463







-
-
-
-
-
-
-
-
-
-
-
-
-







  if( !g.perm.RdTkt && !g.perm.RdWiki ){
    login_needed(g.anon.RdTkt || g.anon.RdWiki);
    return;
  }
  rid = name_to_rid_www("name");
  if( rid==0 ){ fossil_redirect_home(); }
  zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
#if 0
  /* Shunning here needs to get both the attachment control artifact and
  ** the object that is attached. */
  if( g.perm.Admin ){
    if( db_exists("SELECT 1 FROM shun WHERE uuid='%q'", zUuid) ){
      style_submenu_element("Unshun", "%s/shun?uuid=%s&sub=1",
            g.zTop, zUuid);
    }else{
      style_submenu_element("Shun", "%s/shun?shun=%s#addshun",
            g.zTop, zUuid);
    }
  }
#endif
  pAttach = manifest_get(rid, CFTYPE_ATTACHMENT, 0);
  if( pAttach==0 ) fossil_redirect_home();
  zTarget = pAttach->zAttachTarget;
  zSrc = pAttach->zAttachSrc;
  ridSrc = db_int(0,"SELECT rid FROM blob WHERE uuid='%q'", zSrc);
  zName = pAttach->zAttachName;
  zDesc = pAttach->zComment;
543
544
545
546
547
548
549
550

551
552

553
554
555
556
557
558
559
537
538
539
540
541
542
543

544
545
546
547
548
549
550
551
552
553
554







-
+


+







        cgi_redirectf("%R/tktview/%!S", zTktUuid);
      }else{
        cgi_redirectf("%R/wiki?name=%t", zWikiName);
      }
      return;
    }
    if( strcmp(zModAction,"approve")==0 ){
      moderation_approve(rid);
      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"));
  }

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
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







-
+

-
+














-
+







-
+









-
+







  @ </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 />
    @ Delete this change</label><br>
    @ <label><input type="radio" name="modaction" value="approve">
    @ Approve this change</label><br />
    @ Approve this change</label><br>
    @ <input type="submit" value="Submit">
    @ </form>
    @ </blockquote>
  }

  @ <div class="section">Content Appended</div>
  @ <blockquote>
  blob_zero(&attach);
  if( fShowContent ){
    const char *z;
    content_get(ridSrc, &attach);
    blob_to_utf8_no_bom(&attach, 0);
    z = blob_str(&attach);
    if( zLn ){
      output_text_with_line_numbers(z, zLn);
      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 />
    @ <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_footer();
  style_finish_page();
}

/*
** Output HTML to show a list of attachments.
*/
void attachment_list(
  const char *zTarget,   /* Object that things are attached to */
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
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







+

















-
+

-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+

-
+







    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.
** 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.
** 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.
** 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.
*/
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



















































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







-
+
+








-
+


















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
      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')",
        "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 uuid to attach to */
      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);
}

Changes to src/backoffice.c.

17
18
19
20
21
22
23

24
25
26
27
28
29
30
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31







+







**
** 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
75
76
77
78
79
80
81


82
83
84

85
86
87
88
89
90
91
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95







+
+



+







# 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
118
119
120
121
122
123
124


125
126
127






















128
129
130
131
132
133
134
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162







+
+



+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+








/* 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.
*/
211
212
213
214
215
216
217

218
219
220
221
222
223
224
225
226
227

228
229
230
231
232
233
234
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264







+










+







**
** 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){
247
248
249
250
251
252
253

254
255
256
257
258

259
260
261
262
263
264
265
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297







+





+







  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.
*/
274
275
276
277
278
279
280
281

282
283
284
285
286
287
288

289
290
291
292
293

294
295
296
297
298
299
300

301
302
303
304
305
306
307
306
307
308
309
310
311
312

313
314
315
316
317
318
319

320
321
322
323
324

325
326
327
328
329
330
331

332
333
334
335
336
337
338
339







-
+






-
+




-
+






-
+







  CloseHandle(hProcess);
  return 1;
}
#endif

/*
** Check to see if the process identified by pid is alive.  If
** we cannot prove the the process is dead, return true.
** 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 
#endif
}

/*
** Check to see if the process identified by pid has finished.  If
** we cannot prove the the process is still running, return true.
** 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 
#endif
}

/*
** Return a process id number for the current process
*/
static sqlite3_uint64 backofficeProcessId(void){
  return (sqlite3_uint64)GETPID();
330
331
332
333
334
335
336
337

338
339
340





341
342
343
344
345
346







347
348
349
350
351
352
353
362
363
364
365
366
367
368

369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397







-
+



+
+
+
+
+






+
+
+
+
+
+
+







                    backofficeProcessDone(x));
  }
}

/*
** COMMAND: test-backoffice-lease
**
** Usage: %fossil 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)");
380
381
382
383
384
385
386

387
388
389
390
391
392
393
394
395
396
397







398
399
400
401
402
403
404
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456







+











+
+
+
+
+
+
+







  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()");
418
419
420
421
422
423
424
425
426


427
428
429
430
431
432
433
434
435


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
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







-
-
+
+









+
+














+
+




















+
+
+

















-
+
















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+









+
+
+
+
+
+
+
-
-
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+



-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





-
+

+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


+
+
+
+
+
+

+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+







** 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 a on-deck backoffice is
** needed, this routine becomes that on-desk 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( lastWarning+warningDelay < tmNow ){
      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
  char *zLog = db_get("backoffice-logfile",0);
  if( zLog && zLog[0] ){
  if( zLog==0 ) zLog = db_get("backoffice-logfile",0);
  if( zLog && zLog[0] && (backofficeFILE = fossil_fopen(zLog,"a"))!=0 ){
    FILE *pLog = fossil_fopen(zLog, "a");
    if( pLog ){
      char *zDate = db_text(0, "SELECT datetime('now');");
      fprintf(pLog, "%s (%d) backoffice running\n", zDate, GETPID());
      fclose(pLog);
    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 */
  alert_backoffice(0);
  smtp_cleanup();
  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: backoffice [-R repository]
** 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.
**
** Run backoffice processing.  This might be done by a cron job or
** similar to make sure backoffice processing happens periodically.
** 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;
  db_find_and_open_repository(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();
      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.
*/
584
585
586
587
588
589
590



591
592
593
594
595
596
597
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869







+
+
+







      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);
    }

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
73

74
75
76
77
78
79
80
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78

79
80
81
82
83
84
85
86







+
+
+
+
+
+




















-
+







*/
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)  (i*101)
#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
93
94
95
96
97
98
99
100

101
102
103
104
105
106
107
99
100
101
102
103
104
105

106
107
108
109
110
111
112
113







-
+







  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( h==newSize ) h = 0;
        if( (int)h==newSize ) h = 0;
      }
      p->a[h] = e;
      nLive++;
    }else if( e<0 ){
      nDel++;
    }
  }
123
124
125
126
127
128
129
130

131
132
133
134
135
136
137
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( h>=p->sz ) h = 0;
    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
153

154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169

170
171
172
173
174
175
176
152
153
154
155
156
157
158

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( h>=p->sz ) h = 0;
    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( h>=p->sz ) h = 0;
    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
216

217
218
219
220

221
222
223

224
225
226
227
228
229
230
231
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( h>=p->sz ) h = 0;
    if( (int)h>=p->sz ) h = 0;
  }
  assert( p->a[h] );
  h++;
  while( h<p->sz && p->a[h]<=0 ){
  while( (int)h<p->sz && p->a[h]<=0 ){
    h++;
  }
  return h<p->sz ? p->a[h] : 0;
  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;
}

Changes to src/bisect.c.

35
36
37
38
39
40
41
42

43
44

45
46
47












48


49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67


68
69
70
71


72
73
74
75
76
77
78
79
80
81
82


83




84
85
86
87
88
89
90
35
36
37
38
39
40
41

42
43

44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79

80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100

101
102
103
104
105
106
107
108
109
110
111







-
+

-
+



+
+
+
+
+
+
+
+
+
+
+
+
-
+
+


















-
+
+




+
+











+
+
-
+
+
+
+







** 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);
    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);
    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, bisect_option("direct-only"), 0);
    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\" or \"bisect bad\"" },
                             "\"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 ){
      char *z = db_lget(zLabel, (char*)aBisectOption[i].zDefault);
        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;
    }
  }
165
166
167
168
169
170
171









172
173
174
175










176
177
178


179
180
181



























182


183
184
185

186
187

188
189
190
191
192







193
194
195
196











197
198
199
200
201
202
203
204

205













206








































207
208
209
210
211
212
213
214
215
216

217
218
219
220
221
222
223
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216


217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248

249
250
251
252

253
254

255
256
257
258
259
260
261
262
263
264
265
266
267




268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351

352
353
354
355
356
357
358
359







+
+
+
+
+
+
+
+
+




+
+
+
+
+
+
+
+
+
+

-
-
+
+



+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+


-
+

-
+





+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+








+

+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+









-
+







    db_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.
*/
void bisect_create_bilog_table(int iCurrent){
  char *zLog = db_lget("bisect-log","");
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);
    blob_init(&log, zLog, -1);
  }
  db_multi_exec(
     "CREATE TEMP TABLE bilog("
     "  seq INTEGER PRIMARY KEY,"  /* Sequence of events */
     "  rid INTEGER PRIMARY KEY,"  /* Sequence of events */
     "  stat TEXT,"                /* Type of occurrence */
     "  rid INTEGER UNIQUE"        /* Check-in number */
     "  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{
    int rid = atoi(blob_str(&id));
    db_bind_int(&q, ":seq", ++cnt);
    db_bind_text(&q, ":stat", rid>0 ? "GOOD" : "BAD");
    db_bind_int(&q, ":rid", rid>=0 ? rid : -rid);
      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);
  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'"
232
233
234
235
236
237
238





























































































239
240
241
242
243
244


245
246

247
248
249


250
251

252
253
254


255
256
257


258
259
260
261



262
263

264
265
266


267
268

269
270
271


272
273

274
275
276


277
278

279


280

281





282

283
284
285




286
287

288
289

290
291

292
293
294
295
296
297



298
299

300
301
302


303
304
305
306
307
308
309
310

311
312
313
314
315
316
317
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





-
+
+

-
+

-
-
+
+

-
+

-
-
+
+

-
-
+
+

-
-
-
+
+
+

-
+

-
-
+
+

-
+

-
-
+
+

-
+

-
-
+
+

-
+

+
+
-
+

+
+
+
+
+
-
+

-
-
+
+
+
+

-
+

-
+

-
+

-
-
-
-
-
+
+
+
-
-
+
-
-
-
+
+







-
+







        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 for bugs.
** 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?
** > fossil bisect bad ?VERSION?
**
**     Identify version VERSION as non-working.  If VERSION is omitted,
**     the current checkout is marked as non-working.
**       Identify version VERSION as non-working.  If VERSION is omitted,
**       the current check-out is marked as non-working.
**
**   fossil bisect good ?VERSION?
** > fossil bisect good ?VERSION?
**
**     Identify version VERSION as working.  If VERSION is omitted,
**     the current checkout is marked as working.
**       Identify version VERSION as working.  If VERSION is omitted,
**       the current check-out is marked as working.
**
**   fossil bisect log
**   fossil bisect chart
** > fossil bisect log
** > fossil bisect chart
**
**     Show a log of "good" and "bad" versions.  "bisect log" shows the
**     events in the order that they were tested.  "bisect chart" shows
**     them in order of check-in.
**       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
** > fossil bisect next
**
**     Update to the next version that is halfway between the working and
**     non-working versions.
**       Update to the next version that is halfway between the working and
**       non-working versions.
**
**   fossil bisect options ?NAME? ?VALUE?
** > fossil bisect options ?NAME? ?VALUE?
**
**     List all bisect options, or the value of a single option, or set the
**     value of a bisect option.
**       List all bisect options, or the value of a single option, or set the
**       value of a bisect option.
**
**   fossil bisect reset
** > fossil bisect reset
**
**     Reinitialize a bisect session.  This cancels prior bisect history
**     and allows a bisect session to start over from the beginning.
**       Reinitialize a bisect session.  This cancels prior bisect history
**       and allows a bisect session to start over from the beginning.
**
**   fossil bisect vlist|ls|status ?-a|--all?
** > 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
**     List the versions in between "bad" and "good".
**       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 ui
** > fossil bisect skip ?VERSION?
**
**     Like "fossil ui" except start on a timeline that shows only the
**     check-ins that are part of the current bisect.
**       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 undo
** > fossil bisect vlist|ls|status ?-a|--all?
**
**     Undo the most recent "good" or "bad" command.
**       List the versions in between the inner-most "bad" and "good".
**
** Summary:
** > fossil bisect ui
**
**   fossil bisect bad ?VERSION?
**   fossil bisect good ?VERSION?
**   fossil bisect log
**   fossil bisect chart
**   fossil bisect next
**       Like "fossil ui" except start on a timeline that shows only the
**       check-ins that are part of the current bisect.
**
**   fossil bisect options
**   fossil bisect reset
** > fossil bisect undo
**   fossil bisect status
**   fossil bisect ui
**   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 ){
    usage("bad|good|log|next|options|reset|status|undo");
    goto usage;
  }
  zCmd = g.argv[2];
  n = strlen(zCmd);
  if( n==0 ) zCmd = "-";
  if( strncmp(zCmd, "bad", n)==0 ){
    int ridBad;
    foundCmd = 1;
334
335
336
337
338
339
340


















341
342
343
344
345
346
347
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







      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;
379
380
381
382
383
384
385




386


387
388

389


390
391
392
393
394

395
396
397
398
399
400
401
402
403
404
405
406
407


408
409
410
411
412
413
414
415
416

417
418
419
420
421
422
423
424
425

426
427
428
429
430
431
432
433
434
435
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
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







+
+
+
+
-
+
+


+

+
+





+













+
+








-
+








-
+













-
-
-
-
+

















+
-
+


  /* 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();
      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, g.comFmtFlags);
        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, g.argv[4]);
            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 ){
    db_multi_exec(
      "DELETE FROM vvar WHERE name IN "
      " ('bisect-good', 'bisect-bad', 'bisect-log')"
    );
    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|next|options|reset|status|ui|undo");
    usage("bad|good|log|chart|next|options|reset|run|skip|status|ui|undo");
  }
}

Changes to src/blob.c.

15
16
17
18
19
20
21
22
23
24
25
26

27
28
29
30
31
32
33
34
15
16
17
18
19
20
21





22

23
24
25
26
27
28
29







-
-
-
-
-
+
-







**
*******************************************************************************
**
** A Blob is a variable-length containers for arbitrary string
** or binary data.
*/
#include "config.h"
#if defined(FOSSIL_ENABLE_MINIZ)
#  define MINIZ_HEADER_FILE_ONLY
#  include "miniz.c"
#else
#  include <zlib.h>
#include <zlib.h>
#endif
#include "blob.h"
#if defined(_WIN32)
#include <fcntl.h>
#include <io.h>
#endif

#if INTERFACE
56
57
58
59
60
61
62




























63
64
65
66
67
68
69
70
71
72
73
74
75
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90

91
92
93
94
95
96
97







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





-







#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
#define BLOB_SEEK_END 3

#endif /* INTERFACE */

/*
** Make sure a blob is initialized
*/
#define blob_is_init(x) \
100
101
102
103
104
105
106

107
108
109
110
111
112
113
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136







+








/*
** 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){
150
151
152
153
154
155
156

















157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176




177
178
179
180
181
182
183
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214


215
216
217
218
219
220
221
222
223
224
225







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


















-
-
+
+
+
+







** 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<pBlob->nAlloc-4000 ){
    char *pNew = fossil_realloc(pBlob->aData, newSize);
  }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;
    }
  }
}
194
195
196
197
198
199
200


201

202
203
204
205
206
207
208
236
237
238
239
240
241
242
243
244

245
246
247
248
249
250
251
252







+
+
-
+







** 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);
    char *pNew = fossil_malloc( 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;
  }
}
274
275
276
277
278
279
280
281










282
283
284
285




286
287










288
289





290
291
292
293
294
295
296
297






298







299
300


301
302
303

304
305
306
307


308
309


310
311
312
313

314
315
316
317
318
319




















































































320
321
322
323
324
325
326
327
328
329

330


331
332
333
334











335
336
337
338
339
340
341
318
319
320
321
322
323
324

325
326
327
328
329
330
331
332
333
334
335



336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351


352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370

371
372
373
374
375
376
377
378

379
380
381
382

383




384
385


386
387
388
389
390

391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
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







-
+
+
+
+
+
+
+
+
+
+

-
-
-
+
+
+
+


+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+








+
+
+
+
+
+
-
+
+
+
+
+
+
+

-
+
+


-
+
-
-
-
-
+
+
-
-
+
+



-
+






+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+










+
-
+
+




+
+
+
+
+
+
+
+
+
+
+







  pBlob->aData = (char*)zEmpty;
  pBlob->iCursor = 0;
  pBlob->blobFlags = 0;
  pBlob->xRealloc = blobReallocStatic;
}

/*
** Append text or data to the end of a blob.
** 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.
*/
void blob_append(Blob *pBlob, const char *aData, int nData){
  assert( aData!=0 || nData==0 );
  blob_is_init(pBlob);
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( pBlob->nUsed + nData >= pBlob->nAlloc ){
    pBlob->xRealloc(pBlob, pBlob->nUsed + nData + pBlob->nAlloc + 100);
  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
** 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->nUsed+1 >= pBlob->nAlloc ){
  if( pBlob==0 || pBlob->nUsed+1 >= pBlob->nAlloc ){
    pBlob->xRealloc(pBlob, pBlob->nUsed + pBlob->nAlloc + 100);
    if( pBlob->nUsed + 1 >= pBlob->nAlloc ){
      blob_panic();
    }
    blob_append_full(pBlob, &c, 1);
  }else{
  }
  pBlob->aData[pBlob->nUsed++] = c;    
    pBlob->aData[pBlob->nUsed++] = c;
  }
}

/*
** Copy a blob
** 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 ){
  if( p->aData[p->nUsed]!=0 ){
    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){
367
368
369
370
371
372
373
374

375
376
377
378
379
380
381
542
543
544
545
546
547
548

549
550
551
552
553
554
555
556







-
+







  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(Blob *pA, Blob *pB){
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);
413
414
415
416
417
418
419
420

421
422
423
424
425
426
427
428
429
430
431
432
433
434

435
436
437
438
439
440
441
442
443
444
445
446























447
448
449
450
451
452
453
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







-
+













-
+












+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+








/*
** 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 = strlen(z);
  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)
     ((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);
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
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







-
+










-
-







  p->iCursor = 0;
}

/*
** Truncate a blob back to zero length
*/
void blob_truncate(Blob *p, int sz){
  if( sz>=0 && sz<p->nUsed ) p->nUsed = 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;
  }else if( whence==BLOB_SEEK_END ){
    p->iCursor = p->nUsed + offset - 1;
  }
  if( p->iCursor>p->nUsed ){
    p->iCursor = p->nUsed;
  }
  return p->iCursor;
}

671
672
673
674
675
676
677























































































678
679
680
681
682
683
684
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







    i++;
  }
  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' ){
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
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+













-
+
+





-
-
-
-
-
-
-
+
+
+
+
+
+
-

-
-
-
-
-
-
+
+
+
+
+
-

-
+







  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.
** 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, ...){
  if( pBlob ){
    va_list ap;
    va_start(ap, zFormat);
    vxprintf(pBlob, zFormat, ap);
    va_end(ap);
    pBlob->blobFlags |= BLOBFLAG_NotSQL;
  }
  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, ...){
  if( pBlob ){
    va_list ap;
    va_start(ap, zFormat);
    vxprintf(pBlob, zFormat, ap);
    va_end(ap);
  }
  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){
  if( pBlob ) vxprintf(pBlob, zFormat, 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.
*/
812
813
814
815
816
817
818
























819
820
821
822
823
824
825
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







        blob_append(pBlob, zBuf, n);
      }
    }
  }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.
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
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







-
+













-
+







    fflush(stdout);
    _setmode(_fileno(stdout), _O_TEXT);
#endif
  }else{
    file_mkfolder(zFilename, ExtFILE, 1, 0);
    out = fossil_fopen(zFilename, "wb");
    if( out==0 ){
#if _WIN32
#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!=blob_size(pBlob) ){
    if( nWrote!=(int)blob_size(pBlob) ){
      fossil_fatal_recursive("short write: %d of %d bytes to %s", nWrote,
         blob_size(pBlob), zFilename);
    }
  }
  return nWrote;
}

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
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







-











-
+











-







    blob_reset(&b1);
    blob_reset(&b2);
    blob_reset(&b3);
  }
  fossil_print("ok\n");
}

#if defined(_WIN32) || defined(__CYGWIN__)
/*
** 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>=p->nAlloc ){
  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';
    }
  }
}
#endif

/*
** 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;
1198
1199
1200
1201
1202
1203
1204
1205

1206
1207
1208
1209
1210
1211
1212
1519
1520
1521
1522
1523
1524
1525

1526
1527
1528
1529
1530
1531
1532
1533







-
+







      if( (z[i]<0xa0) && (cp1252[z[i]&0x1f]>=0x800) ){
        n++;
      }
      n++;
    }
  }
  j += n;
  if( j>=p->nAlloc ){
  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 ){
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
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








+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+





+
-
-
-
-
-
+
+
+
+
+

-
+

-
+



-
-
-
-
-

+
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+


+
+



+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







      }
    }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 argument zIn.
** 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
** The argument is expected to a filename of some kinds.  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 "./".
** 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){
void blob_append_escaped_arg(Blob *pBlob, const char *zIn, int isFilename){
  int i;
  char c;
  unsigned char c;
  int needEscape = 0;
  int n = blob_size(pBlob);
  char *z = blob_buffer(pBlob);
#if defined(_WIN32)
  const char cQuote = '"';    /* Use "..." quoting on windows */
#else
  const char cQuote = '\'';   /* Use '...' quoting on unix */
#endif

  /* 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 = zIn[i])!=0; i++){
    if( c==cQuote || c=='\\' || c<' ' || c==';' || c=='*' || c=='?' || c=='[') {
      Blob bad;
      blob_token(pBlob, &bad);
      fossil_fatal("the [%s] argument to the \"%s\" command contains "
                   "a character (ascii 0x%02x) that is a security risk",
                   zIn, blob_str(&bad), c);
    }
  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));
        }
    if( !needEscape && !fossil_isalnum(c) && c!='/' && c!='.' && c!='_' ){
      needEscape = 1;
        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]=='-' ){
  if( needEscape ) blob_append_char(pBlob, cQuote);
  if( zIn[0]=='-' ) blob_append(pBlob, "./", 2);
  blob_append(pBlob, zIn, -1);
  if( needEscape ) blob_append_char(pBlob, cQuote);
      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).
**
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
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







-
+







-
+


+












    blob_swap(pBlob, &temp);
    blob_reset(&temp);
  }else if( starts_with_utf16_bom(pBlob, &bomSize, &bomReverse) ){
    zUtf8 = blob_buffer(pBlob);
    if( bomReverse ){
      /* Found BOM, but with reversed bytes */
      unsigned int i = blob_size(pBlob);
      while( i>0 ){
      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_char(pBlob, 0);
    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









































25

26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

45
46
47




48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69


70
71
72
73
74
75
76
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65

66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109






110
111
112
113
114
115
116
117
118







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+



















+



+
+
+
+
















-
-
-
-
-
-
+
+







** 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?
**  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( db_exists(
        "SELECT 1 FROM tagxref"
        " WHERE tagtype>0"
        "   AND tagid=(SELECT tagid FROM tag WHERE tagname='sym-%q')",
        zBranch)!=0 ){
    fossil_fatal("branch \"%s\" already exists", zBranch);
  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");
108
109
110
111
112
113
114
115

116
117
118
119
120
121
122
123
124
125
126
127
128
129
150
151
152
153
154
155
156

157
158
159
160
161
162
163

164
165
166
167
168
169
170







-
+






-







    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 ) zColor = "#fec084";
  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 ){
    blob_appendf(&branch, "T +private *\n");
    noSign = 1;
  }

  /* Cancel all other symbolic tags */
  db_prepare(&q,
      "SELECT tagname FROM tagxref, tag"
      " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid"
150
151
152
153
154
155
156
157

158
159
160
161
162
163

164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181

182













































183
184
185
186
187
188
189
190
191
192
193






194
195
196
197
198
199
200
201
202








203
204































205
206
207
208


209
210
211
212
213
214
215
216
217
218
219
220
221
222


223
224
225
226
227
228
229
230
231
232


233
234
235

236
237
238
239
240
241

























242
243
244
245
246
247
248
191
192
193
194
195
196
197

198
199
200
201
202
203

204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221

222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277


278
279
280
281
282
283
284
285
286
287
288
289



290
291
292
293
294
295
296
297
298

299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331


332
333








334
335
336
337


338
339




340
341
342
343


344
345



346


347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382







-
+





-
+

















-
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+









-
-
+
+
+
+
+
+






-
-
-
+
+
+
+
+
+
+
+

-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


-
-
+
+
-
-
-
-
-
-
-
-




-
-
+
+
-
-
-
-




-
-
+
+
-
-
-
+
-
-




+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







    }
  }

  brid = content_put_ex(&branch, 0, 0, 0, isPrivate);
  if( brid==0 ){
    fossil_fatal("trouble committing manifest: %s", g.zErrMsg);
  }
  db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", brid);
  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 = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", brid);
  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, db_get_int("autosync-tries",1),0);
  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_MTIME            0x004 /* Include lastest check-in time */
#define BRL_ORDERBY_MTIME    0x008 /* Sort by MTIME. (otherwise sort by name)*/
#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 (which<0) then the query pulls only closed branches. If
** (which>0) then the query pulls all (closed and opened)
** branches. Else the query pulls currently-opened branches.
** 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){
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: {
      db_prepare(pQuery,
        "SELECT value FROM tagxref"
      blob_append_sql(&sql,
        "SELECT name, isprivate, mtime, mergeto FROM tmp_brlist WHERE isclosed"
        " WHERE tagid=%d AND value NOT NULL "
        "EXCEPT "
        "SELECT value FROM tagxref"
        " WHERE tagid=%d"
        "   AND rid IN leaf"
        "   AND NOT %z"
        " ORDER BY value COLLATE nocase /*sort*/",
        TAG_BRANCH, TAG_BRANCH, leaf_is_closed_sql("tagxref.rid")
      );
      break;
    }
    case BRL_BOTH: {
      db_prepare(pQuery,
        "SELECT DISTINCT value FROM tagxref"
      blob_append_sql(&sql,
        "SELECT name, isprivate, mtime, mergeto FROM tmp_brlist WHERE 1"
        " WHERE tagid=%d AND value NOT NULL"
        "   AND rid IN leaf"
        " ORDER BY value COLLATE nocase /*sort*/",
        TAG_BRANCH
      );
      break;
    }
    case BRL_OPEN_ONLY: {
      db_prepare(pQuery,
        "SELECT DISTINCT value FROM tagxref"
      blob_append_sql(&sql,
        "SELECT name, isprivate, mtime, mergeto FROM tmp_brlist "
        " WHERE tagid=%d AND value NOT NULL"
        "   AND rid IN leaf"
        "   AND NOT %z"
        "  WHERE NOT isclosed"
        " ORDER BY value COLLATE nocase /*sort*/",
        TAG_BRANCH, leaf_is_closed_sql("tagxref.rid")
      );
      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.
*/
257
258
259
260
261
262
263









































































































































































































264
265
266
267
268
269
270
271
272














273

274
275
276






277

278
279
280
281


282
283
284
285













286










287

288
289

290
291
292
293
294
295








296
297
298
299
300
301
302
303
304

305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320

321
322
323
324
325
326
327
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+









+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+



+
+
+
+
+
+
-
+



-
+
+

-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
-
+


+
-
-
-
-
-
-
+
+
+
+
+
+
+
+








-
+
-
-
-
-
-
-









-
+







                      " 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
** >  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
** >  fossil branch info BRANCH-NAME
**
**        Print information about a branch
**
**    fossil branch list|ls ?-a|--all|-c|--closed?
** >  fossil branch list|ls ?OPTIONS? ?GLOB?
** >  fossil branch lsh ?OPTIONS? ?LIMIT?
**
**        List all branches.  Use -a or --all to list all branches and
**        -c or --closed to list all closed branches.  The default is to
**        show only open branches.
**        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?
** >  fossil branch new BRANCH-NAME BASIS ?OPTIONS?
**
**        Create a new branch BRANCH-NAME off of check-in BASIS.
**
**        Supported options for this subcommand include:
**        --private             branch is private (i.e., remains local)
**        --bgcolor COLOR       use COLOR instead of automatic background
**        --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
**        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 FILE       Run commands on repository FILE
**    -R|--repository REPO       Run commands on repository REPO
**
** Summary:
**    fossil branch current
**    fossil branch info BRANCHNAME
**    fossil branch [list|ls]
**    fossil branch new
*/
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 checkout");
      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);
    }
336
337
338
339
340
341
342

343


344

345
346





347
348
349






























350
351
352
353
354
355
356


357
358


359










360
361






















362
363
364




















365
366
367

368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410

411
412
413
414
415

416

417


418
419
420
421
422

423
424
425
426
427
428
429
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







+
-
+
+

+


+
+
+
+
+



+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+






-
+
+


+
+

+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


-
+


-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-















+





+
-
+

+
+




-
+







        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 ||
  }else if( (strncmp(zCmd,"list",n)==0)||(strncmp(zCmd, "ls", 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);
    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 ? "#" : " ") ),
      fossil_print("%s%s\n", (isCur ? "* " : "  "), zBr);
    }
        (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: "
                 "current info list ls new");
                 "close current hide info list ls lsh new reopen unhide");
  }
}

static const char brlistQuery[] =
@ SELECT
@   tagxref.value,
@   max(event.mtime),
@   EXISTS(SELECT 1 FROM tagxref AS tx
@           WHERE tx.rid=tagxref.rid
@             AND tx.tagid=(SELECT tagid FROM tag WHERE tagname='closed')
@             AND tx.tagtype>0),
@   (SELECT tagxref.value
@      FROM plink CROSS JOIN tagxref
@    WHERE plink.pid=event.objid
@       AND tagxref.rid=plink.cid
@      AND tagxref.tagid=(SELECT tagid FROM tag WHERE tagname='branch')
@      AND tagtype>0),
@   count(*),
@   (SELECT uuid FROM blob WHERE rid=tagxref.rid),
@   event.bgcolor
@  FROM tagxref, tag, event
@ WHERE tagxref.tagid=tag.tagid
@   AND tagxref.tagtype>0
@   AND tag.tagname='branch'
@   AND event.objid=tagxref.rid
@ GROUP BY 1
@ ORDER BY 2 DESC;
;

/*
** This is the new-style branch-list page that shows the branch names
** together with their ages (time of last check-in) and whether or not
** they are closed or merged to another branch.
**
** Control jumps to this routine from brlist_page() (the /brlist handler)
** if there are no query parameters.
*/
static void new_brlist_page(void){
  Stmt q;
  double rNow;
  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, brlistQuery/*works-like:""*/);
  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>Age</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);
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
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







-
-
+
+
+













+

-
+












-
+

+
+
+
+
















+







+







      }
    }
    if( zBgClr && zBgClr[0] && show_colors ){
      @ <tr style="background-color:%s(zBgClr)">
    }else{
      @ <tr>
    }
    @ <td>%z(href("%R/timeline?n=100&r=%T",zBranch))%h(zBranch)</a></td>
    @ <td data-sortkey="%016llx(-iMtime)">%s(zAge)</td>
    @ <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_footer();
  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 (default behavior)
**     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 ){
534
535
536
537
538
539
540
541

542
543
544
545
546
547
548
984
985
986
987
988
989
990

991
992
993
994
995
996
997
998







-
+







  @ 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);
  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 ){
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
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







-
+






-
+




















-
+








+
+
+
+
+
+
+
+


+

+
+
+




+



+
+

+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+

-
+

      cnt++;
    }
    if( colorTest ){
      const char *zColor = hash_color(zBr);
      @ <li><span style="background-color: %s(zColor)">
      @ %h(zBr) &rarr; %s(zColor)</span></li>
    }else{
      @ <li>%z(href("%R/timeline?r=%T&n=200",zBr))%h(zBr)</a></li>
      @ <li>%z(href("%R/timeline?r=%T",zBr))%h(zBr)</a></li>
    }
  }
  if( cnt ){
    @ </ul>
  }
  db_finalize(&q);
  style_footer();
  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&n=200",zTagName))[timeline]</a>
    @  %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);
  db_prepare(&q,
    "%s AND blob.rid IN (SELECT rid FROM tagxref"
    "                     WHERE tagtype>0 AND tagid=%d AND srcid!=0)"
    " ORDER BY event.mtime DESC",
  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));
    timeline_query_for_www(), TAG_BRANCH
  );
  www_print_timeline(&q, 0, 0, 0, 0, brtimeline_extra);
  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_footer();
  style_finish_page();
}

Changes to src/browse.c.

57
58
59
60
61
62
63








64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81


82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97



















98
99
100
101
102
103
104
105

























106
107
108
109
110
111
112
113
114
115
116

117
118

119
120


121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137






138

139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156

157
158
159
160
161
162
163




164
165
166
167
168






























169
170
171
172
173








174
175
176
177
178

179
180


























181

182
183
184
185

186
187
188
189
190
191
192
193



194
195
196
197
198
199
200
201
202

203
204
205
206
207
208
209
210




211
212
213
214
215
216
217
218
219
220
221
222
223
224

225
226
227
228
229
230
231
232
233
234














235
236
237

238
239

240
241
242
243
244
245

246
247
248

249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265




















266
267
268
269
270
271
272
273
274
275

276
277


278
279
280
281
282
283
284
285
286


287

288
289

290
291
292
293
294
295
296
297
298








299
300
301
302
303
304
305
306
307

308
309
310
311
312
313
314
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88

89
90
91
92
93
94
95
96










97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115



116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158

159
160
161
162
163
164
165
166
167
168

169
170
171
172
173

174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190






191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246




247
248
249
250
251
252
253
254
255
256
257
258

259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287

288
289
290
291

292
293
294






295
296
297









298








299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314


315










316
317
318
319
320
321
322
323
324
325
326
327
328
329



330


331






332



333

















334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360

361
362
363


364
365
366
367
368
369
370
371
372
373
374
375
376

377


378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403

404
405
406
407
408
409
410
411







+
+
+
+
+
+
+
+

















-
+
+






-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-





+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+











+

-
+


+
+





-





-





+
+
+
+
+
+

+




-
-
-
-
-
-








+







+
+
+
+





+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
-
+
+
+
+
+
+
+
+




-
+


+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+



-
+


-
-
-
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
+
+
+
+












-
-
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
-
-
+
-
-
-
-
-
-
+
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







-


+
-
-
+
+









+
+
-
+
-
-
+









+
+
+
+
+
+
+
+








-
+







    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 */
  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] && g.perm.Hyperlink ){
      if( zCI ){
        char *zLink = href("%R/%s?name=%#T%s&ci=%!S", zURI, j, zPath, zREx,zCI);
        blob_appendf(pOut, "%s%z%#h</a>",
                     zSep, zLink, j-i, &zPath[i]);
      }else{
        char *zLink = href("%R/%s?name=%#T%s", zURI, j, zPath, zREx);
        blob_appendf(pOut, "%s%z%#h</a>",
                     zSep, zLink, j-i, &zPath[i]);
      }
    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]);
    }
    }else{
      blob_appendf(pOut, "%s%#h", zSep, 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
**    ci=LABEL         Show only files in this check-in.  Optional.
**    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;
  int n;
  char *zPrefix;
  Stmt q;
  const char *zCI = P("ci");
  int rid = 0;
  char *zUuid = 0;
  Blob dirname;
  Manifest *pM = 0;
  const char *zSubdirLink;
  int linkTrunk = 1;
  int linkTip = 1;
  HQuery sURI;
  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; }
  style_header("File List");
  style_adunit_config(ADUNIT_RIGHT_OK);
  sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0,
                          pathelementFunc, 0, 0);
  url_initialize(&sURI, "dir");
  cgi_query_parameters_to_url(&sURI);

  /* If the name= parameter is an empty string, make it a NULL pointer */
  if( zD && strlen(zD)==0 ){ zD = 0; }

  /* If a specific check-in is requested, fetch and parse it.  If the
  ** specific check-in does not exist, clear zCI.  zCI==0 will cause all
  ** files from all check-ins to be displayed.
  */
  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 */
  blob_zero(&dirname);
  if( zD ){
    blob_append(&dirname, "in directory ", -1);
    hyperlinked_path(zD, &dirname, zCI, "dir", "");
  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{
    blob_append(&dirname, "in the top-level directory", -1);
    @ <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 ){
  if( linkTrunk && !bDocDir ){
    style_submenu_element("Trunk", "%s",
                          url_render(&sURI, "ci", "trunk", 0, 0));
  }
  if( linkTip ){
  if( linkTip && !bDocDir ){
    style_submenu_element("Tip", "%s", url_render(&sURI, "ci", "tip", 0, 0));
  }
  if( zCI ){
    @ <h2>Files of check-in [%z(href("vinfo?name=%!S",zUuid))%S(zUuid)</a>]
    @ %s(blob_str(&dirname))
    if( zD ){
      @ &nbsp;&nbsp;%z(href("%R/timeline?chng=%T/*", zD))[history]</a>
    }
  if( zD && !bDocDir ){
    style_submenu_element("History","%R/timeline?chng=%T/*", zD);
  }
    @ </h2>
    zSubdirLink = mprintf("%R/dir?ci=%!S&name=%T", zUuid, zPrefix);
    if( nD==0 ){
      style_submenu_element("File Ages", "%R/fileage?name=%!S", zUuid);
    }
  }else{
    @ <h2>The union of all files from all check-ins
    @ %s(blob_str(&dirname))
    if( zD ){
  if( !bDocDir ){
      @ &nbsp;&nbsp;%z(href("%R/timeline?chng=%T/*", zD))[history]</a>
    }
    @ </h2>
    zSubdirLink = mprintf("%R/dir?name=%T", zPrefix);
  }
  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));
    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 ){
    Stmt ins;
    ManifestFile *pFile;
    /* Files in the specific checked given by zCI */
    ManifestFile *pPrev = 0;
    int nPrev = 0;
    int c;

    db_prepare(&ins,
       "INSERT OR IGNORE INTO localfiles VALUES(pathelement(:x,0), :u)"
    );
    manifest_file_rewind(pM);
    while( (pFile = manifest_file_next(pM,0))!=0 ){
      if( nD>0
    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
       && (fossil_strncmp(pFile->zName, zD, nD-1)!=0
           || pFile->zName[nD-1]!='/')
      ){
      );
        continue;
      }
    }
      if( pPrev
       && fossil_strncmp(&pFile->zName[nD],&pPrev->zName[nD],nPrev)==0
       && (pFile->zName[nD+nPrev]==0 || pFile->zName[nD+nPrev]=='/')
      ){
        continue;
      }
  }else{
      db_bind_text(&ins, ":x", &pFile->zName[nD]);
      db_bind_text(&ins, ":u", pFile->zUuid);
      db_step(&ins);
    /* All files across all check-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 ){
    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( 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*/");
  n = db_int(1,"SELECT count(*) FROM localfiles; /*scan*/");
  if( mxLen<12 ) mxLen = 12;
  mxLen += (mxLen+9)/10;
  db_prepare(&q,
  db_prepare(&q, "SELECT x, u FROM localfiles ORDER BY x /*scan*/");
  @ <div class="columns" style="columns: %d(mxLen)ex %d(n);">
     "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);
      if( zCI ){
      }else if( zCI ){
        const char *zUuid = db_column_text(&q, 1);
        zLink = href("%R/artifact/%!S",zUuid);
        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 LIMIT 1;"
    " '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{
343
344
345
346
347
348
349

350

351
352
353
354
355

356
357
358
359
360
361
362
440
441
442
443
444
445
446
447
448
449
450
451
452
453

454
455
456
457
458
459
460
461







+

+




-
+







        @ 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_footer();
  style_finish_page();
}

/*
** Objects used by the "tree" webpage.
*/
typedef struct FileTreeNode FileTreeNode;
typedef struct FileTree FileTree;
370
371
372
373
374
375
376



377
378
379
380
381
382
383
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485







+
+
+







  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
*/
399
400
401
402
403
404
405
406



407
408
409
410
411
412
413
501
502
503
504
505
506
507

508
509
510
511
512
513
514
515
516
517







-
+
+
+







** 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 */
  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.
  */
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
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







+
+
+
+
+
+












-
+
+
+
+






-
-
+
+







      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
** mtime (larger numbers first) and then by zName (smaller names first).
** 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->mtime>pRight->mtime ) return -1;
  if( pLeft->mtime<pRight->mtime ) return +1;
  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;
499
500
501
502
503
504
505
506
507


508
509
510
511
512
513
514
612
613
614
615
616
617
618


619
620
621
622
623
624
625
626
627







-
-
+
+







    pEnd->pSibling = pLeft;
  }else{
    pEnd->pSibling = pRight;
  }
  return base.pSibling;
}

/* Sort a list of FileTreeNode objects in mtime order. */
static FileTreeNode *sortNodesByMtime(FileTreeNode *p){
/* 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;
532
533
534
535
536
537
538
539

540
541
542

543
544

545
546
547
548
549
550
551
645
646
647
648
649
650
651

652
653
654

655
656

657
658
659
660
661
662
663
664







-
+


-
+

-
+







** This routine invalidates the following fields:
**
**     FileTreeNode.pLastChild
**     FileTreeNode.pNext
**
** Use relinkTree to reconnect the pNext pointers.
*/
static FileTreeNode *sortTreeByMtime(FileTreeNode *p){
static FileTreeNode *sortTree(FileTreeNode *p){
  FileTreeNode *pX;
  for(pX=p; pX; pX=pX->pSibling){
    if( pX->pChild ) pX->pChild = sortTreeByMtime(pX->pChild);
    if( pX->pChild ) pX->pChild = sortTree(pX->pChild);
  }
  return sortNodesByMtime(p);
  return sortNodes(p);
}

/* Reconstruct the FileTree by reconnecting the FileTreeNode.pNext
** fields in sequential order.
*/
static void relinkTree(FileTree *pTree, FileTreeNode *pRoot){
  while( pRoot ){
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
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







-
+












-
+













+
+
+

+











-


-














+


















+
+
+








+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





-
+



-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+



-
+


















-
+



-
+




+
-
+




-
+
-





-
-
-
-
-
-
+
+
+
+
+
+
+
+



+
-
+




-
+
-



+


-
-
-





-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+

-




-
+

+
+




+
+
+
+







**
**    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.
**    mtime            Order directory elements by decreasing mtime
**    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 nFile = 0;           /* Number of files (or folders with "nofiles") */
  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;
    style_header("Folder Hierarchy");
  }else{
    showDirOnly = 0;
    style_header("File Tree");
  }
  style_adunit_config(ADUNIT_RIGHT_OK);
  if( PB("expand") ){
    startExpanded = 1;
  }else{
    startExpanded = 0;
  }

  /* If a regular expression is specified, compile it */
  zRE = P("re");
  if( zRE ){
    re_compile(&pRE, zRE, 0);
    zREx = mprintf("&re=%T", zRE);
  }
  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);
    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);
    }
  }
  style_submenu_binary("mtime","Sort By Time","Sort By Filename", 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=%s", zUuid);
      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, fileage.mtime\n"
       "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 nocase;"
       " 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,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);
      tree_add_node(&sTree, zFile, zUuid, mtime, size, sortOrder);
      nFile++;
    }
    db_finalize(&q);
  }else{
    Stmt q;
    db_prepare(&q,
      "SELECT filename.name, blob.uuid, max(event.mtime)\n"
      "  FROM filename, mlink, blob, event\n"
      " WHERE mlink.fnid=filename.fnid\n"
      "   AND event.objid=mlink.mid\n"
      "   AND blob.rid=mlink.fid\n"
      " GROUP BY 1 ORDER BY 1 COLLATE nocase");
      "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,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);
      tree_add_node(&sTree, zName, zUuid, mtime, size, sortOrder);
      nFile++;
    }
    db_finalize(&q);
  }
  style_submenu_checkbox("nofiles", "Folders Only", 0, 0);

  if( showDirOnly ){
    for(nFile=0, p=sTree.pFirst; p; p=p->pNext){
      if( p->pChild!=0 && p->nFullName>nD ) nFile++;
    }
    zObjType = "Folders";
  }else{
    zObjType = "Files";
  }

  style_submenu_checkbox("nofiles", "Folders Only", 0, 0);

  if( zCI ){
    @ <h2>%s(zObjType) from
    if( sqlite3_strnicmp(zCI, zUuid, (int)strlen(zCI))!=0 ){
      @ "%h(zCI)"
  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))
    }
    @ [%z(href("vinfo?name=%!S",zUuid))%S(zUuid)</a>] %s(blob_str(&dirname))
  }else{
    int n = db_int(0, "SELECT count(*) FROM plink");
    @ <h2>%s(zObjType) from all %d(n) check-ins %s(blob_str(&dirname))
  }
  if( useMtime ){
  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)
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
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







-
+
+



-
-
+
+






-
+





+


-
+









-
+








+












-
-
+
+







    @ <li class="dir last">
  }else{
    @ <li class="dir subdir last">
  }
  @ <div class="filetreeline">
  @ %z(href("%s",url_render(&sURI,"name",0,0,0)))%h(zProjectName)</a>
  if( zNow ){
    @ <div class="filetreeage">%s(zNow)</div>
    @ <div class="filetreeage">Last Change</div>
    @ <div class="filetreesize">Size</div>
  }
  @ </div>
  @ <ul>
  if( useMtime ){
    p = sortTreeByMtime(sTree.pFirst);
  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 = p->nFullName==nD-1 ? " subdir" : "";
      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 || p->nFullName<=nD ){
      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/artifact/%!S",p->zUuid);
        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>
  style_load_one_js_file("tree.js");
  style_footer();
  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. */
}

/*
880
881
882
883
884
885
886
887
888
889
890
891





892
893
894
895
896
897
898
899
1036
1037
1038
1039
1040
1041
1042





1043
1044
1045
1046
1047

1048
1049
1050
1051
1052
1053
1054







-
-
-
-
-
+
+
+
+
+
-







@   pathname TEXT
@ );
@ CREATE VIRTUAL TABLE IF NOT EXISTS temp.foci USING files_of_checkin;
;

static const char zComputeFileAgeRun[] =
@ WITH RECURSIVE
@   ckin(x,m) AS (SELECT objid, mtime FROM event WHERE objid=:ckin
@                 UNION
@                 SELECT plink.pid, event.mtime
@                   FROM ckin, plink, event
@                  WHERE plink.cid=ckin.x AND event.objid=plink.pid
@  ckin(x) AS (VALUES(:ckin)
@              UNION
@              SELECT plink.pid
@                FROM ckin, plink
@               WHERE plink.cid=ckin.x)
@                  ORDER BY 2 DESC)
@ INSERT OR IGNORE INTO fileage(fnid, fid, mid, mtime, pathname)
@   SELECT filename.fnid, mlink.fid, mlink.mid, event.mtime, filename.name
@     FROM foci, filename, blob, mlink, event
@    WHERE foci.checkinID=:ckin
@      AND foci.filename GLOB :glob
@      AND filename.name=foci.filename
@      AND blob.uuid=foci.uuid
909
910
911
912
913
914
915
916

917
918
919
920
921
922
923
1064
1065
1066
1067
1068
1069
1070

1071
1072
1073
1074
1075
1076
1077
1078







-
+







** temporary table named "fileage" that contains the file-id for each
** files, the pathname, the check-in where the file was added, and the
** mtime on that check-in. If zGlob and *zGlob then only files matching
** the given glob are computed.
*/
int compute_fileage(int vid, const char* zGlob){
  Stmt q;
  db_multi_exec(zComputeFileAgeSetup /*works-like:"constant"*/);
  db_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;
}
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

















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







+













+






+



+
-
-
+
+
+
+
+
+
+





-
+
-


















-
-
+
+

-













-
-
+
-
+

+
-
+

-
+






-
+













-
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
*/
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
  @ %z(href("%R/info/%!S",zUuid))[%S(zUuid)]</a>
    @ <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
  @ <p>File ages are expressed relative to the check-in time of
  @ %z(href("%R/ci/%!S",zUuid))[%S(zUuid)]</a> 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 blob.uuid, filename.name, fileage.fid\n"
    "  FROM fileage, blob, filename\n"
    "SELECT filename.name, fileage.fid\n"
    "  FROM fileage, filename\n"
    " WHERE fileage.mid=:mid AND filename.fnid=fileage.fnid"
    "   AND blob.rid=fileage.fid;"
  );
  while( db_step(&q1)==SQLITE_ROW ){
    double age = baseTime - db_column_double(&q1, 0);
    int mid = db_column_int(&q1, 1);
    const char *zUuid = db_column_text(&q1, 2);
    const char *zComment = db_column_text(&q1, 3);
    const char *zUser = db_column_text(&q1, 4);
    const char *zBranch = db_column_text(&q1, 5);
    char *zAge = human_readable_age(age);
    @ <tr><td>%s(zAge)</td>
    @ <td>
    db_bind_int(&q2, ":mid", mid);
    while( db_step(&q2)==SQLITE_ROW ){
      const char *zFUuid = db_column_text(&q2,0);
      const char *zFile = db_column_text(&q2,1);
      const char *zFile = db_column_text(&q2,0);
      int fid = db_column_int(&q2,2);
      @ %z(href("%R/file?name=%T&ci=%!S",zFile,zUuid))%h(zFile)</a> \
      if( showId ){
        int fid = db_column_int(&q2,1);
        @ %z(href("%R/artifact/%!S",zFUuid))%h(zFile)</a> (%d(fid))<br />
        @ (%d(fid))<br>
      }else{
        @ %z(href("%R/artifact/%!S",zFUuid))%h(zFile)</a><br />
        @ </a><br>
      }
    }
    db_reset(&q2);
    @ </td>
    @ <td>
    @ %W(zComment)
    @ (check-in:&nbsp;%z(href("%R/ci/%!S",zUuid))%S(zUuid)</a>,
    @ (check-in:&nbsp;%z(href("%R/info/%!S",zUuid))%S(zUuid)</a>,
    if( showId ){
      @ id: %d(mid)
    }
    @ user:&nbsp;%z(href("%R/timeline?u=%t&c=%!S&nd",zUser,zUuid))%h(zUser)</a>,
    @ branch:&nbsp;\
    @ %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_footer();
  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;
}

Changes to src/builtin.c.

26
27
28
29
30
31
32
33



34
35

36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52


















53
54
55
56
57
58
59



60
61
62
63
64

65
66






67
68
69
70
71
72
73
74
75
76

77
78

79
80
81



82
83
84


85
86
87
88
89
90
91
26
27
28
29
30
31
32

33
34
35
36

37
38
39
40
41
42
43
44
45
46
47
48






49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80

81
82

83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100

101
102
103

104
105
106
107


108
109
110
111
112
113
114
115
116







-
+
+
+

-
+











-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







+
+
+




-
+

-
+
+
+
+
+
+










+

-
+


-
+
+
+

-
-
+
+







** The resources provided by this file are packaged by the "mkbuiltin.c"
** utility program during the built process and stored in the
** builtin_data.h file.  Include that information here:
*/
#include "builtin_data.h"

/*
** Return a pointer to built-in content
** Return the index in the aBuiltinFiles[] array for the file
** whose name is zFilename.  Or return -1 if the file is not
** found.
*/
const unsigned char *builtin_file(const char *zFilename, int *piSize){
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{
      if( piSize ) *piSize = aBuiltinFiles[i].nByte;
      return aBuiltinFiles[i].pData;
    }
  }
  if( piSize ) *piSize = 0;
  return 0;
      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;
  int i, size = 0;;
  for(i=0; i<count(aBuiltinFiles); i++){
    fossil_print("%-30s %6d\n", aBuiltinFiles[i].zName,aBuiltinFiles[i].nByte);
    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");
  @ <ul>
  @ <ol>
  for(i=0; i<count(aBuiltinFiles); i++){
    const char *z = aBuiltinFiles[i].zName;
    @ <li>%z(href("%R/builtin?name=%T&id=%S",z,MANIFEST_UUID))%h(z)</a>
    char *zUrl = href("%R/builtin?name=%T&id=%.8s&mimetype=text/plain",
           z,fossil_exe_id());
    @ <li>%z(zUrl)%h(z)</a>
  }
  @ </ul>
  style_footer();
  @ </ol>
  style_finish_page();
}

/*
** COMMAND: test-builtin-get
**
** Usage: %fossil test-builtin-get NAME ?OUTPUT-FILE?
*/
100
101
102
103
104
105
106

































































































































































































































































































































































































































































































































































































































































































































































































125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
  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);
}

Changes to src/bundle.c.

23
24
25
26
27
28
29
30

31
32
33
34
35
36
37
23
24
25
26
27
28
29

30
31
32
33
34
35
36
37







-
+








/*
** SQL code used to initialize the schema of a bundle.
**
** The bblob.delta field can be an integer, a text string, or NULL.
** If an integer, then the corresponding blobid is the delta basis.
** If a text string, then that string is a SHA1 hash for the delta
** basis, which is presumably in the master repository.  If NULL, then
** 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
@ );
58
59
60
61
62
63
64
65

66
67
68
69
70
71
72
73
74
75
76
77
78

79
80
81
82
83
84
85

86
87
88
89
90
91
92
58
59
60
61
62
63
64

65
66
67
68
69
70
71
72
73
74
75
76
77

78
79
80
81
82
83
84

85
86
87
88
89
90
91
92







-
+












-
+






-
+







  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_panic("out of memory");
  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_panic("out of memory");
    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_panic("out of memory");
    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);
  }
}

112
113
114
115
116
117
118
119

120
121
122
123
124
125
126
112
113
114
115
116
117
118

119
120
121
122
123
124
125
126







-
+







    fossil_print("%s: %s\n", db_column_text(&q,0), db_column_text(&q,1));
  }
  db_finalize(&q);
  fossil_print("%.78c\n",'-');
  if( bDetails ){
    db_prepare(&q,
      "SELECT blobid, substr(uuid,1,10), coalesce(substr(delta,1,10),''),"
      "       sz, length(data), notes"
      "       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),
144
145
146
147
148
149
150
151

152
153
154
155
156
157
158
144
145
146
147
148
149
150

151
152
153
154
155
156
157
158







-
+







    db_finalize(&q);
  }
}

/*
** Implement the "fossil bundle append BUNDLE FILE..." command.  Add
** the named files into the BUNDLE.  Create the BUNDLE if it does not
** alraedy exist.
** already exist.
*/
static void bundle_append_cmd(void){
  Blob content, hash;
  int i;
  Stmt q;

  verify_all_options();
528
529
530
531
532
533
534
535

536
537
538
539
540
541
542
543
544


545
546
547
548
549
550
551
528
529
530
531
532
533
534

535
536
537
538
539
540
541
542


543
544
545
546
547
548
549
550
551







-
+







-
-
+
+







    fossil_fatal("incorrect hash for artifact %b", &h1);
  }
  blob_reset(&h1);
  bag_remove(&busy, blobid);
  db_finalize(&q);
}

/* fossil bundle cat BUNDLE UUID...
/* 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 UUID...");
  bundle_attach_file(g.argv[3], "b1", 1);
  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]);
    }
567
568
569
570
571
572
573
574

575
576
577
578
579
580
581
567
568
569
570
571
572
573

574
575
576
577
578
579
580
581







-
+







*/
static void bundle_import_cmd(void){
  int forceFlag = find_option("force","f",0)!=0;
  int isPriv = find_option("publish",0,0)==0;
  char *zMissingDeltas;
  verify_all_options();
  if ( g.argc!=4 ) usage("import BUNDLE ?OPTIONS?");
  bundle_attach_file(g.argv[3], "b1", 1);
  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'"
589
590
591
592
593
594
595
596

597
598
599
600
601
602
603
589
590
591
592
593
594
595

596
597
598
599
600
601
602
603







-
+







  /* If the bundle contains deltas with a basis that is external to the
  ** bundle and those external basis files are missing from the local
  ** repo, then the delta encodings cannot be decoded and the bundle cannot
  ** be extracted. */
  zMissingDeltas = db_text(0,
      "SELECT group_concat(substr(delta,1,10),' ')"
      "  FROM bblob"
      " WHERE typeof(delta)='text' AND length(delta)>=%d"
      " 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);
  }

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
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







-
+



-
+




-
+






-
+







-
-
+
+


-
+

-
+





-
+





-
+



-
+





-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+







  }else{
    purge_artifact_list("ok",0,0);
  }
  db_end_transaction(0);
}

/*
** COMMAND: bundle
** COMMAND: bundle*
**
** Usage: %fossil bundle SUBCOMMAND ARGS...
**
**   fossil bundle append BUNDLE FILE...
** > fossil bundle append BUNDLE FILE...
**
**      Add files named on the command line to BUNDLE.  This subcommand has
**      little practical use and is mostly intended for testing.
**
**   fossil bundle cat BUNDLE UUID...
** > 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?
** > 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.
**         --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
**                                    artifacts not in the bundle
**
**   fossil bundle extend 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?
** > 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
** > fossil bundle ls BUNDLE
**
**      List the contents of BUNDLE on standard output
**
**   fossil bundle purge BUNDLE
** > fossil bundle purge BUNDLE
**
**      Remove from the repository all files that are used exclusively
**      by check-ins in BUNDLE.  This has the effect of undoing a
**      "fossil bundle import".
**
** SUMMARY:
**   fossil bundle append BUNDLE FILE...              Add files to BUNDLE
**   fossil bundle cat BUNDLE UUID...                 Extract file from BUNDLE
**   fossil bundle export BUNDLE ?OPTIONS?            Create a new BUNDLE
**          --branch BRANCH --from TAG1 --to TAG2       Check-ins to include
**          --checkin TAG                               Use only check-in TAG
**          --standalone                                Omit dependencies
**   fossil bundle extend BUNDLE                      Update with newer content
**   fossil bundle import BUNDLE ?OPTIONS?            Import a bundle
**          --publish                                   Publish the import
**          --force                                     Cross-repo import
**   fossil bundle ls BUNDLE                          List content of a bundle
**   fossil bundle purge BUNDLE                       Undo an import
**
** See also: publish
** 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);

Changes to src/cache.c.

195
196
197
198
199
200
201







202
203
204
205
206
207
208
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215







+
+
+
+
+
+
+








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
260
261
262
263
264
265
266


267
268
269
270
271
272
273
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282







+
+







**    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){
301
302
303
304
305
306
307
308
309




310
311
312
313
314
315
316
317
318
319
320
321
322
323

324
325
326
327
328






329
330
331
332
333
334
335










336
337
338
339






340
341
342

343
344



























345
346
347
348
349

350
351
352
353
354
355
356
357

358
359
360
361
362
363
364
365
366

367
368
369
370
371
372
373

374
375


376
377







378
379
380
381
382
383

384
385








386
387
388
389

390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409

410
411
412

413
414
415
416
417
310
311
312
313
314
315
316


317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335





336
337
338
339
340
341
342
343
344
345
346


347
348
349
350
351
352
353
354
355
356
357
358


359
360
361
362
363
364
365
366

367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400

401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418

419
420
421
422
423
424
425
426
427


428
429
430

431
432
433
434
435
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







-
-
+
+
+
+














+
-
-
-
-
-
+
+
+
+
+
+





-
-
+
+
+
+
+
+
+
+
+
+


-
-
+
+
+
+
+
+


-
+


+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+




-
+








+








-
+







+
-
-
+
+

-
+
+
+
+
+
+
+






+
-
-
+
+
+
+
+
+
+
+



-
+




















+


-
+





    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 )){
  }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));
            fossil_print("%s %4d %8s %s\n",
               sqlite3_column_text(pStmt, 3),
               sqlite3_column_int(pStmt, 2),
               sqlite3_column_text(pStmt, 1),
               sqlite3_column_text(pStmt, 0));
          }
          nEntry++;
        }
        sqlite3_finalize(pStmt);
      }
      sqlite3_close(db);
      fossil_print("Entries: %d  Cache-file Size: %lld\n",
                   nEntry, file_size(zDbName, ExtFILE));
      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, "status", nCmd)==0 ){
    fossil_print("TBD...\n");
  }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 status", zCmd);
                 " 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 Admin privilege.
** 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, sizename(sz), nRef, datetime(tm,'unixepoch')"
         "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: %s(sqlite3_column_text(pStmt,1))
        @ <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))</p></li>
        @ 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>
    @ <p>cache-file name: %h(zDbName)</p>
    @ <p>cache-file size: %s(zBuf)</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_footer();
  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_footer();
    style_finish_page();
    return;
  }
  cgi_set_content(&content);
  cgi_set_content_type("application/x-compressed");
}

Changes to src/capabilities.c.

89
90
91
92
93
94
95
96

97
98
99
100
101
102
103
104

105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121



122
123
124
125
126
127
128
89
90
91
92
93
94
95

96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132







-
+








+

















+
+
+







** Delete a CapabilityString object.
*/
void capability_free(CapabilityString *p){
  fossil_free(p);
}

/*
** Expand the capability string by including all capabilities for 
** 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 ){
233
234
235
236
237
238
239
240

241
242
243
244




245
246
247
248
249
250
251
237
238
239
240
241
242
243

244
245
246


247
248
249
250
251
252
253
254
255
256
257







-
+


-
-
+
+
+
+







  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 attchments to wiki or tickets" },
    "Attach", "Add attachments to wiki or tickets" },
  { 'c', CAPCLASS_TKT, 0,
    "Append-Tkt", "Append to existing tickets" },
  { 'd', CAPCLASS_WIKI|CAPCLASS_TKT, 0,
    "Delete", "Delete wiki or 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,
267
268
269
270
271
272
273
274

275
276
277
278
279
280
281
273
274
275
276
277
278
279

280
281
282
283
284
285
286
287







-
+







  { '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 respository" },
    "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,
291
292
293
294
295
296
297
298

299
300
301
302


303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320

321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338

339
340
341
342
343
344
345
297
298
299
300
301
302
303

304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327

328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345

346
347
348
349
350
351
352
353







-
+




+
+

















-
+

















-
+







  { '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", "Set or remove capability '4' from other users" },
    "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<sizeof(aCap)/sizeof(aCap[0]); i++){
    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<sizeof(aCap)/sizeof(aCap[0]); i++){
  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)">\
353
354
355
356
357
358
359










360
361
362

363

364
365


366

367
368
369


370
371

372
373
374

375
376
377

378
379
380
381
382
383
384
385



386
387
388
389
390
391
392
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381

382
383
384
385
386

387
388


389
390
391

392
393
394

395
396
397

398
399
400
401
402
403
404


405
406
407
408
409
410
411
412
413
414







+
+
+
+
+
+
+
+
+
+



+
-
+


+
+
-
+

-
-
+
+

-
+


-
+


-
+






-
-
+
+
+








/*
** 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"
    " SELECT id, fullcap(user.cap),seq,1"
                    " 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', fullcap(%Q), 10, 1"
    " SELECT 'New User Default', %Q, 110, 1"
    " UNION ALL"
    " SELECT 'Regular User', fullcap(capunion(cap)), 20, count(*) FROM user"
    " WHERE cap NOT GLOB '*[as]*'"
    " 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 'Adminstator', fullcap(capunion(cap)), 30, count(*) FROM user"
    " SELECT 'Administrator', fullcap(capunion(cap)), 300, count(*) FROM user"
    " WHERE cap GLOB '*[as]*'"
    " ORDER BY 3 ASC",
    db_get("default-perms","")
    zSelfCap, hasPubPages, zSelfCap
  );
  @ <table id='capabilitySummary' cellpadding="0" cellspacing="0" border="1">
  @ <tr><th>&nbsp;<th>Code<th>Forum<th>Tickets<th>Wiki\
  @ <tr><th>&nbsp;<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 *azType[] = { "off", "read", "write" };
    static const char *azClass[] = { "capsumOff", "capsumRead", "capsumWrite" };
    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 ){
410
411
412
413
414
415
416
417

418
419
420
421
422
423
424
425
426
427
428
429
430








431
432
433
434
435
436
437
432
433
434
435
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







-
+













+
+
+
+
+
+
+
+







      eType = 1;
    }else{
      eType = 0;
    }
    @ <td class="%s(azClass[eType])">%s(azType[eType])</td>

    /* Ticket */
    if( sqlite3_strglob("*[ascdnqtw]*",zCap)==0 ){
    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 ){

Changes to src/captcha.c.

456
457
458
459
460
461
462

463
464
465
466

467
468
469
470
471
472
473
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475







+




+







  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);
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
512
513
514
515
516
517
518

519
520
521
522
523
524
525







-







  }
  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 );
  if( strlen(zEntered)!=8 ) return 0;
  for(i=0; i<8; i++){
    char c = zEntered[i];
    if( c>='A' && c<='F' ) c += 'a' - 'A';
    if( c=='O' ) c = '0';
    z[i] = c;
  }
  if( strncmp(zDecode,z,8)!=0 ) return 0;
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
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







-
-
+
+



+
+


+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+














+




-
+







  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 />
  @ <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_footer();
  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
597
598
599
600
601
602
603

604
605
606
607
608
609
610

611
612











































































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







+






-
+


+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
  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_footer();
  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.

11
12
13
14
15
16
17
18
19
20
21
22















































23
24
25
26
27
28
29
11
12
13
14
15
16
17





18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71







-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







**
** Author contact information:
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*******************************************************************************
**
** This file contains C functions and procedures that provide useful
** services to CGI programs.  There are procedures for parsing and
** dispensing QUERY_STRING parameters and cookies, the "mprintf()"
** formatting function and its cousins, and routines to encode and
** decode strings in HTML or HTTP.
** 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>
40
41
42
43
44
45
46

47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62









63











64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81

82
83
84
85
86











87
88
89
90
91
92
93
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98







99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136

137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160







+









-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+

















-
+





+
+
+
+
+
+
+
+
+
+
+







#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(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 HTTP reply is generated in two pieces: the header and the body.
** 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];

/*
106
107
108
109
110
111
112
113
114


115
116
117
118
119
120
121
122
123
124

125
126
127
128
129
130
131

132
133
134
135
136
137
138
139

140
141
142
143
144
145







146



147
148
149
150
151
152
153
154
155
156
157

158
159
160
161
162
163
164
165
166
167
168


169
170


171
172
173





174
175
176

177
178
179



180

181
182
183
184
185
186
187
188
189
190
191
192
193

194
195
196
197
198

199
200
201






202
203
204
205
206
207
208


209
210
211
212
213
214
215
216
217
218
219
220
221
222

223
224
225

226
227
228


229
230
231

232
233
234
235
236
237
238
239
240
241
242
























243
244
245
246
247

























































































248


















249








250
251

252
253
254
255
256
257
258







259
260
261
262




263

264

265













266

267
268
269

270
271
272
273
274

275
276
277
278
279
280
281
282

283
284
285
286
287


288
289
290
291
292
293
294
295
296
297
298
299
300
301
302

303

304
305
306
307
308
309
310





311
312
313

314
315
316
317
318
319
320
321
322
323


324
325





326

327
328
329
330
331





332
333
334
335
336
337
338










339
340
341
342
343


344
345
346
347
348
349
350
351
352
353
354

355

356
357
358
359

360
361
362
363
364
365

366

367
368
369
370
371
372
373
173
174
175
176
177
178
179


180
181
182
183
184
185
186
187
188
189
190

191
192
193
194
195
196
197

198
199
200
201
202
203
204
205

206
207
208
209
210
211
212
213
214
215
216
217
218
219

220
221
222
223
224
225
226
227
228
229
230
231
232

233
234
235
236
237
238
239
240
241
242


243
244
245
246
247
248
249
250

251
252
253
254
255
256
257

258
259
260
261
262
263
264

265
266
267
268
269
270
271
272
273
274
275
276
277

278
279
280
281
282

283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298

299
300
301
302
303
304
305
306
307
308
309
310
311
312
313

314
315
316

317
318


319
320
321
322

323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358





359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
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







-
-
+
+









-
+






-
+







-
+






+
+
+
+
+
+
+
-
+
+
+










-
+









-
-
+
+


+
+


-
+
+
+
+
+


-
+



+
+
+
-
+












-
+




-
+



+
+
+
+
+
+






-
+
+













-
+


-
+

-
-
+
+


-
+











+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+


+







+
+
+
+
+
+
+
-
-
-
-
+
+
+
+

+
-
+

+
+
+
+
+
+
+
+
+
+
+
+
+
-
+


-
+
-
-
-

-
+
-
-
-
-



-
+



-
-
+
+















+
-
+
-
-
-
-
-
-
-
+
+
+
+
+

-
-
+








-
-
+
+


+
+
+
+
+
-
+



-
-
+
+
+
+
+





-
-
+
+
+
+
+
+
+
+
+
+



-
-
+
+











+
-
+



-
+






+
-
+







    default: {
      cgi_panic("bad destination");
    }
  }
}

/*
** Check to see if the header contains the zNeedle string.  Return true
** if it does and false if it does not.
** 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 reply content to what already exists.
** Append new reply content to what already exists.
*/
void cgi_append_content(const char *zData, int nAmt){
  blob_append(pContent, zData, nAmt);
}

/*
** Reset the HTTP reply text to be an empty string.
** 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 the CGI output blob.
** 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 of the CGI into a single string.
** 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 HTTP reply text.
** 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 char *zContentType = "text/html";     /* Content type of the reply */
static char *zReplyStatus = "OK";            /* Reply status description */
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
** 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 = mprintf("%s", 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
** Set the reply content to the specified BLOB.
** 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 = mprintf("%s", zStat);
  zReplyStatus = fossil_strdup(zStat);
  iReplyStatus = iStat;
}

/*
** Append text to the header of an HTTP reply
** 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.
** 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 && strncmp(g.zBaseURL, "https:", 6)==0 ){
  if( g.zBaseURL!=0 && fossil_strncmp(g.zBaseURL, "https:", 6)==0 ){
    zSecure = " secure;";
  }
  if( lifetime>0 ){
  if( lifetime!=0 ){
    blob_appendf(&extraHeader,
       "Set-Cookie: %s=%t; Path=%s; max-age=%d; HttpOnly;%s Version=1\r\n",
        zName, zValue, zPath, lifetime, zSecure);
       "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 Version=1\r\n",
       "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 strncmp(zContentType, "text/", 5)==0
    || sqlite3_strglob("application/*xml", zContentType)==0
    || sqlite3_strglob("application/*javascript", zContentType)==0;
}

      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 "";
}
** Do a normal HTTP reply

/*
** 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";
    }
    fprintf(g.httpOut, "HTTP/1.0 %d %s\r\n", iReplyStatus, zReplyStatus);
    fprintf(g.httpOut, "Date: %s\r\n", cgi_rfc822_datestamp(time(0)));
    fprintf(g.httpOut, "Connection: close\r\n");
    fprintf(g.httpOut, "X-UA-Compatible: IE=edge\r\n");
    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 );
    fprintf(g.httpOut, "Status: %d %s\r\n", iReplyStatus, zReplyStatus);
    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()));
    }
  if( g.isConst ){
  }else if( g.isConst ){
    /* isConst means that the reply is guaranteed to be invariant, even
    ** after configuration changes and/or Fossil binary recompiles. */
    fprintf(g.httpOut, "Cache-Control: max-age=31536000\r\n");
    blob_appendf(&hdr, "Cache-Control: max-age=315360000, immutable\r\n");
  }else if( etag_tag()!=0 ){
    fprintf(g.httpOut, "ETag: %s\r\n", etag_tag());
    fprintf(g.httpOut, "Cache-Control: max-age=%d\r\n", etag_maxage());
  }else{
    fprintf(g.httpOut, "Cache-control: no-cache\r\n");
    blob_appendf(&hdr, "Cache-control: no-cache\r\n");
  }
  if( etag_mtime()>0 ){
    fprintf(g.httpOut, "Last-Modified: %s\r\n",
            cgi_rfc822_datestamp(etag_mtime()));
  }

  if( blob_size(&extraHeader)>0 ){
    fprintf(g.httpOut, "%s", blob_buffer(&extraHeader));
    blob_appendf(&hdr, "%s", blob_buffer(&extraHeader));
  }

  /* Add headers to turn on useful security options in browsers. */
  fprintf(g.httpOut, "X-Frame-Options: SAMEORIGIN\r\n");
  /* This stops fossil pages appearing in frames or iframes, preventing
  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 ) {
  /* Content intended for logged in users should only be cached in
    blob_appendf(&hdr, "Content-Type: %s%s\r\n", zContentType,
  ** the browser, not some shared location.
  */
  fprintf(g.httpOut, "Content-Type: %s; charset=utf-8\r\n", zContentType);
  if( fossil_strcmp(zContentType,"application/x-fossil")==0 ){
    cgi_combine_header_and_body();
    blob_compress(&cgiContent[0], &cgiContent[0]);
  }
                 content_type_charset(zContentType));
    if( fossil_strcmp(zContentType,"application/x-fossil")==0 ){
      cgi_combine_header_and_body();
      blob_compress(&cgiContent[0], &cgiContent[0]);
    }

  if( iReplyStatus != 304 ) {
    if( is_gzippable() ){
    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]);
      fprintf(g.httpOut, "Content-Encoding: gzip\r\n");
      fprintf(g.httpOut, "Vary: Accept-Encoding\r\n");
      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;
    }
    fprintf(g.httpOut, "Content-Length: %d\r\n", total_size);
    blob_appendf(&hdr, "Content-Length: %d\r\n", total_size);
  }else{
    total_size = 0;
  }
  fprintf(g.httpOut, "\r\n");
  if( total_size>0 && iReplyStatus != 304
  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>0 ){
        fwrite(blob_buffer(&cgiContent[i]), 1, size, g.httpOut);
      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;
      }
    }
  }
  fflush(g.httpOut);
  CGIDEBUG(("DONE\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
** Do a redirect request to the URL given in the argument.
** to the URL given in the argument.
**
** The URL must be relative to the base of the fossil server.
*/
NORETURN static void cgi_redirect_with_status(
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
  if( strncmp(zURL,"http:",5)==0 || strncmp(zURL,"https:",6)==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{
389
390
391
392
393
394
395





















396
397
398
399

400
401
402
403
404
405
406
407
408
409
410

411
412

413
414
415
416
417
418
419
420


421
422

423
424
425


426
427
428
429

430
431
432
433
434


























































435
436


437
438
439
440
441
442
443
444
445
446
447

448
449
450
451
452
453
454
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



-
+











+

-
+
-
-
-
-
-

-
-
+
+

-
+
-
-
-
+
+
-
-

-
+





+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+











+







}
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.
** "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 appears to be safe from a
** Return true if the current request is coming from the same origin.
** Cross-Site Request Forgery (CSRF) attack.  Conditions that must
** be met:
**
**    *   The HTTP_REFERER must have the same origin
**    *   The REQUEST_METHOD must be POST - or requirePost==0
*/
int cgi_csrf_safe(int requirePost){
  const char *zRef = P("HTTP_REFERER");
int cgi_same_origin(void){
  const char *zRef;
  int nBase;
  if( zRef==0 ) return 0;
  if( g.zBaseURL==0 ) return 0;
  if( requirePost ){
    const char *zMethod = P("REQUEST_METHOD");
    if( zMethod==0 ) return 0;
  zRef = P("HTTP_REFERER");
  if( zRef==0 ) return 0;
    if( strcmp(zMethod,"POST")!=0 ) return 0;
  }
  nBase = (int)strlen(g.zBaseURL);
  if( strncmp(g.zBaseURL,zRef,nBase)!=0 ) return 0;
  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 and cookies are stored
** in these variables.
** 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.
**
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
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







+



+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+









-
+


-
+







  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(mprintf("%s",zName), mprintf("%s",zValue), 0);
  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(mprintf("%s",zName), mprintf("%s",zValue), 1);
  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;
509
510
511
512
513
514
515





516
517
518
519
520
521
522
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878







+
+
+
+
+







    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;
541
542
543
544
545
546
547
548
549


550
551
552

553
554
555
556
557
558
559
560
561
897
898
899
900
901
902
903


904
905
906
907

908
909

910
911
912
913
914
915
916







-
-
+
+


-
+

-







      }
      return;
    }
  }
}

/*
** Add a query parameter.  The zName portion is fixed but a copy
** must be made of zValue.
** 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, mprintf("%s",zValue), 0);
  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
601
602
603
604
605
606
607

608
609





610
611
612
613
614
615
616
956
957
958
959
960
961
962
963


964
965
966
967
968
969
970
971
972
973
974
975







+
-
-
+
+
+
+
+







        z++;
      }
      dehttpize(zValue);
    }else{
      if( *z ){ *z++ = 0; }
      zValue = "";
    }
    if( zName[0] && fossil_no_strange_characters(zName+1) ){
    if( fossil_islower(zName[0]) && fossil_no_strange_characters(zName+1) ){
      cgi_set_parameter_nocopy(zName, zValue, isQP);
      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 */
  }
}

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
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







-
-
+
+




-
+





-
+





-
+


-
+
+



-
+







  *pz = &z[i];
  *pLen -= i;
  return z;
}

/*
** The input *pz points to content that is terminated by a "\r\n"
** followed by the boundry marker zBoundry.  An extra "--" may or
** may not be appended to the boundry marker.  There are *pLen characters
** 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 boundry.
** 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 *zBoundry,    /* Boundry text marking the end of content */
  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 nBoundry = strlen(zBoundry);
  int nBoundary = strlen(zBoundary);
  *pnContent = len;
  for(i=0; i<len; i++){
    if( z[i]=='\n' && strncmp(zBoundry, &z[i+1], nBoundry)==0 ){
    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 += nBoundry;
      i += nBoundary;
      break;
    }
  }
  *pz = &z[i];
  get_line_from_string(pz, pLen);
  return z;
}
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
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







-
+





-
-
+
+



-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+















+
-
-
+
+
+
+
+
+




+
-
-
+
+
+
+
+
+










-
-
-
-
-
-
-
-
+
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
+
+
+
-
-

-
+

-
-
-

-
+
-
-
-
-
-
+
-
-
-
+


















-
+







** 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 *zBoundry;
  char *zBoundary;
  char *zValue;
  char *zName = 0;
  int showBytes = 0;
  char *azArg[50];

  zBoundry = get_line_from_string(&z, &len);
  if( zBoundry==0 ) return;
  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, zBoundry, &nContent);
      if( zName && zValue && 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);
      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( zName && z && fossil_islower(zName[0]) ){
            cgi_set_parameter_nocopy(mprintf("%s:filename",zName), z, 1);
            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( zName && z && fossil_islower(zName[0]) ){
            cgi_set_parameter_nocopy(mprintf("%s:mimetype",zName), z, 1);
            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
/*
** Internal helper for cson_data_source_FILE_n().
*/
typedef struct CgiPostReadState_ {
    FILE * fh;
    unsigned int len;
    unsigned int pos;
} CgiPostReadState;

** Reads a JSON object from the given blob, which is assumed to have
/*
** cson_data_source_f() impl which reads only up to
** a specified amount of data from its input FILE.
** state MUST be a full populated (CgiPostReadState*).
** been populated by the caller from stdin, the SSL API, or a file, as
*/
static int cson_data_source_FILE_n( void * state,
                                    void * dest,
                                    unsigned int * n ){
    if( ! state || !dest || !n ) return cson_rc.ArgError;
    else {
      CgiPostReadState * st = (CgiPostReadState *)state;
      if( st->pos >= st->len ){
        *n = 0;
        return 0;
      }else if( !*n || ((st->pos + *n) > st->len) ){
        return cson_rc.RangeError;
      }else{
        unsigned int rsz = (unsigned int)fread( dest, 1, *n, st->fh );
        if( ! rsz ){
          *n = rsz;
          return feof(st->fh) ? 0 : cson_rc.IOError;
        }else{
          *n = rsz;
          st->pos += *n;
          return 0;
        }
      }
    }
}

** appropriate for the particular use case. On success g.json.post is
/*
** Reads a JSON object from the first contentLen bytes of zIn.  On
** 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).
** 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).
**
** If contentLen is 0 then the whole file is read.
*/
void cgi_parse_POST_JSON( FILE * zIn, unsigned int contentLen ){
void cgi_parse_POST_JSON( Blob * pIn ){
  cson_value * jv = NULL;
  int rc;
  CgiPostReadState state;
  cson_parse_opt popt = cson_parse_opt_empty;
  cson_parse_info pinfo = cson_parse_info_empty;
  assert(g.json.gc.a && "json_main_bootstrap() was not called!");
  assert(g.json.gc.a && "json_bootstrap_early() was not called!");
  popt.maxDepth = 15;
  state.fh = zIn;
  state.len = contentLen;
  state.pos = 0;
  rc = cson_parse( &jv,
  jv = cson_parse_Blob(pIn, &pinfo);
                   contentLen ? cson_data_source_FILE_n : cson_data_source_FILE,
                   contentLen ? (void *)&state : (void *)zIn, &popt, &pinfo );
  if(rc){
  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 );
      free(msg);
      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);
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
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







-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+










+
+
+
+







+
+
+
+
+



+
+
+
+
+
+
+
+
+
+
+
+
+




+




+
+
+
+


-
+



+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


+




+
+
+
-
+
+






+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


-
+

+
+
+
-
-
+
+
-
-
-
-
-
+
+



-
+


-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


-
-
-
-
-
+
+
+
-
-
-
+
-
-
-
-
+
+
-

-
-
-
-


-
-
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-




-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







      return;
    }
  }
  fputs(z, pLog);
}

/* Forward declaration */
static NORETURN void malformed_request(const char *zMsg);
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
  json_main_bootstrap();
  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 ) malformed_request("missing SCRIPT_NAME");
  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);
      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("PATH_INFO", mprintf("%.*s", j-i, zRequestUri+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 = mprintf("%s",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);
  }

    }
  }
  z = (char*)P("QUERY_STRING");
  if( z ){
    z = mprintf("%s",z);
    add_param_list(z, '&');
  }

  cgi_setup_query_string();

  z = (char*)P("REMOTE_ADDR");
  if( z ){
    g.zIpAddr = mprintf("%s", z);
    g.zIpAddr = fossil_strdup(z);
  }

  len = atoi(PD("CONTENT_LENGTH", "0"));
  g.zContentType = zType = P("CONTENT_TYPE");
  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( fossil_strcmp(zType,"application/x-www-form-urlencoded")==0
         || strncmp(zType,"multipart/form-data",19)==0 ){
      z = fossil_malloc( len+1 );
      len = fread(z, 1, len, g.httpIn);
      z[len] = 0;
    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));
      cgi_trace(z);
      if( zType[0]=='a' ){
        add_param_list(z, '&');
      malformed_request(zMsg);
      }else{
        process_multipart_form_data(z, len);
      }
    }else if( fossil_strcmp(zType, "application/x-fossil")==0 ){
    }
    if( fossil_strcmp(zType, "application/x-fossil")==0 ){
      blob_read_from_channel(&g.cgiIn, g.httpIn, len);
      blob_uncompress(&g.cgiIn, &g.cgiIn);
    }else if( fossil_strcmp(zType, "application/x-fossil-debug")==0 ){
      blob_read_from_channel(&g.cgiIn, g.httpIn, len);
    }else if( fossil_strcmp(zType, "application/x-fossil-uncompressed")==0 ){
      blob_read_from_channel(&g.cgiIn, g.httpIn, len);
    }
#ifdef FOSSIL_ENABLE_JSON
    else if( fossil_strcmp(zType, "application/json")
              || fossil_strcmp(zType,"text/plain")/*assume this MIGHT be JSON*/
              || fossil_strcmp(zType,"application/javascript")){
      g.json.isJsonMode = 1;
      cgi_parse_POST_JSON(g.httpIn, (unsigned int)len);
    if( noJson==0 && g.json.isJsonMode!=0
        && json_can_consume_content_type(zType)!=0 ){
      cgi_parse_POST_JSON(&g.cgiIn);
      /* FIXMEs:

      - See if fossil really needs g.cgiIn to be set for this purpose
      (i don't think it does). If it does then fill g.cgiIn and
      refactor to parse the JSON from there.

      - After parsing POST JSON, copy the "first layer" of keys/values
      to cgi_setenv(), honoring the upper-case distinction used
      in add_param_list(). However...

      - If we do that then we might get a disconnect in precedence of
      GET/POST arguments. i prefer for GET entries to take precedence
      over like-named POST entries, but in order for that to happen we
      need to process QUERY_STRING _after_ reading the POST data.
      */
      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){
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
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







+
+
+
+









+












-
+










+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







      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( zName && fossil_isupper(zName[0]) ){
  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;
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
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+


+
+
+
+
+
+
+

-
+




-
-
-
+
+
+
+
+
+

+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







    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.  Format the
** parameters as HTML.  This is used for testing and debugging.
** 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, int onConsole){
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;
    if( !showAll ){
      if( fossil_stricmp("HTTP_COOKIE",zName)==0 ) continue;
      if( fossil_strnicmp("fossil-",zName,7)==0 ) continue;
    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 ){
    if( onConsole ){
      fossil_trace("%s = %s\n", zName, aParamQP[i].zValue);
    }else{
      cgi_printf("%h = %h  <br />\n", zName, aParamQP[i].zValue);
    }
      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.
*/
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
















1319
1320
1321
1322
1323
1324
1325
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







-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







  vxprintf(pContent,zFormat,ap);
}


/*
** Send a reply indicating that the HTTP request was malformed
*/
static NORETURN void malformed_request(const char *zMsg){
  cgi_set_status(501, "Not Implemented");
  cgi_printf(
    "<html><body><p>Bad Request: %s</p></body></html>\n", zMsg
  );
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.
*/
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
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







+


+
-
-
+
+







-
-
-
+
+
+
+
+
+





-
+












-
+

+



-
+







** 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( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
    malformed_request("missing HTTP header");
  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");
  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 URL in HTTP header");
    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 = mprintf("%s", zIpAddr);
    g.zIpAddr = fossil_strdup(zIpAddr);
  }


  /* Get all the optional fields that follow the first line.
  */
  while( fgets(zLine,sizeof(zLine),g.httpIn) ){
  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;
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
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







+

+

+
+
+










+
+



-
+


+
+
+
+
+
+
-
-
+
+
+
+







      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 = mprintf("%s", zIpAddr);
        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
1528
1529
1530
1531
1532
1533
1534




1535
1536
1537
1538

1539
1540
1541

1542

1543
1544
1545
1546
1547
1548
1549
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







+
+
+
+



-
+


-
+

+







  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 = mprintf("%s", zIpAddr);
      g.zIpAddr = fossil_strdup(zIpAddr);
    }
  }else{
    fossil_panic("missing SSH IP address");
    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");
1593
1594
1595
1596
1597
1598
1599
1600

1601
1602
1603
1604
1605
1606
1607
2242
2243
2244
2245
2246
2247
2248

2249
2250
2251
2252
2253
2254
2255
2256







-
+







  }

  for(i=0; zToken[i] && zToken[i]!='?'; i++){}
  if( zToken[i] ) zToken[i++] = 0;
  if( nCycles==0 ){
    cgi_setenv("PATH_INFO", zToken);
  }else{
    cgi_replace_parameter("PATH_INFO", mprintf("%s",zToken));
    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;
1615
1616
1617
1618
1619
1620
1621
1622

1623
1624
1625
1626
1627
1628
1629
2264
2265
2266
2267
2268
2269
2270

2271
2272
2273
2274
2275
2276
2277
2278







-
+







    zVal[i] = 0;
    for(i=0; zFieldName[i]; i++){
      zFieldName[i] = fossil_tolower(zFieldName[i]);
    }
    if( fossil_strcmp(zFieldName,"content-length:")==0 ){
      content_length = atoi(zVal);
    }else if( fossil_strcmp(zFieldName,"content-type:")==0 ){
      g.zContentType = zType = mprintf("%s", zVal);
      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);
1666
1667
1668
1669
1670
1671
1672

1673
1674
1675
1676
1677
1678
1679
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329







+







}

/*
** 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 ){
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
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







-
+










+







    }
  }

  /* Got all probes now first transport_open is completed
  ** so return the command that was requested
  */
  g.fSshClient |= CGI_SSH_COMPAT;
  return mprintf("%s", zToken);
  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 ){
1752
1753
1754
1755
1756
1757
1758

1759
1760
1761
1762
1763
1764
1765
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417







+







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");
1783
1784
1785
1786
1787
1788
1789

1790
1791
1792
1793
1794
1795
1796
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449







+







** 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".
*/
1831
1832
1833
1834
1835
1836
1837
1838

1839
1840
1841
1842
1843
1844
1845
2484
2485
2486
2487
2488
2489
2490

2491
2492
2493
2494
2495
2496
2497
2498







-
+







  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 == (-1) ){
      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);
    }
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
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







-
+








-
+
+






-
+







-
+



















+
+
+
-
+
+







      iPort++;
      continue;
    }
    break;
  }
  if( iPort>mxPort ){
    if( mnPort==mxPort ){
      fossil_fatal("unable to open listening socket on ports %d", mnPort);
      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":"HTTP",  iPort);
     (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( strncmp(zBrowser, "echo ", 5)==0 ){
    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( system(zBrowser)<0 ){
    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();
          child = fork();
        }
        if( child!=0 ){
          if( child>0 ){
            nchildren++;
            nRequest++;
          }
          close(connection);
        }else{
1941
1942
1943
1944
1945
1946
1947
1948

1949
1950
1951
1952
1953
1954
1955
2599
2600
2601
2602
2603
2604
2605

2606
2607
2608
2609
2610
2611
2612
2613







-
+







        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;
}
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
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



















-
+







    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( !strncmp( azMonths[mon], zMonth, 3 )){
      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;
2048
2049
2050
2051
2052
2053
2054
2055

2056
2057
2058
2059
2060
2061
2062
2736
2737
2738
2739
2740
2741
2742

2743
2744
2745
2746
2747
2748
2749
2750







-
+







** its IP or return default
*/
const char *cgi_ssh_remote_addr(const char *zDefault){
  char *zIndex;
  const char *zSshConn = fossil_getenv("SSH_CONNECTION");

  if( zSshConn && zSshConn[0] ){
    char *zSshClient = mprintf("%s",zSshConn);
    char *zSshClient = fossil_strdup(zSshConn);
    if( (zIndex = strchr(zSshClient,' '))!=0 ){
      zSshClient[zIndex-zSshClient] = '\0';
      return zSshClient;
    }
  }
  return zDefault;
}
2078
2079
2080
2081
2082
2083
2084









































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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
*/
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)">&#128065;</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">&#9881;</span>
  @       <span class='cbutton' id="chat-button-submit" \
  @         title="Send message (Ctrl-Enter)">&#128228;</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.

59
60
61
62
63
64
65
66




67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85

86
87
88
89
90
91

92
93

94
95
96
97
98

99
100
101




102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120

121
122
123
124
125
126
127
59
60
61
62
63
64
65

66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87

88
89
90
91
92
93

94
95

96
97
98
99
100
101
102
103


104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125

126
127
128
129
130
131
132
133







-
+
+
+
+


















-
+





-
+

-
+





+

-
-
+
+
+
+


















-
+







};

/*
** 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 checkout.
** 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);
    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, RepoFILE);
      isDir = file_isdir(zName, SymFILE);
      if( isDir==1 ){
        vfile_scan(&name, nRoot-1, scanFlags, pIgnore, 0);
        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) VALUES(%Q)",
           &zName[nRoot]
            "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 rewrittenPathname;
  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;
148
149
150
151
152
153
154
155


156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176

177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196

197
198
199
200
201
202

203
204
205
206
207
208
209
210
211
212

213
214
215
216
217
218
219
154
155
156
157
158
159
160

161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182

183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202

203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228







-
+
+




















-
+



















-
+






+










+








  /* 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"
      "       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"
      " 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 checkout version ID to the query if needed. */
  /* 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";
258
259
260
261
262
263
264
265
266
267
268
269
270
271






272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295



296

297

298
299
300










301
302

303
304
305
306

307
308
309
310
311
312

313
314
315
316
317
318
319
320
267
268
269
270
271
272
273


274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303

304



305
306
307
308
309

310



311
312
313
314
315
316
317
318
319
320
321

322
323
324
325
326
327
328
329
330
331
332

333

334
335
336
337
338
339
340







-
-





+
+
+
+
+
+



















-

-
-
-
+
+
+

+
-
+
-
-
-
+
+
+
+
+
+
+
+
+
+

-
+




+





-
+
-







      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_RENAMED) && isRenamed ){
      zClass = "RENAMED";
    }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. */
        const char *zDisplayName;
        file_relative_name(zFullName, &rewrittenPathname, 0);
        zDisplayName = blob_str(&rewrittenPathname);
        if( zDisplayName[0]=='.' && zDisplayName[1]=='/' ){
          zDisplayName += 2;  /* no unnecessary ./ prefix */
        zPathname = blob_str(&rewrittenPathname);
        if( zPathname[0]=='.' && zPathname[1]=='/' ){
          zPathname += 2;  /* no unnecessary ./ prefix */
        }
        if( (flags & (C_FILTER ^ C_RENAMED)) && zOrigName ){
        blob_append(report, zDisplayName, -1);
          char *zOrigFullName = mprintf("%s%s", g.zLocalRoot, zOrigName);
      }else{
        /* If not C_RELPATH, display paths relative to project root. */
        blob_append(report, zPathname, -1);
          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_append(report, "\n", 1);
      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 uuid, id FROM vmerge JOIN blob ON merge=rid"
    db_prepare(&q, "SELECT mhash, id FROM vmerge WHERE id<=0" );
                   " 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) ){
353
354
355
356
357
358
359
360

361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378

379
380
381
382
383
384
385
373
374
375
376
377
378
379

380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397

398
399
400
401
402
403
404
405







-
+

















-
+








/*
** COMMAND: changes
** COMMAND: status
**
** Usage: %fossil changes|status ?OPTIONS? ?PATHS ...?
**
** Report the change status of files in the current checkout.  If one or
** 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
** 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.
**
395
396
397
398
399
400
401
402

403
404
405
406
407
408
409
410
411
412
413
414

415
416

417
418
419
420
421




422
423
424
425
426
427




428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444















445
446

447
448
449
450
451
452
453
415
416
417
418
419
420
421

422
423
424
425
426
427
428
429
430
431
432
433

434
435

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







-
+











-
+

-
+

-
-
-
-
+
+
+
+


-
-
-
-
+
+
+
+


-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+







** 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 checkout directory.  In addition to 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.
**    --abs-paths       Display absolute pathnames
**    --rel-paths       Display pathnames relative to the current working
**                      directory.
**                      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.
**                      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.
**    --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.
**    --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
** 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[] = {
503
504
505
506
507
508
509
510

511
512
513

514
515
516
517
518
519
520
523
524
525
526
527
528
529

530
531
532

533
534
535
536
537
538
539
540







-
+


-
+







  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 checkout. */
  /* Confirm current working directory is within check-out. */
  db_must_be_within_tree();

  /* Get checkout version. l*/
  /* 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;
  }

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
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







-
+




















-
-
-
-
-
+
+
+
+
+
+
+

-
+










+










+
+
+







}

/*
** COMMAND: ls
**
** Usage: %fossil ls ?OPTIONS? ?PATHS ...?
**
** List all files in the current checkout.  If PATHS is included, only the
** 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 FILE  Extract info from repository FILE.
**   --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
** 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 ){
733
734
735
736
737
738
739
740

741
742
743
744
745
746
747
759
760
761
762
763
764
765

766
767
768
769
770
771
772
773







-
+







       " %s (pathname=%Q %s) "
       "OR (pathname>'%q/' %s AND pathname<'%q0' %s)",
       (blob_size(&where)>0) ? "OR" : "WHERE", zName,
       filename_collation(), zName, filename_collation(),
       zName, filename_collation()
    );
  }
  vfile_check_signature(vid, 0);
  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*/
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
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







-
+













-
-
-
-
-
-
-
+
+
+
+
+
+
+

-
+








/*
** COMMAND: extras
**
** Usage: %fossil extras ?OPTIONS? ?PATH1 ...?
**
** Print a list of all files in the source tree that are not part of the
** current checkout. See also the "clean" command. If paths are specified,
** 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.
**    --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
** 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;
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
881
882
883
884
885
886
887


888
889
890
891
892
893
894







-
-







  /* We should be done with options.. */
  verify_all_options();

  if( zIgnoreFlag==0 ){
    zIgnoreFlag = db_get("ignore-glob", 0);
  }
  pIgnore = glob_create(zIgnoreFlag);
  /* Always consider symlinks. */
  g.allowSymlinks = db_allow_symlinks_by_default();
  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 ){
878
879
880
881
882
883
884
885

886
887
888
889
890
891
892
902
903
904
905
906
907
908

909
910
911
912
913
914
915
916







-
+








/*
** 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 checkout.  If one or more PATH
** 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
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
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







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+







**
** 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 checkouts
**                     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      This option disables prompting the user for input
**                     and assumes an answer of 'No' for every question.
**    --temp           Remove only Fossil-generated temporary files.
**    -v|--verbose     Show all files as they are removed.
**    --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
** 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;
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1038
1039
1040
1041
1042
1043
1044


1045
1046
1047
1048
1049
1050
1051







-
-







  }
  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);
  /* Always consider symlinks. */
  g.allowSymlinks = db_allow_symlinks_by_default();
  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"
1104
1105
1106
1107
1108
1109
1110
1111

1112
1113
1114
1115
1116
1117
1118
1126
1127
1128
1129
1130
1131
1132

1133
1134
1135
1136
1137
1138
1139
1140







-
+







  }
  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);
                   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)
    );
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
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







-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-



















-
+









+
-
+




-

+
+
+
+
+
+
+







  const char *zEditor;
  char *zCmd;
  char *zFile;
  Blob reply, line;
  char *zComment;
  int i;

  zEditor = db_get("editor", 0);
  if( zEditor==0 ){
    zEditor = fossil_getenv("VISUAL");
  zEditor = fossil_text_editor();
  }
  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);
    blob_add_cr(pPrompt);
#endif
  }
#endif
  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");
      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 \"%s\"", zEditor, zFile);
    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;
      }
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
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







-
+
+


















+
+
+
+







**
** 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 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 ){
1333
1334
1335
1336
1337
1338
1339
1340
















































1341
1342
1343




































































1344
1345
1346
1347
1348
1349
1350
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








+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







  }
  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.
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
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







-
+



-
-
-
-
-
-
+
-
-





+
+







      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 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
  int b;
  b = db_exists(
    "SELECT 1 FROM event"
    " WHERE datetime(mtime)>=%Q"
    "   AND type='ci' AND objid=%d",
    zDate, rid
  if(checkin_is_younger(rid,zDate)==0){
  );
  if( b ){
    fossil_fatal("ancestor check-in [%S] (%s) is not older (clock skew?)"
                 " Use --allow-older to override.", zUuid, zDate);
  }
#endif
}



/*
** zDate should be a valid date string.  Convert this string into the
** 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){
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
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







+















-
+






-
+


















-
+







*/
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,  /* UUID of baseline, or zero */
  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;      /* UUID of parent 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 checkout/repo mismatch.", vid);
                   "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++;
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
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







-
+












-
-
+
+

-




-
-
+
+
-







    db_prepare(&q, "SELECT merge FROM vmerge WHERE id=0 OR id<-2");
    while( db_step(&q)==SQLITE_ROW ){
      char *zMergeUuid;
      int mid = db_column_int(&q, 0);
      if( (!g.markPrivate && content_is_private(mid)) || (mid == vid) ){
        continue;
      }
      zMergeUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", mid);
      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 || blob.uuid, merge"
    "  FROM vmerge, blob"
    "SELECT CASE vmerge.id WHEN -1 THEN '+' ELSE '-' END || mhash, merge"
    "  FROM vmerge"
    " WHERE (vmerge.id=-1 OR vmerge.id=-2)"
    "   AND blob.rid=vmerge.merge"
    " 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( mid != vid ){
      blob_appendf(pOut, "Q %s\n", zCherrypickUuid);
    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 */
1665
1666
1667
1668
1669
1670
1671
1672

1673
1674
1675
1676
1677
1678
1679








1680
1681
1682
1683
1684
1685
1686
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







-
+







+
+
+
+
+
+
+
+







  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 uuid,merge FROM vmerge JOIN blob ON merge=rid"
  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++){
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
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







+












+
+





+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+




-
-







** 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);
  if( fUnicode || fHasAnyCr || fBinary || fHasInvalidUtf8 ){
    const char *zWarning;
    const char *zDisable;
    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 ){
      int fHasNul = (lookFlags & LOOK_NUL); /* contains NUL chars? */
      int fHasLong = (lookFlags & LOOK_LONG); /* overly long line? */
      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{
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
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







+
+
+








+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+







        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 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);
      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{
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
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







-
-
+
+
















-
+












+





+














-
+







+



-
+







          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 in %s",
                   zWarning, blob_str(&fname));
      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 checkout, including unmodified ones, using all
** 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;
    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, 2,
    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;
  }
1935
1936
1937
1938
1939
1940
1941
1942

1943
1944
1945

1946
1947
1948

1949
1950
1951
1952
1953
1954
1955
2112
2113
2114
2115
2116
2117
2118

2119
2120
2121
2122
2123
2124
2125

2126
2127
2128
2129
2130
2131
2132
2133







-
+



+


-
+







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: 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
** checkout.  You will be prompted to enter a check-in comment unless
** 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
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
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







-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+



-
-
-
+
+
+
+
+
-
-
-
-
+
+








-
+









-
+


+
+


+
+







**
** 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
**    --close                    close the branch being committed
**    --delta                    use a delta manifest in the commit process
**    --integrate                close all merged-in branches
**    -m|--comment COMMENT-TEXT  use COMMENT-TEXT as commit comment
**    -M|--message-file FILE     read the commit comment from given file
**    --mimetype MIMETYPE        mimetype of check-in comment
**    --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
**    --nosign                   do not attempt to sign this commit with gpg
**    --private                  do not sync changes and their descendants
**    --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
**    --hash                     verify file status using hashing rather
**                               than relying on file mtimes
**    --tag TAG-NAME             assign given tag TAG-NAME to the check-in
**    --date-override DATETIME   DATE to use instead of 'now'
**    --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, checkout, extras, sync
** 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;           /* UUID of the new check-in */
  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 */
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
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







-
-
-
+
+
+
+
+
+
+






+


+
+
+
+
-
-
+
+
+
+
+
+
+










+



+
+



+
+
+
+



+








-
-
-
-
-
-
-


-



+

+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



















+
+
+
+
+
+
+
+
+
+
+
+
+





-
-
+
+
-
-
-
-
+
+
-
-
-
-
-
-
-
+
-
-
+
+
+

-
-
-
-
-
+
+
+
+
+
+
+





+
+
+
-
+














-
+







  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;
  Blob ans;
  char cReply;
  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 && forceBaseline ){
    fossil_fatal("cannot use --delta and --baseline together");
  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);
  if( find_option("private",0,0) ){
    g.markPrivate = 1;
    if( sCiInfo.zBranch==0 ) sCiInfo.zBranch = "private";
    if( sCiInfo.zBrClr==0 && sCiInfo.zColor==0 ){
      sCiInfo.zBrClr = "#fec084";  /* Orange */
    }
  }
  sCiInfo.zDateOvrd = find_option("date-override",0,1);
  sCiInfo.zUserOvrd = find_option("user-override",0,1);
  db_must_be_within_tree();
  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.
  */
  if( !forceDelta && !db_get_boolean("seen-delta-manifest",0) ){
  **
  ** The forbid-delta-manifests setting prevents new delta manifests.
    forceBaseline = 1;
  }

  /* Get the ID of the parent manifest artifact */
  **
  ** If the remote repository sent an avoid-delta-manifests pragma on
  vid = db_lget_int("checkout", 0);
  if( vid==0 ){
    useCksum = 1;
  }else if( content_is_private(vid) ){
    g.markPrivate = 1;
  }

  ** the autosync above, then also try to avoid deltas, unless the
  /*
  ** Autosync if autosync is enabled and this is not a private check-in.
  ** --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( !g.markPrivate ){
    if( autosync_loop(SYNC_PULL, db_get_int("autosync-tries", 1), 1) ){
      fossil_exit(1);
    }
  }
  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.");
    if( !noPrompt ){
    }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
  ** 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.
  */
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
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







+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







-



-
+


+
-
-
-
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
















-
+









-
+







+






-
+













+
-
-
-
-
-
+
+
+
+
+
+
+







        " 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".
  */
    /*
    ** Do not allow a commit that will cause a fork unless the --allow-fork
    ** or --force flags is used, or unless this is a private check-in.
    ** The initial commit MUST have tags "trunk" and "sym-trunk".
    */
  if( !vid ){
    if( sCiInfo.zBranch==0 ){
      if( allowFork==0 && forceFlag==0 && g.markPrivate==0
    if( sCiInfo.zBranch==0
     && allowFork==0
        && db_exists("SELECT 1 from event where type='ci'") ){
        fossil_fatal("would fork.  \"update\" first or use --allow-fork.");
      }
      sCiInfo.zBranch = db_get("main-branch", "trunk");
    }
  }else if( sCiInfo.zBranch==0 && allowFork==0 && forceFlag==0
    && g.markPrivate==0 && !is_a_leaf(vid)
  ){
    fossil_fatal("would fork.  \"update\" first or use --allow-fork.");
  }

  /*
  ** Do not allow a commit against a closed leaf unless the commit
  ** ends up on a different branch.
  */
  if(
      /* parent check-in has the "closed" tag... */
     && 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... */
      db_exists("SELECT 1 FROM tagxref"
                " WHERE tagid=%d AND rid=%d AND tagtype>0",
                TAG_CLOSED, vid)
      /* ... and the new check-in has no --branch option or the --branch
      ** option does not actually change the branch */
   && (sCiInfo.zBranch==0
       || db_exists("SELECT 1 FROM tagxref"
                    " WHERE tagid=%d AND rid=%d AND tagtype>0"
                    "   AND value=%Q", TAG_BRANCH, vid, sCiInfo.zBranch))
  ){
    fossil_fatal("cannot commit against a closed leaf");
  }
       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);
    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( dryRunFlag ){
    blob_zero(&comment);
  }else if( !noPrompt ){
    char *zInit = db_text(0, "SELECT value FROM vvar WHERE name='ci-comment'");
    prepare_commit_comment(&comment, zInit, &sCiInfo, vid);
    if( zInit && zInit[0] && fossil_strcmp(zInit, blob_str(&comment))==0 ){
      prompt_user("unchanged check-in comment.  continue (y/N)? ", &ans);
      cReply = blob_str(&ans)[0];
      blob_reset(&ans);
      if( cReply!='y' && cReply!='Y' ){
        fossil_exit(1);
      }
    }
    free(zInit);
  }
    }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{
        fossil_print("Abandoning commit due to empty check-in comment\n");
        cReply = 'N';
      }
      if( cReply!='y' && cReply!='Y' ){
        fossil_exit(1);
        fossil_fatal("Abandoning commit due to empty check-in comment\n");
      }
    }
  }
  }else{
    db_multi_exec("REPLACE INTO vvar VALUES('ci-comment',%B)", &comment);
    db_end_transaction(0);

  if( !noVerify && hook_exists("before-commit") ){
    /* Run before-commit hooks */
    char *zAuxFile;
    zAuxFile = prepare_commit_description_file(
    db_begin_transaction();
                     &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==1 AND NOT deleted AND is_selected(id)",
    "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;
    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, noPrompt,
                                    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 WHERE id=%d", nrid,nrid,id);
    db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid);
      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; "
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482

2483
2484
2485
2486
2487
2488
2489
2758
2759
2760
2761
2762
2763
2764

2765
2766
2767

2768
2769
2770
2771
2772
2773
2774
2775







-



-
+







  }
  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{
      fossil_print("Abandoning commit due to manifest signing failure\n");
      cReply = 'N';
    }
    if( cReply!='y' && cReply!='Y' ){
      fossil_exit(1);
      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 ){
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
2783
2784
2785
2786
2787
2788
2789

2790
2791
2792
2793
2794
2795
2796
2797
2798


2799
2800
2801
2802
2803
2804
2805
2806







-
+








-
-
+







    free(zManifestFile);
  }

  nvid = content_put(&manifest);
  if( nvid==0 ){
    fossil_fatal("trouble committing manifest: %s", g.zErrMsg);
  }
  db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nvid);
  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 uuid,merge FROM vmerge JOIN blob ON merge=rid"
                 " WHERE id=-4");
  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);
    }
2533
2534
2535
2536
2537
2538
2539
2540

2541
2542
2543
2544

2545
2546
2547
2548
2549
2550
2551
2818
2819
2820
2821
2822
2823
2824

2825
2826
2827
2828

2829
2830
2831
2832
2833
2834
2835
2836







-
+



-
+







  }

  /* 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, chnged=0, deleted=0, origname=NULL"
    "UPDATE vfile SET rid=mrid, mhash=NULL, chnged=0, deleted=0, origname=NULL"
    " WHERE is_selected(id);"
    , vid, nvid
  );
  db_lset_int("checkout", 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);
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
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







-
+












-
+






-
+












-
+














+
-
+



+
+


    /* Verify that the repository checksum matches the expected checksum
    ** calculated before the check-in started (and stored as the R record
    ** of the manifest file).
    */
    vfile_aggregate_checksum_repository(nvid, &cksum2);
    if( blob_compare(&cksum1, &cksum2) ){
      vfile_compare_repository_to_disk(nvid);
      fossil_fatal("working checkout does not match what would have ended "
      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 checkout does not match manifest after commit: "
         "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 checkout before and after commit does not match");
      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);
    exit(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(SYNC_PUSH|SYNC_PULL, db_get_int("autosync-tries", 1), 0);
    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
26

27
28
29
30


31
32
33
34
35
36
37
38
39
40
41
42
43




44
45

46
47
















48




















49
50
51
52
53
54

55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75

76
77
78
79
80
81
82
19
20
21
22
23
24
25

26
27
28


29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50


51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66

67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91

92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112

113
114
115
116
117
118
119
120







-
+


-
-
+
+













+
+
+
+


+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





-
+




















-
+







** from the local repository.
*/
#include "config.h"
#include "checkout.h"
#include <assert.h>

/*
** Check to see if there is an existing checkout that has been
** Check to see if there is an existing check-out that has been
** modified.  Return values:
**
**     0:   There is an existing checkout but it is unmodified
**     1:   There is a modified checkout - there are unsaved changes
**     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 ){
    vfile_unlink(vid);
  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 UUID name of a version, load the content of that
** 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 checkout");
    fossil_fatal("missing content, unable to check out");
  };
  return vid;
}

/*
** Set or clear the vfile.isexe flag for a file.
*/
140
141
142
143
144
145
146
147

148
149
150
151
152
153
154
178
179
180
181
182
183
184

185
186
187
188
189
190
191
192







-
+







  int flg;

  flg = db_get_manifest_setting();

  if( flg & MFESTFLG_RAW ){
    blob_zero(&manifest);
    content_get(vid, &manifest);
    sterilize_manifest(&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);
217
218
219
220
221
222
223
224

225
226
227
228





229
230
231



232
233
234
235

236
237
238
239
240
241






242
243

244
245
246
247


248
249
250


251
252

253
254
255
256
257
258
259
260
261

262
263
264
265
266
267
268
269
270

271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288

289
290
291
292
293
294
295

296
297
298
299
300
301
302
303
304
305
306
307
308
309


310
311
312
313
314
315
316
317
318
319
320
321

322
323
324
325
326
327
328
255
256
257
258
259
260
261

262
263
264
265
266
267
268
269
270
271



272
273
274
275
276
277

278
279
280
281



282
283
284
285
286
287
288

289
290
291


292
293
294


295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317

318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357


358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379







-
+




+
+
+
+
+
-
-
-
+
+
+



-
+



-
-
-
+
+
+
+
+
+

-
+


-
-
+
+

-
-
+
+


+









+








-
+


















+







+












-
-
+
+












+







  db_reset(&stmt);
  db_finalize(&stmt);
}


/*
** COMMAND: checkout*
** COMMAND: co*
** 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.
**
** Check out a version specified on the command-line.  This command
** will abort if there are edited files in the current checkout unless
** the --force option appears on the command-line.  The --keep option
** 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 checkout the
** 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 checkout
**    --keep            Only update the manifest and manifest.uuid files
**    --force-missing   Force checkout even if content is missing
**    --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
** See also: [[update]]
*/
void checkout_cmd(void){
  int forceFlag;                 /* Force checkout even if edits exist */
  int forceMissingFlag;          /* Force checkout even if missing content */
  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;                /* Checkout the latest version */
  char *zVers;                   /* Version to checkout */
  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 checkout");
    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();
  db_lset_int("checkout", 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){
339
340
341
342
343
344
345
346

347
348
349
350
351

352
353

354
355
356
357
358
359
360
361
362
363

364
365
366
367
368
369

370
371
372

373
374
375
376
377
378
379
390
391
392
393
394
395
396

397
398
399
400
401

402
403

404
405
406
407
408
409
410
411
412
413

414
415
416
417
418
419

420
421
422

423


424
425
426
427
428







-
+




-
+

-
+









-
+





-
+


-
+
-
-





}

/*
** COMMAND: close*
**
** Usage: %fossil close ?OPTIONS?
**
** The opposite of "open".  Close the current database connection.
** 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:
**   --force|-f  necessary to close a check out with uncommitted changes
**   -f|--force  Necessary to close a check-out with uncommitted changes
**
** See also: open
** See also: [[open]]
*/
void close_cmd(void){
  int forceFlag = find_option("force","f",0)!=0;
  db_must_be_within_tree();

  /* We should be done with options.. */
  verify_all_options();

  if( !forceFlag && unsaved_changes(0) ){
    fossil_fatal("there are unsaved changes in the current checkout");
    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 checkout will delete your stash");
    fossil_fatal("closing the check-out will delete your stash");
  }
  if( db_is_writeable("repository") ){
    char *zUnset = mprintf("ckout:%q", g.zLocalRoot);
    db_unset_mprintf(1, "ckout:%q", g.zLocalRoot);
    db_unset(zUnset, 1);
    fossil_free(zUnset);
  }
  unlink_local_database(1);
  db_close(1);
  unlink_local_database(0);
}

Changes to src/clone.c.

78
79
80
81
82
83
84
85

86
87
88


89
90
91
92





93
94
95



96
97
98
99



100
101
102


103
104
105
106



107









108
109
110



111
112
113




114

115
116


117
118

119
120
121


122
123

124
125
126
127
128
129
130
131
132





133
134
135
136




137

138






139
140

141
142

143
144
145
146
147
148


149
150
















151
152


153




154


155
156
157
158

159
160
161



162
163
164
165
166
167
168
169
170
171

172
173
174
175


176
177
178

179
180
181
182
183
184
185
186
187
188
189
190
191

192

193
194

195
196
197
198
199

200
201
202
203
204

205
206
207
208
209
210
211
212
213














214
215




216
217

218


219
220
221








222
223
224
225
226
227
228

229

230
231
232
233

















234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255

256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279

280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301

302

303
304
305
306
307
308
309
310
311
312
313

314
315
316
317
318
319

320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338

339
340
341
342
343
344
345
346
347
348
349
350

351
78
79
80
81
82
83
84

85
86
87

88
89
90



91
92
93
94
95
96


97
98
99

100


101
102
103
104


105
106
107



108
109
110
111
112
113
114
115
116
117
118
119
120



121
122
123
124
125

126
127
128
129
130
131
132
133
134
135
136

137

138
139
140
141
142

143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167

168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183


184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203


204
205
206
207
208
209
210

211
212
213
214
215

216
217


218
219
220
221
222
223
224
225
226
227
228
229

230
231
232


233
234
235
236

237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266

267
268
269
270
271
272




273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293

294
295
296
297
298


299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350

351
352
353
354
355
356
357

358
359
360
361
362
363
364

365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380

381


382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421

422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440

441
442
443
444
445
446
447
448
449
450
451
452

453
454







-
+


-
+
+

-
-
-
+
+
+
+
+

-
-
+
+
+
-

-
-
+
+
+

-
-
+
+

-
-
-
+
+
+

+
+
+
+
+
+
+
+
+
-
-
-
+
+
+


-
+
+
+
+

+


+
+

-
+
-


+
+

-
+









+
+
+
+
+




+
+
+
+

+
-
+
+
+
+
+
+


+


+




-
-
+
+


+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+

+
+
+
+
-
+
+



-
+

-
-
+
+
+









-
+


-
-
+
+


-
+













+

+


+





+




-
+





-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+


+
+
+
+

-
+

+
+

-
-
+
+
+
+
+
+
+
+







+

+




+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+













-







-
+






-
















-
+
-
-




















+

+











+





-
+


















-
+











-
+

  );
}


/*
** COMMAND: clone
**
** Usage: %fossil clone ?OPTIONS? URI FILENAME
** Usage: %fossil clone ?OPTIONS? URI ?FILENAME?
**
** Make a clone of a repository specified by URI in the local
** file named FILENAME.
** 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 form: ([...] mean optional)
**   HTTP/HTTPS protocol:
**     http[s]://[userid[:password]@]host[:port][/path]
** 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\\
**  * SSH protocol:
**
**      ssh://[userid@]host[:port]/path/to/repo.fossil[?fossil=path/fossil.exe]
**     [?fossil=path/to/fossil.exe]
**
**   Filesystem:
**     [file://]path/to/repo.fossil
**  * Filesystem:
**
**      [file://]path/to/repo.fossil
**
** Note 1: For ssh and filesystem, path must have an extra leading
**         '/' to use an absolute path.
** For ssh and filesystem, path must have an extra leading
** '/' to use an absolute path.
**
** Note 2: 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 ":".
** 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, your current login name is used to create the default
** admin user. This can be overridden using the -A|--admin-user
** parameter.
** 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:
**    --admin-user|-A USERNAME   Make USERNAME the administrator
**    -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
**    --ssh-command|-c SSH       Use SSH as the "ssh" command
**    --transport-command CMD    Use CMD to move messages to the server and back
**    --httpauth|-B USER:PASS    Add HTTP Basic Authorization to requests
**    -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
** 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( 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 < 4 ){
    usage("?OPTIONS? FILE-OR-URL NEW-REPOSITORY");
  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(g.argv[3], ExtFILE) ){
    fossil_fatal("file already exists: %s", g.argv[3]);
  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, g.argv[3]);
    file_copy(g.url.name, zRepo);
    db_close(1);
    db_open_repository(g.argv[3]);
    db_record_repository_filename(g.argv[3]);
    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", g.argv[3]);
    fossil_print("Repository cloned into %s\n", zRepo);
  }else{
    db_close_config();
    db_create_repository(g.argv[3]);
    db_open_repository(g.argv[3]);
    db_create_repository(zRepo);
    db_open_repository(zRepo);
    db_open_config(0,0);
    db_begin_transaction();
    db_record_repository_filename(g.argv[3]);
    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);
    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(g.argv[3]);
      fossil_fatal("server returned an error - clone aborted");
    }
    db_open_repository(g.argv[3]);
      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(0, 1, 0);
  rebuild_db(1, 0);
  if( !noCompress ){
    int nDelta = 0;
    i64 nByte;
    fossil_print("Extra delta compression... "); fflush(stdout);
    extra_deltification();
    fossil_print("\n");
    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 */
){
  char *zKey = mprintf("http-auth:%s", g.url.canonical);
  if( zHttpAuth && zHttpAuth[0] ){
    g.zHttpAuth = mprintf("%s", zHttpAuth);
  }
  if( fRemember ){
    if( g.zHttpAuth && g.zHttpAuth[0] ){
      set_httpauth(g.zHttpAuth);
    }else if( zUrl && zUrl[0] ){
      db_unset(zKey, 0);
      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();
  }
  free(zKey);
}

/*
** Get the HTTP Authorization preference from db.
*/
char *get_httpauth(void){
  char *zKey = mprintf("http-auth:%s", g.url.canonical);
  char * rc = unobscure(db_get(zKey, 0));
  free(zKey);
  return rc;
}

/*
** Set the HTTP Authorization preference in db.
*/
void set_httpauth(const char *zHttpAuth){
  char *zKey = mprintf("http-auth:%s", g.url.canonical);
  db_set_mprintf(obscure(zHttpAuth), 0, "http-auth:%s", g.url.canonical);
  db_set(zKey, obscure(zHttpAuth), 0);
  free(zKey);
}

/*
** Look for SSH clone command line options and setup in globals.
*/
void clone_ssh_find_options(void){
  const char *zSshCmd;        /* SSH command string */

  zSshCmd = find_option("ssh-command","c",1);
  if( zSshCmd && zSshCmd[0] ){
    g.zSshCmd = mprintf("%s", zSshCmd);
  }
}

/*
** Set SSH options discovered in global variables (set from command line
** options).
*/
void clone_ssh_db_set_options(void){
  if( g.zSshCmd && g.zSshCmd[0] ){
    db_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
      @ <a href="../login">logged in</a>.
      @ %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
      @ <a href="../login">logged in</a>.
      @ %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_footer();
  style_finish_page();
}

Deleted src/codecheck1.c.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
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


































































































































































































































































































































































































































































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
/*
** Copyright (c) 2014 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the Simplified BSD License (also
** known as the "2-Clause License" or "FreeBSD License".)
**
** This program is distributed in the hope that it will be useful,
** but without any warranty; without even the implied warranty of
** merchantability or fitness for a particular purpose.
**
** Author contact information:
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*******************************************************************************
**
** This program reads Fossil source code files and tries to verify that
** printf-style format strings are correct.
**
** This program implements a compile-time validation step on the Fossil
** source code.  Running this program is entirely optional.  Its role is
** similar to the -Wall compiler switch on gcc, or the scan-build utility
** of clang, or other static analyzers.  The purpose is to try to identify
** problems in the source code at compile-time.  The difference is that this
** static checker is specifically designed for the particular printf formatter
** implementation used by Fossil.
**
** Checks include:
**
**    *  Verify that vararg formatting routines like blob_printf() or
**       db_multi_exec() have the correct number of arguments for their
**       format string.
**
**    *  For routines designed to generate SQL, warn about the use of %s
**       which might allow SQL injection.
*/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <assert.h>

/*
** 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     /* Generates SQL text */
#define FMT_HTML  0x00002     /* Generates HTML text */
#define FMT_URL   0x00004     /* Generates URLs */
#define FMT_SAFE  0x00008     /* Always safe for %s */

/*
** A list of internal Fossil interfaces that take a printf-style format
** string.
*/
struct {
  const char *zFName;    /* Name of the function */
  int iFmtArg;           /* Index of format argument.  Leftmost is 1. */
  unsigned fmtFlags;     /* Processing flags */
} aFmtFunc[] = {
  { "admin_log",               1, 0 },
  { "blob_append_sql",         2, FMT_SQL },
  { "blob_appendf",            2, 0 },
  { "cgi_debug",               1, FMT_SAFE },
  { "cgi_panic",               1, FMT_SAFE },
  { "cgi_printf",              1, FMT_HTML },
  { "cgi_redirectf",           1, FMT_URL },
  { "chref",                   2, FMT_URL },
  { "db_blob",                 2, FMT_SQL },
  { "db_debug",                1, FMT_SQL },
  { "db_double",               2, FMT_SQL },
  { "db_err",                  1, 0 },
  { "db_exists",               1, FMT_SQL },
  { "db_get_mprintf",          2, 0 },
  { "db_int",                  2, FMT_SQL },
  { "db_int64",                2, FMT_SQL },
  { "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_mprintf",          3, 0 },
  { "db_static_prepare",       2, FMT_SQL },
  { "db_text",                 2, FMT_SQL },
  { "db_unset_mprintf",        2, 0 },
  { "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 },
  { "href",                    1, FMT_URL },
  { "json_new_string_f",       1, 0 },
  { "json_set_err",            2, 0 },
  { "json_warn",               2, 0 },
  { "mprintf",                 1, 0 },
  { "socket_set_errmsg",       1, 0 },
  { "ssl_set_errmsg",          1, 0 },
  { "style_header",            1, FMT_HTML },
  { "style_js_onload",         1, FMT_HTML },
  { "style_set_current_page",  1, FMT_URL },
  { "style_submenu_element",   2, FMT_URL },
  { "style_submenu_sql",       3, FMT_SQL },
  { "webpage_error",           1, FMT_SAFE },
  { "xhref",                   2, FMT_URL },
};

/*
** Determine if the indentifier zIdent of length nIndent is a Fossil
** internal interface that uses a printf-style argument.  Return zero if not.
** Return the index of the format string if true with the left-most
** argument having an index of 1.
*/
static int isFormatFunc(const char *zIdent, int nIdent, unsigned *pFlags){
  int upr, lwr;
  lwr = 0;
  upr = sizeof(aFmtFunc)/sizeof(aFmtFunc[0]) - 1;
  while( lwr<=upr ){
    unsigned x = (lwr + upr)/2;
    int c = strncmp(zIdent, aFmtFunc[x].zFName, nIdent);
    if( c==0 ){
      if( aFmtFunc[x].zFName[nIdent]==0 ){
        *pFlags = aFmtFunc[x].fmtFlags;
        return aFmtFunc[x].iFmtArg;
      }
      c = -1;
    }
    if( c<0 ){
      upr = x - 1;
    }else{
      lwr = x + 1;
    }
  }
  *pFlags = 0;
  return 0;
}

/*
** Return the expected number of arguments for the format string.
** Return -1 if the value cannot be computed.
**
** For each argument less than nType, store the conversion character
** for that argument in cType[i].
*/
static int formatArgCount(const char *z, int nType, char *cType){
  int nArg = 0;
  int i, k;
  int len;
  int eType;
  int ln = 0;
  while( z[0] ){
    len = token_length(z, &eType, &ln);
    if( eType==TK_STR ){
      for(i=1; i<len-1; i++){
        if( z[i]!='%' ) continue;
        if( z[i+1]=='%' ){ i++; continue; }
        for(k=i+1; k<len && !isalpha(z[k]); k++){
          if( z[k]=='*' || z[k]=='#' ){
            if( nArg<nType ) cType[nArg] = z[k];
            nArg++;
          }
        }
        if( z[k]!='R' ){
          if( nArg<nType ) cType[nArg] = z[k];
          nArg++;
        }
      }
    }
    z += len;
  }
  return nArg;
}

/*
** The function call that begins at zFCall[0] (which is on line lnFCall of the
** original file) is a function that uses a printf-style format string
** on argument number fmtArg.  It has processings flags fmtFlags.  Do
** compile-time checking on this function, output any errors, and return
** the number of errors.
*/
static int checkFormatFunc(
  const char *zFilename, /* Name of the file being processed */
  const char *zFCall,    /* Pointer to start of function call */
  int lnFCall,           /* Line number that holds z[0] */
  int fmtArg,            /* Format string should be this argument */
  int fmtFlags           /* Extra processing flags */
){
  int szFName;
  int eToken;
  int ln = lnFCall;
  int len;
  const char *zStart;
  char *z;
  char *zCopy;
  int nArg = 0;
  const char **azArg = 0;
  int i, k;
  int nErr = 0;
  char *acType;

  szFName = token_length(zFCall, &eToken, &ln);
  zStart = next_non_whitespace(zFCall+szFName, &len, &eToken);
  assert( zStart[0]=='(' && len==1 );
  len = distance_to(zStart+1, ')');
  zCopy = safe_malloc( len + 1 );
  memcpy(zCopy, zStart+1, len);
  zCopy[len] = 0;
  azArg = 0;
  nArg = 0;
  z = zCopy;
  while( z[0] ){
    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( !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))>=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_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;
  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;
}

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
7

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

33
34
35
36
37
38

39
40
41
42
43
44
45
1
2
3
4
5
6

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23






24
25

26
27
28
29
30
31
32
33
34
35
36
37
38
39
40






-
+
















-
-
-
-
-
-


-
+






+







/*
** 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>
#ifdef _WIN32
# include <windows.h>
#else
# include <termios.h>
# include <sys/ioctl.h>
#endif

#if INTERFACE
#define COMMENT_PRINT_NONE       ((u32)0x00000000) /* No flags. */
#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.
*/
64
65
66
67
68
69
70
71

72
73

74
75
76

77
78

79
80
81
82
83


84
85
86
87
88
89
90
91
92
93
94








95
96
97



98
99
100
101


102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126


127
128

129
130

131







132
133
134
135
136
137
138
139
































140
141


142
143

144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
59
60
61
62
63
64
65

66


67



68
69

70





71
72
73










74
75
76
77
78
79
80
81



82
83
84
85
86


87
88
89
90
91
92
93
94
95
96

97
98
99
100
101
102
103
104
105
106
107
108
109
110
111

112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167


168
169
170

171
172
173
174
175
176
177



178
179
180
181
182
183
184







-
+
-
-
+
-
-
-
+

-
+
-
-
-
-
-
+
+

-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
+
+
+


-
-
+
+








-















-
+
+


+


+

+
+
+
+
+
+
+








+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+

-
+






-
-
-







** returned to indicate the terminal line width is using the hard-coded
** legacy default value.
*/
static int comment_set_maxchars(
  int indent,
  int *pMaxChars
){
#if defined(_WIN32)
  struct TerminalSize ts;
  CONSOLE_SCREEN_BUFFER_INFO csbi;
  memset(&csbi, 0, sizeof(CONSOLE_SCREEN_BUFFER_INFO));
  if ( !terminal_get_size(&ts) ){
  if( GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi) ){
    *pMaxChars = csbi.srWindow.Right - csbi.srWindow.Left - indent;
    return 1;
    return 0;
  }
  return 0;

#elif defined(TIOCGWINSZ)
  struct winsize w;
  memset(&w, 0, sizeof(struct winsize));
  if( ioctl(0, TIOCGWINSZ, &w)!=-1 ){
    *pMaxChars = w.ws_col - indent;
  if( ts.nColumns ){
    *pMaxChars = ts.nColumns - indent;
    return 1;
  }
  return 0;
#else
  /*
  ** Fallback to using more-or-less the "legacy semantics" of hard-coding
  ** the maximum line length to a value reasonable for the vast majority
  ** of supported systems.
  */
  *pMaxChars = COMMENT_LEGACY_LINE_LENGTH - indent;
  return -1;
  }else{
    /*
    ** Fallback to using more-or-less the "legacy semantics" of hard-coding
    ** the maximum line length to a value reasonable for the vast majority
    ** of supported systems.
    */
    *pMaxChars = COMMENT_LEGACY_LINE_LENGTH - indent;
    return -1;
#endif
}

  }
}

/*
** This function checks the current line being printed against the original
** comment text.  Upon matching, it emits a new line and updates the provided
** character and line counts, if applicable.
** 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 ){
    fossil_print("\n");
    if( pCharCnt ) *pCharCnt = 0;
    if( pLineCnt ) (*pLineCnt)++;
    return 1;
  }
  return 0;
}

/*
** This function scans the specified comment line starting just after the
** initial index and returns the index of the next spacing character -OR-
** zero if such a character cannot be found.  For the purposes of this
** algorithm, the NUL character is treated the same as a spacing character.
*/
static int comment_next_space(
  const char *zLine, /* [in] The comment line being printed. */
  int index          /* [in] The current character index being handled. */
  int 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 perform
** the necessary indenting.
** 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_print_indent(
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( indent>0 ){
    fossil_print("%*s", indent, "");
  }
  if( zLine && piIndex ){
    int index = *piIndex;
    if( trimCrLf ){
      while( zLine[index]=='\r' || zLine[index]=='\n' ){ index++; }
    }
    if( trimSpace ){
      while( fossil_isspace(zLine[index]) ){ index++; }
177
178
179
180
181
182
183
184



185
186








187










188
189
190
191








192
193
194
195
196
197

198
199




200
201
202
203
204
205
206
207
208
209

210
211


212
213
214
215
216
217

218
219
220

221
222


223
224
225
226
227
228
229
230
231













232
233
234
235
236

237






238
239
240
241
242
243
244
202
203
204
205
206
207
208

209
210
211
212
213
214
215
216
217
218
219
220
221

222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250


251
252
253
254
255
256
257
258
259
260
261
262
263
264
265


266
267
268
269
270
271
272

273
274
275
276
277


278
279
280
281
282
283
284
285
286


287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303

304
305
306
307
308
309
310
311
312
313
314
315
316
317
318







-
+
+
+


+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+




+
+
+
+
+
+
+
+






+
-
-
+
+
+
+










+
-
-
+
+





-
+



+
-
-
+
+







-
-
+
+
+
+
+
+
+
+
+
+
+
+
+




-
+

+
+
+
+
+
+







  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;
  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_print_indent(zLine, indent, trimCrLf, trimSpace, &index);
  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_print_indent(zCurrent, origIndent, trimCrLf, trimSpace,
                               &index);
          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);
      if( nextIndex<=0 || (nextIndex-index)>maxChars ){
      int nextIndex = comment_next_space(zLine, index, &distUTF8);
      if( nextIndex<=0 || distUTF8>maxChars ){
        break;
      }
      charCnt++;
      useChars = COMMENT_TAB_WIDTH;
      if( maxChars<useChars ){
        fossil_print(" ");
        zBuf[iBuf++] = ' ';
        break;
      }
    }else if( wordBreak && fossil_isspace(c) ){
      int distUTF8;
      int nextIndex = comment_next_space(zLine, index);
      if( nextIndex<=0 || (nextIndex-index)>maxChars ){
      int nextIndex = comment_next_space(zLine, index, &distUTF8);
      if( nextIndex<=0 || distUTF8>=maxChars ){
        break;
      }
      charCnt++;
    }else{
      charCnt++;
    }
    assert( c!='\n' || charCnt==0 );
    fossil_print("%c", c);
    if( (c&0x80)==0 || (zLine[index+1]&0xc0)!=0xc0 ) maxChars -= useChars;
    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 ){
    fossil_print("\n");
    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;
  }
257
258
259
260
261
262
263
264

265
266
267
268

269
270
271
272
273
274
275
276

277
278


279
280
281
282
283
284
285
286
287
288
289
290
291
292

293















294

295
296
297
298
299
300
301
331
332
333
334
335
336
337

338
339
340
341
342
343
344
345
346
347
348
349
350
351
352


353
354
355
356
357
358
359
360
361
362
363
364
365
366
367

368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384

385
386
387
388
389
390
391
392







-
+




+








+
-
-
+
+













-
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+







*/
static int comment_print_legacy(
  const char *zText, /* The comment text to be printed. */
  int indent,        /* Number of spaces to indent each non-initial line. */
  int width          /* Maximum number of characters per line. */
){
  int maxChars = width - indent;
  int si, sk, i, k;
  int 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 >= (sizeof(zBuffer)) ){
    zBuf = fossil_malloc(maxChars+1);
  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=0; zText[i] && k<maxChars; i++){
    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];
        }
      }
      if( fossil_isspace(c) ){
      else if( fossil_isspace(c) ){
        si = i;
        sk = k;
        if( k==0 || zBuf[k-1]!=' ' ){
          zBuf[k++] = ' ';
        }
      }else{
        zBuf[k] = c;
408
409
410
411
412
413
414


































415
416
417
418
419
420
421
422
423
424
425
426

427
428
429
430
431
432




433
434

435
436
437

438
439
440
441
442
443
444
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+











-
+


-
-
-
-
+
+
+
+

-
+


-
+







    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.
**                    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.
**   --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.
**                    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).
**   -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;

Changes to src/config.h.

64
65
66
67
68
69
70






71
72
73
74
75
76
77
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83







+
+
+
+
+
+







#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

/*
175
176
177
178
179
180
181


182
183
184
185
186
187
188
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196







+
+








#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
241
242
243
244
245
246
247

248
249

250
251

252
253
254
255
256
257


258
259
260
261
262
263
264
265
266
267
268
269
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267

268
269
270
271
272
273
274
275
276
277
278
279
280
281







+


+


+





-
+
+












#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) (sizeof(X)/sizeof(X[0]))
#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.

35
36
37
38
39
40
41

42

43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64


65
66
67
68
69
70
71
72
73
74










75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91

92
93


94
95
96
97


98




99
100



101
102
103
104
105
106



107
108
109
110
111
112
113
114
35
36
37
38
39
40
41
42

43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63


64
65
66









67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117



118
119
120

121
122
123
124
125
126
127







+
-
+




















-
-
+
+

-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+

















+


+
+




+
+

+
+
+
+


+
+
+



-
-
-
+
+
+
-







#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       0x0003ff     /* Everything */
#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"         },
  { "/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"  },
  { "/all",         CONFIGSET_ALL,   "All of the above"                     },
                                       "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 },
  { "sitemap-docidx",         CONFIGSET_SKIN },
  { "sitemap-download",       CONFIGSET_SKIN },
  { "sitemap-license",        CONFIGSET_SKIN },
  { "default-csp",            CONFIGSET_SKIN },
  { "sitemap-extra",          CONFIGSET_SKIN },
  { "safe-html",              CONFIGSET_SKIN },
  { "sitemap-contact",        CONFIGSET_SKIN },

#ifdef FOSSIL_ENABLE_TH1_DOCS
  { "th1-docs",               CONFIGSET_TH1 },
#endif
#ifdef FOSSIL_ENABLE_TH1_HOOKS
  { "th1-hooks",              CONFIGSET_TH1 },
#endif
129
130
131
132
133
134
135
136
137
138
139
140
141
142



143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159

160
161
162
163
164
165
166
167


168
169
170
171
172
173
174
142
143
144
145
146
147
148

149
150
151
152


153
154
155
156


157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188







-




-
-
+
+
+

-
-














+








+
+







  { "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 },
  { "allow-symlinks",         CONFIGSET_PROJ },
  { "dotfiles",               CONFIGSET_PROJ },
  { "parent-project-code",    CONFIGSET_PROJ },
  { "parent-project-name",    CONFIGSET_PROJ },
  { "hash-policy",            CONFIGSET_PROJ },

#ifdef FOSSIL_ENABLE_LEGACY_MV_RM
  { "comment-format",         CONFIGSET_PROJ },
  { "mimetypes",              CONFIGSET_PROJ },
  { "forbid-delta-manifests", CONFIGSET_PROJ },
  { "mv-rm-files",            CONFIGSET_PROJ },
#endif

  { "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 },

};
244
245
246
247
248
249
250



251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274







+
+
+







        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.
*/
339
340
341
342
343
344
345
346

347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371





372
373

374
375
376
377
378
379
380
356
357
358
359
360
361
362

363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383





384
385
386
387
388
389

390
391
392
393
394
395
396
397







-
+




















-
-
-
-
-
+
+
+
+
+

-
+







** 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
**    /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", 4, { "pw","cap","info","photo",0,0} },
      { "@shun",      "uuid",  1, { "scom", 0,0,0,0,0}             },
      { "@reportfmt", "title", 3, { "owner","cols","sqlcode",0,0,0}},
      { "@concealed", "hash",  1, { "content", 0,0,0,0,0 }         },
      { "/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"}         },
          { "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;
417
418
419
420
421
422
423
424

425
426
427
428
429
430
431
432









433

434
435
436
437
438
439
440
441
442
443
444
445
446

447
448
449
450
451
452
453
434
435
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







-
+








+
+
+
+
+
+
+
+
+

+













+







      }
      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") ) alert_schema(0);
    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.
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
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







+
-
-
-
+
+
+
+
+
+
+
+

-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
+








+
-
-
-
+
+
+
+
+
+
+
+

-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
+







                   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) FROM user"
                   " WHERE mtime>=%lld", iStart);
      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 ){
      blob_appendf(&rec,"%s %s pw %s cap %s info %s photo %s",
        db_column_text(&q, 0),
        db_column_text(&q, 1),
        db_column_text(&q, 2),
        db_column_text(&q, 3),
        db_column_text(&q, 4),
        db_column_text(&q, 5)
      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) FROM reportfmt"
                   " WHERE mtime>=%lld", iStart);
      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 ){
      blob_appendf(&rec,"%s %s owner %s cols %s sqlcode %s",
        db_column_text(&q, 0),
        db_column_text(&q, 1),
        db_column_text(&q, 2),
        db_column_text(&q, 3),
        db_column_text(&q, 4)
      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);
  }
574
575
576
577
578
579
580
















581
582
583
584
585
586
587
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







    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);
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
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







-
+
-

-
+




+
-
+

-
+




-
+





-
+






-
+






-
+



-
+





-
+

-
+









/*
** COMMAND: configuration*
**
** Usage: %fossil configuration METHOD ... ?OPTIONS?
**
** Where METHOD is one of: export import merge pull push reset.  All methods
** Where METHOD is one of: export import merge pull push reset.
** accept the -R or --repository option to specify a repository.
**
**    %fossil configuration export AREA FILENAME
** >  fossil configuration export AREA FILENAME
**
**         Write to FILENAME exported configuration information for AREA.
**         AREA can be one of:
**
**             all email interwiki project shun skin
**             all email project shun skin ticket user alias subscriber
**             ticket user alias subscriber
**
**    %fossil configuration import FILENAME
** >  fossil configuration import FILENAME
**
**         Read a configuration from FILENAME, overwriting the current
**         configuration.
**
**    %fossil configuration merge FILENAME
** >  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?
** >  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?
** >  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
** >  fossil configuration reset AREA
**
**         Restore the configuration to the default.  AREA as above.
**
**    %fossil configuration sync AREA ?URL?
** >  fossil configuration sync AREA ?URL?
**
**         Synchronize configuration changes in the local repository with
**         the remote repository at URL.
**
** Options:
**    -R|--repository FILE       Extract info from repository FILE
**    -R|--repository REPO       Affect repository REPO with changes
**
** See also: settings, unset
** 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 ){
776
777
778
779
780
781
782

783

784
785
786
787
788
789
790
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855







+

+







    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;
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
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







-
+





-
+

+
-
+
+

-
+















+

+

+

+







    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_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);
      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);
      client_sync(0,(unsigned)mask,0,0,0);
      if( overwriteFlag ) db_protect_pop();
    }else{
      client_sync(0,(unsigned)mask,(unsigned)mask);
      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") ){
1038
1039
1040
1041
1042
1043
1044

1045
1046
1047
1048
1049
1050
1051
1052
1053
1054

1055
1056
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129







+










+


    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.

97
98
99
100
101
102
103
104



105
106

107
108
109
110
111
112
113
114
115





116
117
118
119
120
121
122
97
98
99
100
101
102
103

104
105
106
107

108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129







-
+
+
+

-
+









+
+
+
+
+







  contentCache.szTotal += blob_size(pBlob);
  p->content = *pBlob;
  blob_zero(pBlob);
  bag_insert(&contentCache.inCache, rid);
}

/*
** Clear the content cache.
** 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(void){
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){
311
312
313
314
315
316
317
318

319
320
321
322

323
324

325
326
327
328
329
330
331
318
319
320
321
322
323
324

325
326
327
328

329
330

331
332
333
334
335
336
337
338







-
+



-
+

-
+








/*
** 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 4th argument is given, in
** standard output, or if the optional second argument is given, in
** the named output file.
**
** Options:
**    -R|--repository FILE       Extract artifacts from repository FILE
**    -R|--repository REPO       Extract artifacts from repository REPO
**
** See also: finfo
** 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?");
546
547
548
549
550
551
552
553
554


555
556

557
558
559
560
561
562

563
564
565
566
567
568
569
553
554
555
556
557
558
559


560
561


562
563
564
565
566
567

568
569
570
571
572
573
574
575







-
-
+
+
-
-
+





-
+








  /* 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 || pBlob==0 ){
      /* Either the entry is not a phantom or it is a phantom but we
    if( db_column_int(&s1, 1)>=0 ){
      /* The entry is not a phantom. There is nothing for us to do
      ** have no data with which to dephantomize it.  In either case,
      ** there is nothing for us to do other than return the RID. */
      ** other than return the RID. */
      db_finalize(&s1);
      db_end_transaction(0);
      return rid;
    }
  }else{
    rid = 0;  /* No entry with the same UUID currently exists */
    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);

596
597
598
599
600
601
602
603

604
605
606
607
608
609
610
602
603
604
605
606
607
608

609
610
611
612
613
614
615
616







-
+







    db_exec(&s1);
    rid = db_last_insert_rowid();
    if( !pBlob ){
      db_multi_exec("INSERT OR IGNORE INTO phantom VALUES(%d)", rid);
    }
  }
  if( g.markPrivate || isPrivate ){
    db_multi_exec("INSERT INTO private VALUES(%d)", rid);
    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.
  */
650
651
652
653
654
655
656
657

658
659
660
661
662
663
664
656
657
658
659
660
661
662

663
664
665
666
667
668
669
670







-
+







*/
int content_put(Blob *pBlob){
  return content_put_ex(pBlob, 0, 0, 0, 0);
}


/*
** Create a new phantom with the given UUID and return its artifact ID.
** 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();
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
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







+
+
+
+
+
+
+
+
+
+
+
+









+
+







-
+







-
+
+











+
+
+
+
+
+
+
+
+




+
+
+







  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 dleta that depends on rid, then it is
** 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 1 if a delta is made and 0 if no delta occurs.
** 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 ){
869
870
871
872
873
874
875

876
877
878
879
880
881
882

883
884
885
886
887
888
889
902
903
904
905
906
907
908
909
910
911
912
913
914
915

916
917
918
919
920
921
922
923







+






-
+







  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 = 1;
    rc -= blob_size(&bestDelta);
  }
  blob_reset(&data);
  blob_reset(&bestDelta);
  return rc;
}

/*
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
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







-
+
+


+
+











+
+

+
+
+
+
+
+
+
+
+
+
+
+







** 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)"
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
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







-
+


















-
+







    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( blob_size(&content)!=size ){
    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<sizeof(zFirstLine)-1; i++){}
      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 ){
1069
1070
1071
1072
1073
1074
1075
1076
1077



1078
1079
1080

1081
1082
1083
1084
1085
1086
1087
1120
1121
1122
1123
1124
1125
1126


1127
1128
1129
1130
1131

1132
1133
1134
1135
1136
1137
1138
1139







-
-
+
+
+


-
+







}

/* 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 artifact zUuid exists in the repository.  If it does,
** return 0.  If it does not, generate an error message and return 1.
** 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,     /* The artifact we are checking for */
  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;
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1194
1195
1196
1197
1198
1199
1200

1201
1202
1203
1204
1205
1206
1207







-







** 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;
1216
1217
1218
1219
1220
1221
1222
1223

1224
1225
1226
1227
1228
1229
1230
1267
1268
1269
1270
1271
1272
1273

1274
1275
1276
1277
1278
1279
1280
1281







-
+







** 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 hashs or labels.
** 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"

Changes to src/cookies.c.

50
51
52
53
54
55
56

57
58
59
60
61
62
63
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64







+







**    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"
124
125
126
127
128
129
130
131




132
133
134
135
136
137
138
125
126
127
128
129
130
131

132
133
134
135
136
137
138
139
140
141
142







-
+
+
+
+







  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( 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++;
167
168
169
170
171
172
173
174
175













176
177
178
179
180
181
182
171
172
173
174
175
176
177


178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197







-
-
+
+
+
+
+
+
+
+
+
+
+
+
+







  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
/* 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++){
196
197
198
199
200
201
202






203

204
205
206
207
208
209

210
211




212
213
214

215
216



217
218
219
220
221


















222
223
224
225
226
227


















228
211
212
213
214
215
216
217
218
219
220
221
222
223

224
225
226
227
228
229
230
231


232
233
234
235


236
237


238
239
240

241



242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259






260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278







+
+
+
+
+
+
-
+






+
-
-
+
+
+
+
-
-

+
-
-
+
+
+
-

-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

  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:  cookies
** WEBPAGE: fdscookie
**
** Show the current display settings contained in the
** "fossil_display_settings" cookie.
*/
void cookie_page(void){
  int i;
  int nCookie = 0;
  if( PB("clear") ){
    cgi_set_cookie(DISPLAY_SETTINGS_COOKIE, "", 0, 1);
  const char *zName = 0;
  const char *zValue = 0;
  int isQP = 0;
  int bFDSonly = strstr(g.zPath, "fdscookie")!=0;
    cgi_replace_parameter(DISPLAY_SETTINGS_COOKIE, "");
  }
  cookie_parse();
  if( bFDSonly ){
  style_header("User Preference Cookie Values");
  if( cookies.nParam ){
    style_header("Display Preferences Cookie");
  }else{
    style_header("All Cookies");
    style_submenu_element("Clear", "%R/cookies?clear");
  }
  @ <p>The following are user preference settings held in the
  @ "fossil_display_settings" cookie.
  @ <ul>
  @ <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>
  @ <li>Raw cookie value: "%h(PD("fossil_display_settings",""))"
  for(i=0; i<cookies.nParam; i++){
    @ <li>%h(cookies.aParam[i].zPName): "%h(cookies.aParam[i].zPValue)"
  }
  @ </ul>
  style_footer();
      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);
    }
  }
}

Deleted src/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>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 */

Deleted src/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 */

Changes to src/db.c.

16
17
18
19
20
21
22
23


24
25
26
27

28
29
30
31
32
33
34

35
36
37



38
39
40



41
42
43
44
45
46










47
48
49















50
51
52
53
54
55
56
16
17
18
19
20
21
22

23
24
25
26
27

28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89







-
+
+



-
+







+



+
+
+



+
+
+






+
+
+
+
+
+
+
+
+
+



+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







*******************************************************************************
**
** Code for interfacing to the various databases.
**
** There are three separate database files that fossil interacts
** with:
**
**    (1)  The "user" database in ~/.fossil
**    (1)  The "configdb" database in ~/.fossil or ~/.config/fossil.db
**         or in %LOCALAPPDATA%/_fossil
**
**    (2)  The "repository" database
**
**    (3)  A local checkout database named "_FOSSIL_" or ".fslckout"
**    (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() */
66
67
68
69
70
71
72

73
74
75
76
77
78
79
80
81







82
83
84
85
86
87
88
89
90
91













92
93
94
95
96
97
98

99
100
101

102
103
104
105
106
107
108

109
110
111
112
113
114






115



116
117
118
119
120
121

122
123
124
125
126
127
128
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114

115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130

131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161

162
163
164
165
166
167
168
169
170
171
172
173
174

175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191







+








-
+
+
+
+
+
+
+









-
+
+
+
+
+
+
+
+
+
+
+
+
+







+



+






-
+






+
+
+
+
+
+
-
+
+
+






+







*/
#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 ){
  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_panic("Database error: %s", z);
  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[5];
  } 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 = {0, 0, 0, 0, 0, 0, };
} 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.
*/
173
174
175
176
177
178
179

180
181
182
183
184
185
186
187



188
189
190
191
192
193
194









195
196
197
198
199
200
201
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254







255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270







+








+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+







  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;
  }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++;
}

213
214
215
216
217
218
219

220
221
222
223
224
225
226

227
228
229
230
231
232
233
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304







+







+







    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);
      }
291
292
293
294
295
296
297














































































































































































































































































298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315

316
317
318
319
320
321
322
323
324

325
326


327
328
329
330
331
332
333
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


















+








-
+


+
+







      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, 0);
  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;
359
360
361
362
363
364
365






366
367
368
369
370
371
372
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722







+
+
+
+
+
+







    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;
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
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







-
+

-
+














-
+


-
+







}

/*
** Reset or finalize a statement.
*/
int db_reset(Stmt *pStmt){
  int rc;
  db_stats(pStmt);
  if( g.fSqlStats ){ db_stats(pStmt); }
  rc = sqlite3_reset(pStmt->pStmt);
  db_check_result(rc);
  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;
  db_stats(pStmt);
  if( g.fSqlStats ){ db_stats(pStmt); }
  blob_reset(&pStmt->sql);
  rc = sqlite3_finalize(pStmt->pStmt);
  db_check_result(rc);
  db_check_result(rc, pStmt);
  pStmt->pStmt = 0;
  return rc;
}

/*
** Return the rowid of the most recent insert
*/
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
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







-
-
-
-
-
-
-
-
-
-







-
+


+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







** 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));
}

/*
** Check a result code.  If it is not SQLITE_OK, print the
** corresponding error message and exit.
*/
void db_check_result(int rc){
  if( rc!=SQLITE_OK ){
    db_err("SQL error: %s", sqlite3_errmsg(g.db));
  }
}

/*
** 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);
  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;
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
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







-
+
+

-
+
-

-
-

-
-
-
-
+
-













+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







    z = zEnd;
  }
  blob_reset(&sql);
  return rc;
}

/*
** Execute multiple SQL statements.
** Execute multiple SQL statements.  The input text is executed
** directly without any formatting.
*/
int db_multi_exec(const char *zSql, ...){
int db_exec_sql(const char *z){
  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);
  const char *zEnd;
  z = blob_str(&sql);
  while( rc==SQLITE_OK && z[0] ){
    pStmt = 0;
    rc = sqlite3_prepare_v2(g.db, z, -1, &pStmt, &zEnd);
    if( rc ){
      db_err("%s: {%s}", sqlite3_errmsg(g.db), z);
    }else if( pStmt ){
      db.nPrepare++;
      while( sqlite3_step(pStmt)==SQLITE_ROW ){}
      rc = sqlite3_finalize(pStmt);
      if( rc ) db_err("%s: {%.*s}", sqlite3_errmsg(g.db), (int)(zEnd-z), z);
    }
    z = zEnd;
  }
  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
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
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







+
+
+






-
+




-
-
-
+
+
+

-
+



-
+

-
+



-
-
+
+
+
+
+
+







  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 *db;
  sqlite3 *xdb;
  int rc;
  const char *zSql;
  va_list ap;

  db = db_open(zFileName);
  sqlite3_exec(db, "BEGIN EXCLUSIVE", 0, 0, 0);
  rc = sqlite3_exec(db, zSchema, 0, 0, 0);
  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(db));
    db_err("%s", sqlite3_errmsg(xdb));
  }
  va_start(ap, zSchema);
  while( (zSql = va_arg(ap, const char*))!=0 ){
    rc = sqlite3_exec(db, zSql, 0, 0, 0);
    rc = sqlite3_exec(xdb, zSql, 0, 0, 0);
    if( rc!=SQLITE_OK ){
      db_err("%s", sqlite3_errmsg(db));
      db_err("%s", sqlite3_errmsg(xdb));
    }
  }
  va_end(ap);
  sqlite3_exec(db, "COMMIT", 0, 0, 0);
  sqlite3_close(db);
  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(
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
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









+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+




+




















+
+
+
+
+
+
+
+
+
+
+
+
+
+











-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+













+







  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.
*/
size_t savedKeySize = 0;
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);
  }
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
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







-
+



















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
+
+






-










+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+







  savedKeySize = 0;
}

/*
** This function sets the saved database encryption key to the specified
** string value, allocating or freeing the underlying memory if needed.
*/
void db_set_saved_encryption_key(
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 sets the saved database encryption key to one that gets
** read from the specified Fossil parent process.  This is only necessary
** (or functional) on Windows.
** 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.
*/
void db_read_saved_encryption_key_from_process(
  DWORD processId, /* Identifier for Fossil parent process. */
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;
  HANDLE hProcess = NULL;

  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)
  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_panic("bad size read, %u out of %u bytes at %p from pid %lu",
                     nRead, nSize, pAddress, processId);
      }
    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);
      CloseHandle(hProcess);
      fossil_panic("failed read, %u bytes at %p from pid %lu: %lu", nSize,
                   pAddress, processId, GetLastError());
    }
  }else{
    fossil_panic("failed to open pid %lu: %lu", processId, GetLastError());
  }
}
  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");
    }
#endif /* defined(_WIN32) */
    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
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
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







+


+
+
+
+
+
-
+






+









+
+
+
+
+
+
-
+











-
+



-
+
+

















+





-
+





-
+







/*
** 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(zDbName, g.nameOfExe)==0 ){
  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, 5000);
  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_STMT, db_sql_trace, 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_exec(db, "PRAGMA foreign_keys=OFF;", 0, 0, 0);
  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_multi_exec(zCmd /*works-like:""*/);
    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_multi_exec(zCmd /*works-like:""*/);
    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
  }
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310






1311
1312
1313
1314
1315
1316
1317
2205
2206
2207
2208
2209
2210
2211





2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224







-
-
-
-
-
+
+
+
+
+
+







*/
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 ) return iSlot;
  while( db_step(&q)==SQLITE_ROW ){
    if( fossil_strcmp(db_column_text(&q,1),zLabel)==0 ){
      iSlot = db_column_int(&q, 0);
      break;
  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;
}

/*
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
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







-
+





-




-






+
-
+
+
+

+
+



-
-
+
+

-
+
-
-
-
-
-
-

-
+
-
-
-
-
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+

+





+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
+
+
+
+

-
+
+
+

-
+
-
-
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
-
+
+

-
+


+



-
+







    db_set_main_schemaname(g.db, zLabel);
  }else{
    db_attach(zDbName, zLabel);
  }
}

/*
** Close the per-user database file in ~/.fossil
** Close the per-user configuration database file
*/
void db_close_config(){
  int iSlot = db_database_slot("configdb");
  if( iSlot>0 ){
    db_detach("configdb");
    g.zConfigDbName = 0;
  }else if( g.dbConfig ){
    sqlite3_wal_checkpoint(g.dbConfig, 0);
    sqlite3_close(g.dbConfig);
    g.dbConfig = 0;
    g.zConfigDbName = 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.zConfigDbName = 0;
    g.localOpen = 0;
  }else{
    return;
  }
  fossil_free(g.zConfigDbName);
  g.zConfigDbName = 0;
}

/*
** Open the user database in "~/.fossil".  Create the database anew if
** it does not already exist.
** 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.
**
** If the useAttach flag is 0 (the usual case) then the user database is
** Space to hold the result comes from fossil_malloc().
** opened on a separate database connection g.dbConfig.  This prevents
** the ~/.fossil database from becoming locked on long check-in or sync
** operations which hold an exclusive transaction.  In a few cases, though,
** it is convenient for the ~/.fossil to be attached to the main database
** connection so that we can join between the various databases.  In that
** case, invoke this routine with useAttach as 1.
*/
int db_open_config(int useAttach, int isOptional){
static char *db_configdb_name(int isOptional){
  char *zDbName;
  char *zHome;
  if( g.zConfigDbName ){
    int alreadyAttached = db_database_slot("configdb")>0;
  char *zHome;        /* Home directory */
  char *zDbName;      /* Name of the database file */

    if( useAttach==alreadyAttached ) return 1; /* Already open. */
    db_close_config();
  }
  zHome = fossil_getenv("FOSSIL_HOME");

  /* 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);
      }
    }
  }
  if( zHome==0 ){
    if( isOptional ) return 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;
    fossil_panic("cannot locate home directory - please set the "
                 "FOSSIL_HOME, LOCALAPPDATA, APPDATA, or HOMEPATH "
                 "environment variables");
  }
#else
  if( zHome==0 ){
    zHome = fossil_getenv("HOME");
  }
  if( zHome==0 ){
    if( isOptional ) return 0;
    fossil_panic("cannot locate home directory - please set the "
                 "FOSSIL_HOME or HOME environment variables");

#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);
  }
#endif
  if( file_isdir(zHome, ExtFILE)!=1 ){

  /* The HOME variable is required in order to continue.
  */
  if( zHome==0 ){
    if( isOptional ) return 0;
    fossil_panic("invalid home directory: %s", zHome);
    fossil_fatal("cannot locate home directory - please set one of the "
                 "FOSSIL_HOME, XDG_CONFIG_HOME, or HOME environment "
                 "variables");
  }
#if defined(_WIN32) || defined(__CYGWIN__)

  /* . filenames give some window systems problems and many apps problems */
  zDbName = mprintf("%//_fossil", zHome);
  /* Step 4: If $HOME/.config is a directory -> $HOME/.config/fossil.db
#else
  zDbName = mprintf("%s/.fossil", zHome);
#endif
  */
  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);
    }
    if( file_access(zHome, W_OK) ){
    rc = file_access(zHome, W_OK);
    if( rc ){
      if( isOptional ) return 0;
      fossil_panic("home directory %s must be writeable", zHome);
      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_panic("configuration file %s must be writeable", zDbName);
    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");
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
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







-





-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





-
+







-
+











-
+
+
+
+
+
+















+
+
+
+





-
+














-
+












+






-
+

+
+
+














+

+
+
+
+






-
+
-

-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-



-
+

-
-
-
+
+
+
+
+
+
+
+
+
+















-
+








/*
** 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;
  char *zVFileDef;

  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");
  zVFileDef = db_text(0, "SELECT sql FROM localdb.sqlite_master"
                         " WHERE name=='vfile'");
  if( zVFileDef==0 ) return 0;

  /* 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( sqlite3_strglob("* isexe *", zVFileDef)!=0 ){
  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( sqlite3_strglob("* islink *", zVFileDef)!=0 ){
  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");
    }
  }
  fossil_free(zVFileDef);

  /* 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(const char *zDbName){
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 checkout database file */
        /* 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 checkout database file could not be found */
  /* 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 the default value for the "allow-symlinks" setting
** Returns non-zero if support for symlinks is currently enabled.
** is "on".  When on Windows, this always returns false.
*/
int db_allow_symlinks_by_default(void){
#if defined(_WIN32)
  return 0;
#else
  return 1;
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;
#endif
}

/*
** Returns non-zero if support for symlinks is currently enabled.
** COMMAND: test-is-repo
*/
int db_allow_symlinks(void){
  return g.allowSymlinks;
}
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( file_access(zDbName, R_OK) || file_size(zDbName, ExtFILE)<1024 ){
  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) ){
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
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







+

-
-
+
+







+




+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+














-
+






-
-
+
+
+





-
+







    }
  }
  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",
                                   db_allow_symlinks_by_default());
  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;               
  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_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 checkout if there is one.
** 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;
1708
1709
1710
1711
1712
1713
1714
1715






1716
1717
1718
1719
1720
1721
1722
2812
2813
2814
2815
2816
2817
2818

2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831







-
+
+
+
+
+
+







  }
  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)==0 ){
  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");
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
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







-
+














-
+


















-
+














+








/*
** 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 checkout
** 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 checkout");
    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 checkout");
    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);
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
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







-
-
-
+
+
+
+
+



+
-
+











+

+

















+














+







    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 && reportErrors ){
    fossil_warning("Transaction started at %s:%d never commits",
                   db.zStartFile, db.iStartLine);
  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 from PRAGMA optimize */
  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;
}

/*
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
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







+




-
-
+
+




-
+



-
+




+







  }
  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=lower(hex(randomblob(3)))"
     " WHERE login=%Q", zUser
     "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)),'hmnc','Anon');"
       "   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','','dei','Dev');"
       "   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.
*/
2004
2005
2006
2007
2008
2009
2010

2011
2012
2013
2014
2015
2016
2017
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137







+







  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)"
2062
2063
2064
2065
2066
2067
2068

2069
2070
2071
2072
2073
2074
2075
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196







+







      "  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);
2086
2087
2088
2089
2090
2091
2092
2093

2094
2095
2096
2097
2098
2099
2100
3207
3208
3209
3210
3211
3212
3213

3214
3215
3216
3217
3218
3219
3220
3221







-
+







    blob_reset(&hash);
    rid = content_put(&manifest);
    manifest_crosslink(rid, &manifest, MC_NONE);
  }
}

/*
** COMMAND: new*
** 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
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
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







-
+

-
+
+
+
+
+







-
+






+
+
+







+
+




















+
+




+
+





+








-
+












+
+
+
+
-
+




+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+


-
+







** 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
**    --admin-user|-A USERNAME     Select given USERNAME as admin user
**    -A|--admin-user USERNAME     Select given USERNAME as admin user
**    --date-override DATETIME     Use DATETIME as time of the initial check-in
**    --sha1                       Use a initial hash policy of "sha1"
**    --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
** 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.
*/
LOCAL void db_sql_print(
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();
*/
LOCAL int db_sql_trace(unsigned m, void *notUsed, void *pP, void *pX){
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\n", zSql, (n>0 && zSql[n-1]==';') ? "" : ";");
  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.
2280
2281
2282
2283
2284
2285
2286
















2287

2288
2289
2290
2291
2292
2293
2294
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+







    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 a artifact hash.  Make a notation in the
** 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
2359
2360
2361
2362
2363
2364
2365
2366

2367
2368
2369
2370
2371



2372
2373
2374
2375
2376
2377
2378
3534
3535
3536
3537
3538
3539
3540

3541
3542
3543



3544
3545
3546
3547
3548
3549
3550
3551
3552
3553







-
+


-
-
-
+
+
+







    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 ~/.fossil database instead of on the repository database.
** work on the configuration database instead of on the repository database.
** Be sure to swap them back after doing the operation.
**
** If the ~/.fossil 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.
** 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
2415
2416
2417
2418
2419
2420
2421
2422

2423
2424
2425
2426
2427
2428
2429
3590
3591
3592
3593
3594
3595
3596

3597
3598
3599
3600
3601
3602
3603
3604







-
+







  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 checkout if there wasn't a cache hit. */
  /* 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);
2454
2455
2456
2457
2458
2459
2460

2461
2462
2463
2464
2465
2466
2467
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643







+







      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));
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
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







+
+
-
-
+
+
+
+
+
+
+

+
+

-
+

+
+
+
+
+



-
+
+

+
+
+










-
+
+












+
+
+
+
+
+
+
+
+
+
+
+














+



+










+















-
-
+
+
+




-
+




+

-
+

+
+
+
+
+



+
+
+
+

+
+












+



-
-
+
+
+
+
+
+


















+
+
+
+
+
+
+
+
+
+






+
+
+
+
+


















-
+








-
+



+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







** 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;
    z = db_text(0, "SELECT value FROM config WHERE name=%Q", zName);
  }
    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();
    z = db_text(0, "SELECT value FROM global_config WHERE name=%Q", zName);
    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 */
    ** 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 *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 ){
    Stmt q;
    db_prepare(&q, "SELECT value FROM config WHERE name=%Q", zName);
    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_finalize(&q);
    db_reset(&q);
  }else{
    rc = SQLITE_DONE;
  }
  if( rc==SQLITE_DONE && g.zConfigDbName ){
    static Stmt q2;
    db_swap_connections();
    v = db_int(dflt, "SELECT value FROM global_config WHERE name=%Q", zName);
    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) ) return 1;
  if( is_false(zVal) ) return 0;
  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, zNew, iGlobal);
  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, iGlobal);
  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 */
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
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







-
+




-
+













+
+









+





+



















+











-
+

-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-

+
+
+
+
+
+

-
-
+
+

+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+






-
+
+

+
+
+
+
+
+
+



-
+


+
+
+
+
+
+
+







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
-
+






-
+
+
+
+



-
-
-
-
-
-
-
-
-
-
-
-
-






-
+






-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
















+
+
+
+
+
+









-
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+














+
+
+
-
-
+
+
+
+
+



-
-
-
+
-
-
-
-
-
-
+
+
-












-
-
+
+
+
+
+







-
-
-
+
+
+
+
+
+

















-

-
+

-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-

-
-
-
+
+
+
-
-

-







-
-
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+








-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+














+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+







-
-
-
+
+
+
+







** The repository filename %s is recorded as an entry with a "name" field
** of the following form:
**
**       repo:%s
**
** The value field is set to 1.
**
** If running from a local checkout, also record the root of the checkout
** If running from a local check-out, also record the root of the check-out
** as follows:
**
**       ckout:%s
**
** Where %s is the checkout root.  The value is the repository file.
** 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 FILENAME ?VERSION? ?OPTIONS?
** Usage: %fossil open REPOSITORY ?VERSION? ?OPTIONS?
**
** Open a connection to the local repository in FILENAME.  A checkout
** for the repository is created with its root at the working directory.
** If VERSION is specified then that version is checked out.  Otherwise
** the latest version is checked out.  No files other than "manifest"
** 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:
** and "manifest.uuid" are modified if the --keep option is present.
**
**     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 checkout as being empty, but still connected
**                     with the local repository. If you commit this checkout,
**   --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
**   --keep            Only modify the manifest and manifest.uuid files
**   --nested          Allow opening a repository inside an opened checkout
**   --force-missing   Force opening a repository with missing content
**   -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
** See also: [[close]], [[clone]]
*/
void cmd_open(void){
  int emptyFlag;
  int keepFlag;
  int forceMissingFlag;
  int allowNested;
  int allowSymlinks;
  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",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( !allowNested && db_open_local(0) ){
    fossil_fatal("already within an open tree rooted at %s", g.zLocalRoot);
  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(g.argv[2]);
  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", "trunk");
      g.zOpenRevision = db_get("main-branch", 0);
    }
    if( autosync_loop(SYNC_PULL, !bForce, "open") && !bForce ){
      fossil_fatal("unable to auto-sync the repository");
    }
  }

  if( g.zOpenRevision ){
    /* Since the repository is open and we know the revision now,
    ** refresh the allow-symlinks flag.  Since neither the local
    ** checkout nor the configuration database are open at this
    ** point, this should always return the versioned setting,
    ** if any, or the default value, which is negative one.  The
    ** value negative one, in this context, means that the code
    ** below should fallback to using the setting value from the
    ** repository or global configuration databases only. */
    allowSymlinks = db_get_versioned_boolean("allow-symlinks", -1);
  }else{
    allowSymlinks = -1; /* Use non-versioned settings only. */
  }

#if defined(_WIN32) || defined(__CYGWIN__)
# define LOCALDB_NAME "./_FOSSIL_"
#else
# define LOCALDB_NAME "./.fslckout"
#endif
  db_init_database(LOCALDB_NAME, zLocalSchema,
  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);
  if( allowSymlinks>=0 ){
    /* Use the value from the versioned setting, which was read
    ** prior to opening the local checkout (i.e. which is most
    ** likely empty and does not actually contain any versioned
    ** setting files yet).  Normally, this value would be given
    ** first priority within db_get_boolean(); however, this is
    ** a special case because we know the on-disk files may not
    ** exist yet. */
    g.allowSymlinks = allowSymlinks;
  }else{
    /* Since the local checkout may not have any files at this
    ** point, this will probably be the setting value from the
    ** repository or global configuration databases. */
    g.allowSymlinks = db_get_boolean("allow-symlinks",
                                     db_allow_symlinks_by_default());
  }
  db_lset("repository", g.argv[2]);
  db_record_repository_filename(g.argv[2]);
  db_lset_int("checkout", 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){
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));
      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( pSetting->versionable && g.localOpen ){
    /* Check to see if this is overridden by a versionable settings file */
    Blob versionedPathname;
  if( versioned ){
    blob_zero(&versionedPathname);
    blob_appendf(&versionedPathname, "%s.fossil-settings/%s",
                 g.zLocalRoot, pSetting->name);
    if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){
      fossil_print("  (overridden by contents of file .fossil-settings/%s)\n",
                   pSetting->name);
    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.
** 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. */
  int versionable;      /* Is this setting versionable? */
  int forceTextArea;    /* Force using a text area for display? */
  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.
*/
#if defined(_WIN32)
/*
** SETTING: allow-symlinks  boolean default=off versionable
** SETTING: allow-symlinks  boolean default=off sensitive
**
** When allow-symlinks is OFF, symbolic links in the repository are followed
** and treated no differently from real files.  When allow-symlinks is ON,
** the object to which the symbolic link points is ignored, and the content
** of the symbolic link that is stored in the repository is the name of the
** object to which the symbolic link points.
*/
#endif
#if !defined(_WIN32)
/*
** 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.
** SETTING: allow-symlinks  boolean default=on versionable
**
** When allow-symlinks is OFF, symbolic links in the repository are followed
** and treated no differently from real files.  When allow-symlinks is ON,
** the object to which the symbolic link points is ignored, and the content
** 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.
** of the symbolic link that is stored in the repository is the name of the
** object to which the symbolic link points.
*/
#endif
/*
** 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  boolean default=on
** Use javascript to enable hyperlinks on web pages
** SETTING: auto-hyperlink  width=16 default=1
**
** If non-zero, enable hyperlinks on web pages even for users that lack
** for all users (regardless of the "h" privilege) if the
** User-Agent string in the HTTP header look like it came
** from real person, not a spider or bot.
** 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 can take either a boolean value or "pullonly"
** If enabled, automatically pull prior to commit
** or update and automatically push after commit or
** tag or branch creation.  If the value is "pullonly"
** 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
** then only pull operations occur automatically.
**
**    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
** 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 comma or newline-separated list of
** GLOB patterns that should be treated as binary files
** for committing and merging purposes.  Example: *.jpg
** 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
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
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







-
-
+
+
-
-
+
+







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
+
+
+
+







-
+











-
+



+
+
+
+
+











-
+





-
-
-
-
+
+
+
+



-
+
-
-
-
-
+
+
+
+















+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+




-
+





-
+

















-
+
-
-
+
-
-
+
+



-
-
+
+
+
+



-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+








-
+


















+
+
+
+
+
+
+







-



-
+

-
+

-

-
+




-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+








-
+




+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+






-
+


+

-
+











+
+
+


-
+










-
+







-
+





+
+
+
+
+
+


-
+

















-
+





-
+




+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+








-
+





+
+
+
+
+
+
+







** 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 comma or newline-separated list of GLOB
** patterns specifying files that the "clean" command will
** The VALUE of this setting is a list of GLOB patterns matching files
** that the "clean" command will delete without prompting or allowing
** delete without prompting or allowing undo.
** Example: *.a,*.lib,*.o
** 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 is a comma or newline-separated list of GLOB patterns for
** text files in which it is ok to have CR, CR+LF or mixed
** line endings. Set to "*" to disable CR+LF checking.
** 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
** 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
** 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
** 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 comma or newline-separated list of pathnames. On
** update and checkout commands, if no file or directory
** exists with that name, an empty directory will be
** created.
** 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 is a comma or newline-separated list of GLOB
** The VALUE of this setting is a list of GLOB patterns matching files that
** patterns specifying 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.
** 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
** 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
** 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"
**     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 is a comma or newline-separated list of GLOB
** The VALUE of this setting is a list of GLOB patterns matching files that
** patterns specifying files that the "add", "addremove",
** "clean", and "extras" commands will ignore.
** the "add", "addremove", "clean", and "extras" commands will ignore.
**
** Example:  *.log customCode.c notes.txt
** 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 is a comma or newline-separated list of GLOB
** patterns specifying files that the "clean" command will keep.
** 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
** 127.0.0.1 be authenticated by password.  If
** false, all HTTP requests from localhost have
** unrestricted access to the repository.
** 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 checkout.
** 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.
*/
#if FOSSIL_ENABLE_LEGACY_MV_RM
/*
** SETTING: mv-rm-files      boolean default=off
** If enabled, the "mv" and "rename" commands will also move
** the associated files within the checkout -AND- the "rm"
** the associated files within the check-out -AND- the "rm"
** and "delete" commands will also remove the associated
** files from within the checkout.
** files from within the check-out.
*/
#endif
/*
** SETTING: pgp-command      width=40
** 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=off
** URL of the HTTP proxy.  If undefined or "off" then
** the "http_proxy" environment variable is consulted.
** If the http_proxy environment variable is undefined
** then a direct HTTP connection is used.
** 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 checkout as a double-check
** 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
** 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
** 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
** 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
** 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
** 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 versionable block-text
** 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
** 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 versionable block-text
** 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 versionable block-text
** 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
** 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.
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
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







-
-
-
-
+
+
+


-
-
+
+
-
-
+
+

-
+





+
+
+
+















+
+
+
+
+
+



-
+













-
+















-
+

+
-
+
+











+
+
+
+
+
+
+
+
+
-
+
+







** 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
** $HOME/.fossil file on unix or in the %LOCALAPPDATA%/_fossil file on Windows.
** 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.
** 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.
**   --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.
**   --exact    Only consider exact name matches
**   --value    Only show the value of a given property (implies --exact)
**
** See also: configuration
** 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]);
      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!=strlen(pSetting[0].name) && pSetting[1].name &&
      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, globalFlag);
        db_unset(pSetting->name/*works-like:"x"*/, globalFlag);
      }else{
        db_protect_only(PROTECT_NONE);
        db_set(pSetting->name, g.argv[3], globalFlag);
        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);
          print_setting(pSetting, valueFlag);
        }
        pSetting++;
      }
    }
  }else{
    usage("?PROPERTY? ?VALUE? ?-global?");
  }
}
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
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







+
+








-
-
+
+





-
+










-
+







  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 ~/.fossil file or a local
** .fslckout or _FOSSIL_ file.
** 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:
**    --dryrun | -n         No changes.  Just print what would happen.
**    -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_master "
      "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);
3612
3613
3614
3615
3616
3617
3618

3619
3620
3621
3622
3623
3624
3625
3626
3627
3628











3629
3630
3631
3632
3633
3634
3635
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







+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+








/*
** 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 IF NOT EXISTS 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"
    ")"
  );
    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, ...){
3652
3653
3654
3655
3656
3657
3658
3659

3660
3661
3662
3663
3664
3665
3666
3667






















































































































































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







-
+








+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
}

/*
** COMMAND: test-database-names
**
** Print the names of the various database files:
** (1) The main repository database
** (2) The local checkout 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;
  }
}

Deleted src/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





























































































































































































































































































































































































































































































































































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
// This is the template file for the default CSS for Fossil.  Lines
// beginning with "//" are stripped out by the pre-processor and never
// reach the web browser.
//
// Each repository skin has skin-specific CSS.  The rules contained in this
// file are appended to the skin-CSS as required.  Each rule is evaluated
// separately and is only appended to the final CSS if there is not an
// overriding rule with the same selector in the skin-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);
}
tr.timelineSelected {
  padding: .1em .2em;
  border: 2px solid lightgray;
  background-color: #ffc;
  box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.5);
}
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.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;
}
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;
}
.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\/\/\/yEhIf\/\/\/wAAACH5BAEHAAIALAAAAAAQABAAAAIvlIKpxqcfmgOUvoaqDSCxrEEfF14GqFXImJZsu73wepJzVMNxrtNTj3NATMKhpwAAOw==);
  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 {
  background-image: url(data:image/gif;base64,R0lGODlhEAAQAJEAAP\/\/\/yEhIf\/\/\/wAAACH5BAEHAAIALAAAAAAQABAAAAIvlIKpxqcfmgOUvoaqDSCxrEEfF14GqFXImJZsu73wepJzVMNxrtNTj3NATMKhpwAAOw==);
  background-repeat: no-repeat;
  background-position: 0px center;
  padding-left: 20px;
  padding-top: 2px;
}
ul.browser li.dir {
  background-image: url(data:image/gif;base64,R0lGODlhEAAQAJEAAP/WVCIiIv\/\/\/wAAACH5BAEHAAIALAAAAAAQABAAAAInlI9pwa3XYniCgQtkrAFfLXkiFo1jaXpo+jUs6b5Z/K4siDu5RPUFADs=);
  background-repeat: no-repeat;
  background-position: 0px center;
  padding-left: 20px;
  padding-top: 2px;
}
div.filetreeline {
  display: table;
  width: 100%;
  white-space: nowrap;
}
.filetree .dir > div.filetreeline > a {
  background-image: url(data:image/gif;base64,R0lGODlhEAAQAJEAAP/WVCIiIv\/\/\/wAAACH5BAEHAAIALAAAAAAQABAAAAInlI9pwa3XYniCgQtkrAFfLXkiFo1jaXpo+jUs6b5Z/K4siDu5RPUFADs=);
}
div.filetreeage {
 display: table-cell;
 padding-left: 3em;
 text-align: right;
}
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:"'";
}
div.selectedText {
  font-weight: bold;
  color: blue;
  background-color: #d5d5ff;
  border: 1px blue solid;
}
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;
}
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-collapse:collapse;
  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;
}
table.sbsdiffcols {
  width: 90%;
  border-spacing: 0;
  font-size: xx-small;
}
table.sbsdiffcols td {
  padding: 0;
  vertical-align: top;
}
table.sbsdiffcols pre {
  margin: 0;
  padding: 0;
  border: 0;
  font-size: inherit;
  background: inherit;
  color: inherit;
}
div.difflncol {
  padding-right: 1em;
  text-align: right;
  color: #a0a0a0;
}
div.difftxtcol {
  width: 45em;
  overflow-x: auto;
}
div.diffmkrcol {
  padding: 0 1em;
}
span.diffchng {
  background-color: #c0c0ff;
}
span.diffadd {
  background-color: #c0ffc0;
}
span.diffrm {
  background-color: #ffc8c8;
}
span.diffhr {
  display: inline-block;
  margin: .5em 0 1em;
  color: #0000ff;
}
span.diffln {
  color: #a0a0a0;
}
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 {
  background-color: #446979;
}
.statistics-report-table-events th {
  padding: 0 1em 0 1em;
}
.statistics-report-table-events td {
  padding: 0.1em 1em 0.1em 1em;
}
.statistics-report-row-year {
  text-align: left;
}
.statistics-report-week-number-label {
  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.forumHier, div.forumTime {
  border: 1px solid black;
  padding-left: 1ex;
  padding-right: 1ex;
  margin-top: 1ex;
}
div.forumSel {
  background-color: #cef;
}
div.forumObs {
  color: #bbb;
}
#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;
}

Changes to src/delta.c.

202
203
204
205
206
207
208
209


210
211
212
213
214
215
216
202
203
204
205
206
207
208

209
210
211
212
213
214
215
216
217







-
+
+







  return v;
}

/*
** Return the number digits in the base-64 representation of a positive integer
*/
static int digit_count(int v){
  unsigned int i, x;
  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
377
378
379
380
381
382
383
384

385
386
387
388
389
390
391
392
393
394

395
396
397
398
399
400
401
378
379
380
381
382
383
384

385
386
387
388
389
390
391
392
393
394

395
396
397
398
399
400
401
402







-
+









-
+







  /* 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<lenSrc-NHASH; i+=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<lenOut ){
  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;
447
448
449
450
451
452
453
454

455
456
457
458
459
460
461
448
449
450
451
452
453
454

455
456
457
458
459
460
461
462







-
+







        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>bestCnt ){
        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"); )
        }
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
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







-
+








-
+


















-
+







        }
        base += bestCnt;
        putInt(bestCnt, &zDelta);
        *(zDelta++) = '@';
        putInt(bestOfst, &zDelta);
        DEBUG2( printf("copy %d bytes from %d\n", bestCnt, bestOfst); )
        *(zDelta++) = ',';
        if( bestOfst + bestCnt -1 > lastRead ){
        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>=lenOut ){
      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<lenOut ){
  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);
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
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







-
+















-
+







        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( ofst+cnt > lenSrc ){
        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( cnt>lenDelta ){
        if( (int)cnt>lenDelta ){
          /* ERROR: insert count exceeds size of delta */
          return -1;
        }
        memcpy(zOut, zDelta, cnt);
        zOut += cnt;
        zDelta += cnt;
        lenDelta -= cnt;
686
687
688
689
690
691
692
693

694
695
696
697
698
699
700
687
688
689
690
691
692
693

694
695
696
697
698
699
700
701







-
+







        zDelta++; lenDelta--;
        nCopy += cnt;
        break;
      }
      case ':': {
        zDelta++; lenDelta--;
        nInsert += cnt;
        if( cnt>lenDelta ){
        if( (int)cnt>lenDelta ){
          /* ERROR: insert count exceeds size of delta */
          return -1;
        }
        zDelta += cnt;
        lenDelta -= cnt;
        break;
      }

Changes to src/deltacmd.c.

58
59
60
61
62
63
64
65

66
67
68
69
70
71
72
58
59
60
61
62
63
64

65
66
67
68
69
70
71
72







-
+







  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])<blob_size(&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);
}

141
142
143
144
145
146
147
148

149
150

151
152
153
154
155
156
157
158
159
160
161
162

163
164

165
166
167
168
169
170
171
141
142
143
144
145
146
147

148
149

150
151
152
153
154
155
156
157
158
159
160
161
162
163
164

165
166
167
168
169
170
171
172







-
+

-
+












+

-
+







  *pTarget = out;
  return len;
}

/*
** COMMAND: test-delta-apply
**
** Usage: %fossil test-delta-apply FILE1 DELTA
** Usage: %fossil test-delta-apply FILE1 DELTA FILE2
**
** Apply DELTA to FILE1 and output the result.
** 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])<blob_size(&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);
}

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.

157
158
159
160
161
162
163
164


165
166

167
168
169
170
171


172

































































173
174
175
176
177
178
179
180
181


182
183
184
185






186
187
188
189
190
191
192
157
158
159
160
161
162
163

164
165
166

167
168
169
170
171
172
173
174

175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246


247
248
249



250
251
252
253
254
255
256
257
258
259
260
261
262







-
+
+

-
+





+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







-
-
+
+

-
-
-
+
+
+
+
+
+







      TAG_CLOSED
    );
  }
}

/*
** Load the record ID rid and up to |N|-1 closest ancestors into
** the "ok" table.  If N is zero, no limit.
** 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){
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(
    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 %s"
    "     ORDER BY mtime DESC LIMIT %d"
    "       AND event.objid=plink.pid"
    "     ORDER BY mtime DESC"
    "  )"
    "INSERT INTO ok"
    "  SELECT rid FROM ancestor;",
    rid, rid, directOnly ? "AND plink.isPrim" : "", N
    "  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
221
222
223
224
225
226
227
228

229
230
231
232
233
234
235
291
292
293
294
295
296
297

298
299
300
301
302
303
304
305







-
+







  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);
    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);
271
272
273
274
275
276
277
278
279
280
281




282
283

284
285
286
287
288
289
290
341
342
343
344
345
346
347




348
349
350
351
352

353
354
355
356
357
358
359
360







-
-
-
-
+
+
+
+

-
+







**
** Usage: %fossil descendants ?CHECKIN? ?OPTIONS?
**
** Find all leaf descendants of the check-in specified or if the argument
** is omitted, of the check-in currently checked out.
**
** Options:
**    -R|--repository FILE       Extract info from repository FILE
**    -W|--width <num>           Width of lines (default is to auto-detect).
**                               Must be >20 or 0 (= no limit, resulting in a
**                               single line per entry).
**    -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
** See also: [[finfo]], [[info]], [[leaves]]
*/
void descendants_cmd(void){
  Stmt q;
  int base, width;
  const char *zWidth;

  db_find_and_open_repository(0,0);
310
311
312
313
314
315
316
317

318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341








342
343

344
345
346
347
348
349
350
351
352
353
354
355
356

357
358
359
360
361
362
363
380
381
382
383
384
385
386

387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403








404
405
406
407
408
409
410
411
412

413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434







-
+
















-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+

-
+













+







  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);
  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 <num> Width of lines (default is to auto-detect). Must be
**                    >39 or 0 (= no limit, resulting in a single line per
**                    entry).
**   -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
** 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");
    }
419
420
421
422
423
424
425
426


427
428
429
430
431
432
433
434
435
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
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







-
+
+










+
+
+
+
+
+
+
+
+
+
-
-
+
+
+

+

+


















+
+
+
+
+






+
+
+
+
+
+
+



-
+
+
+
+
+
+

-
+


-
+


-
+

+
+
+


+







  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;
    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", zDate, zId, zCom);
    comment_print(z, zCom, 7, width, g.comFmtFlags);
    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", "leaves?all");
    style_submenu_element("All", "%s", url_render(&url, "all", "", 0, 0));
  }
  if( !showClosed ){
    style_submenu_element("Closed", "leaves?closed");
    style_submenu_element("Closed", "%s", url_render(&url, "closed", "", 0, 0));
  }
  if( showClosed || showAll ){
    style_submenu_element("Open", "leaves");
    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
503
504
505
506
507
508
509







510
511






512

513
514
515


516
517
518
519
520
521
522
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







+
+
+
+
+
+
+


+
+
+
+
+
+
-
+

-
-
+
+







  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, TIMELINE_LEAFONLY, 0, 0, 0, 0);
  www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, 0);
  db_finalize(&q);
  @ <br />
  style_footer();
  @ <br>
  style_finish_page();
}

#if INTERFACE
/* Flag parameters to compute_uses_file() */
#define USESFILE_DELETE   0x01  /* Include the check-ins where file deleted */

#endif

Changes to src/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
1
2
3
4
5
6

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
















32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79

80
81

82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133





134
135
136
137
138
139
140
141
142
143

144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163






-
+
















+







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


















-
+

-
+


+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+










-
-
-
-
-
+
+
+
+
+
+




-
+







+
+
+
+
+







/*
** 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_CONTEXT_MASK ((u64)0x0000ffff) /* Lines of context. Default if 0 */
#define DIFF_WIDTH_MASK   ((u64)0x00ff0000) /* side-by-side column width */
#define DIFF_IGNORE_EOLWS ((u64)0x01000000) /* Ignore end-of-line whitespace */
#define DIFF_IGNORE_ALLWS ((u64)0x03000000) /* Ignore all whitespace */
#define DIFF_SIDEBYSIDE   ((u64)0x04000000) /* Generate a side-by-side diff */
#define DIFF_VERBOSE      ((u64)0x08000000) /* Missing shown as empty files */
#define DIFF_BRIEF        ((u64)0x10000000) /* Show filenames only */
#define DIFF_HTML         ((u64)0x20000000) /* Render for HTML */
#define DIFF_LINENO       ((u64)0x40000000) /* Show line numbers */
#define DIFF_NUMSTAT      ((u64)0x80000000) /* Show line count of changes */
#define DIFF_NOOPT        (((u64)0x01)<<32) /* Suppress optimizations (debug) */
#define DIFF_INVERT       (((u64)0x02)<<32) /* Invert the diff (debug) */
#define DIFF_CONTEXT_EX   (((u64)0x04)<<32) /* Use context even if zero */
#define DIFF_NOTTOOBIG    (((u64)0x08)<<32) /* Only display if not too big */
#define DIFF_STRIP_EOLCR  (((u64)0x10)<<32) /* Strip trailing CR */
#define DIFF_SLOW_SBS     (((u64)0x20)<<32) /* Better but slower side-by-side */
#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**13 = 8192 bytes)
** Maximum length of a line in a text file, in bytes.  (2**15 = 32768 bytes)
*/
#define LENGTH_MASK_SZ  13
#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 */
  unsigned int h;       /* Hash of the line */
  unsigned short indent;  /* Indent of the line. Only !=0 with -w/-Z option */
  unsigned short n;     /* number of bytes */
  unsigned int iNext;   /* 1+(Index of next line with same the same hash) */
  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) */
  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
113
114
115
116
117
118
119
120

121






















122
123
124
125
126
127
128




129
130

131
132
133
134
135
136
137
172
173
174
175
176
177
178

179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214

215
216
217
218
219
220
221
222







-
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







+
+
+
+

-
+







  int *aEdit;        /* Array of copy/delete/insert triples */
  int nEdit;         /* Number of integers (3x num of triples) in aEdit[] */
  int nEditAlloc;    /* Space allocated for aEdit[] */
  DLine *aFrom;      /* File on left side of the diff */
  int nFrom;         /* Number of lines in aFrom[] */
  DLine *aTo;        /* File on right side of the diff */
  int nTo;           /* Number of lines in aTo[] */
  int (*same_fn)(const DLine*,const DLine*); /* comparison function */
  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.
*/
static int count_lines(
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++){}
162
163
164
165
166
167
168
169

170
171
172
173
174
175
176
247
248
249
250
251
252
253

254
255
256
257
258
259
260
261







-
+







static DLine *break_into_lines(
  const char *z,
  int n,
  int *pnLine,
  u64 diffFlags
){
  int nLine, i, k, nn, s, x;
  unsigned int h, h2;
  u64 h, h2;
  DLine *a;
  const char *zNL;

  if( count_lines(z, n, &nLine)==0 ){
    return 0;
  }
  assert( nLine>0 || z[0]=='\0' );
191
192
193
194
195
196
197
198
199
200

201
202
203
204



205
206
207

208
209
210

211
212
213
214
215


216
217


218

219



220
221
222

223
224
225
226
227
228
229
230
231
232
233
234
235
236
237

238
239
240



241
242
243
244

245
246
247
248
249

250
251




252
253
254
255



256
257

258
259

260
261
262
263
264
265
266
267
268
269



270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286

287
288
289
290

291
292
293
294
295
296
297
298
299
300
301
302
303
304

305
306

307
308
309
310
311
312

313
314
315

316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332

333
334
335
336
337

338
339
340
341
342
343




344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359


360
361
362
363
364
365
366
367
368

369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
276
277
278
279
280
281
282

283

284
285
286
287

288
289
290
291
292

293
294
295

296

297
298
299
300
301
302


303
304

305
306
307
308
309
310


311
312
313
314
315
316
317
318
319
320
321
322
323
324
325

326
327


328
329
330
331
332
333

334
335
336
337


338

339
340
341
342
343
344



345
346
347
348

349
350

351
352
353
354
355
356
357
358



359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377

378


379

380














381


382
383
384
385
386
387

388

389

390

391
392
393
394
395
396
397
398
399
400

401
402
403


404
405
406
407
408

409

410




411
412
413
414
415
416
417
418
419
420
421
422

423
424

425
426


427
428

429
430
431
432
433
434
435

436
437
438

























439
440
441
442
443
444
445







-

-
+



-
+
+
+


-
+


-
+
-




+
+
-
-
+
+
-
+

+
+
+

-
-
+














-
+

-
-
+
+
+



-
+



-
-
+
-

+
+
+
+

-
-
-
+
+
+

-
+

-
+







-
-
-
+
+
+
















-
+
-
-

-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
+





-
+
-

-
+
-










-



-
-
+




-
+
-

-
-
-
-
+
+
+
+








-


-


-
-
+
+
-







-
+


-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-







    }
    a[i].z = z;
    k = nn;
    if( diffFlags & DIFF_STRIP_EOLCR ){
      if( k>0 && z[k-1]=='\r' ){ k--; }
    }
    a[i].n = k;
    s = 0;
    if( diffFlags & DIFF_IGNORE_EOLWS ){
      while( k>0 && fossil_isspace(z[k-1]) ){ k--; }
      while( k>0 && diff_isspace(z[k-1]) ){ k--; }
    }
    if( (diffFlags & DIFF_IGNORE_ALLWS)==DIFF_IGNORE_ALLWS ){
      int numws = 0;
      while( s<k && fossil_isspace(z[s]) ){ s++; }
      for(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( fossil_isspace(c) ){
        if( diff_isspace(c) ){
          ++numws;
        }else{
          h += c;
          h = (h^c)*9000000000000000041LL;
          h *= 0x9e3779b1;
        }
      }
      k -= numws;
    }else{
      int k2 = k & ~0x7;
      u64 m;
      for(h=0, x=s; x<k; x++){
        h += z[x];
      for(h=x=s=0; x<k2; x += 8){
        memcpy(&m, z+x, 8);
        h *= 0x9e3779b1;
        h = (h^m)*9000000000000000041LL;
      }
      m = 0;
      memcpy(&m, z+x, k-k2);
      h ^= m;
    }
    a[i].indent = s;
    a[i].h = h = (h<<LENGTH_MASK_SZ) | (k-s);
    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 true if two DLine elements are identical.
** Return zero if two DLine elements are identical.
*/
static int same_dline(const DLine *pA, const DLine *pB){
  return pA->h==pB->h && memcmp(pA->z,pB->z, pA->h&LENGTH_MASK)==0;
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 true if two DLine elements are identical, ignoring
** 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 same_dline_ignore_allws(const DLine *pA, const DLine *pB){
static int compare_dline_ignore_allws(const DLine *pA, const DLine *pB){
  int a = pA->indent, b = pB->indent;
  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 0;
      while( a<pA->n && fossil_isspace(pA->z[a])) ++a;
      while( b<pB->n && fossil_isspace(pB->z[b])) ++b;
      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 pA->n-a != pB->n-b;
  }
  return 0;
  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 */
  DLine *aDLine,      /* First of N DLines to compare against */
  int N               /* Number of DLines to check */
  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 "-" */
  DLine *pLine,       /* The line to be output */
  const DLine *pLine  /* The line to be output */
  int html,           /* True if generating HTML.  False for plain text */
  ReCompiled *pRe     /* Colorize only if line matches this Regex */
){
  blob_append(pOut, &cPrefix, 1);
  blob_append_char(pOut, cPrefix);
  if( html ){
    if( pRe && re_dline_match(pRe, pLine, 1)==0 ){
      cPrefix = ' ';
    }else if( cPrefix=='+' ){
      blob_append(pOut, "<span class=\"diffadd\">", -1);
    }else if( cPrefix=='-' ){
      blob_append(pOut, "<span class=\"diffrm\">", -1);
    }
    htmlize_to_blob(pOut, pLine->z, pLine->n);
    if( cPrefix!=' ' ){
      blob_append(pOut, "</span>", -1);
    }
  }else{
    blob_append(pOut, pLine->z, pLine->n);
  blob_append(pOut, pLine->z, pLine->n);
  }
  blob_append(pOut, "\n", 1);
  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.  The "html" parameter means to format
** to leave that number field blank.
** the output for HTML.
*/
static void appendDiffLineno(Blob *pOut, int lnA, int lnB, int html){
static void appendDiffLineno(Blob *pOut, int lnA, int lnB){
  if( html ) blob_append(pOut, "<span class=\"diffln\">", -1);
  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);
  }
  if( html ) blob_append(pOut, "</span>", -1);
}

/*
** Given a raw diff p[] in which the p->aEdit[] array has been filled
** in, compute a context diff into pOut.
** Output a patch-style text diff.
*/
static void contextDiff(
  DContext *p,      /* The difference */
  Blob *pOut,       /* Output a context diff to here */
  ReCompiled *pRe,  /* Only show changes that match this regex */
  DiffConfig *pCfg  /* Configuration options */
  u64 diffFlags     /* Flags controlling the diff format */
){
  DLine *A;     /* Left side of the diff */
  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[] */
  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 */
  static int nChunk = 0;  /* Number of diff chunks seen so far */
  int nContext;    /* Number of lines of context */
  int showLn;      /* Show line numbers */
  int html;        /* Render as HTML */
  int showDivider = 0;  /* True to show the divider between diff blocks */

  nContext = diff_context_lines(diffFlags);
  showLn = (diffFlags & DIFF_LINENO)!=0;
  nContext = diff_context_lines(pCfg);
  showLn = (pCfg->diffFlags & DIFF_LINENO)!=0;
  html = (diffFlags & DIFF_HTML)!=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; R[r+nr*3]>0 && R[r+nr*3]<nContext*2; nr++){}
    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); */

    /* 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( 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(pRe, &A[xa], R[r+i*3+1]);
        c2 = re_dline_match(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;
      }
    }

    /* 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;
      skip = R[r] - nContext;
    }else{
424
425
426
427
428
429
430
431
432
433
434
435
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
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







-
-



-

-






-
-
+
+
-








-
-
+
+








-
-
+
+




-
-
+
+





-
-
+
+











-
-
+
+




-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-

+
-
+

+
-
-
+
+
-
-
+
-
-
-
-
-
+
-
-
+
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
-







    ** the previous block.
    */
    nChunk++;
    if( showLn ){
      if( !showDivider ){
        /* Do not show a top divider */
        showDivider = 1;
      }else if( html ){
        blob_appendf(pOut, "<span class=\"diffhr\">%.80c</span>\n", '.');
      }else{
        blob_appendf(pOut, "%.80c\n", '.');
      }
      if( html ) blob_appendf(pOut, "<span id=\"chunk%d\"></span>", nChunk);
    }else{
      if( html ) blob_appendf(pOut, "<span class=\"diffln\">");
      /*
       * 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 : 0, na,
        nb ? b+skip+1 : 0, nb);
        na ? a+skip+1 : a+skip, na,
        nb ? b+skip+1 : b+skip, nb);
      if( html ) blob_appendf(pOut, "</span>");
      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, html);
      appendDiffLine(pOut, ' ', &A[a+j], html, 0);
      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, html);
        appendDiffLine(pOut, '-', &A[a+j], html, pRe);
        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, html);
        appendDiffLine(pOut, '+', &B[b+j], html, pRe);
        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, html);
          appendDiffLine(pOut, ' ', &A[a+j], html, 0);
          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, html);
      appendDiffLine(pOut, ' ', &A[a+j], html, 0);
      if( showLn ) appendDiffLineno(pOut, a+j+1, b+j+1);
      appendDiffLine(pOut, ' ', &A[a+j]);
    }
  }
}

/*
** Status of a single output line
*/
typedef struct SbsLine SbsLine;
struct SbsLine {
  Blob *apCols[5];         /* Array of pointers to output columns */
  int width;               /* Maximum width of a column in the output */
  unsigned char escHtml;   /* True to escape html characters */
  int iStart;              /* Write zStart prior to character iStart */
  const char *zStart;      /* A <span> tag */
  int iEnd;                /* Write </span> prior to character iEnd */
  int iStart2;             /* Write zStart2 prior to character iStart2 */
  const char *zStart2;     /* A <span> tag */
  int iEnd2;               /* Write </span> prior to character iEnd2 */
  ReCompiled *pRe;         /* Only colorize matching lines, if not NULL */
};

#define MX_CSN  8  /* Maximum number of change spans across a change region */
/*
** Column indices for SbsLine.apCols[]
*/
#define SBS_LNA  0     /* Left line number */
#define SBS_TXTA 1     /* Left text */
#define SBS_MKR  2     /* Middle separator column */
#define SBS_LNB  3     /* Right line number */
#define SBS_TXTB 4     /* Right text */

/*
** Append newlines to all columns.
*/
static void sbsWriteNewlines(SbsLine *p){
  int i;
  for( i=p->escHtml ? SBS_LNA : SBS_TXTB; i<=SBS_TXTB; i++ ){
    blob_append(p->apCols[i], "\n", 1);
  }
}

/*
** Append n spaces to the column.
*/
static void sbsWriteSpace(SbsLine *p, int n, int col){
  blob_appendf(p->apCols[col], "%*s", n, "");
}

/*
** Write the text of pLine into column iCol of p.
**
** If outputting HTML, write the full line.  Otherwise, only write the
** width characters.  Translate tabs into spaces.  Add newlines if col
** is SBS_TXTB.  Translate HTML characters if escHtml is true.  Pad the
** rendering to width bytes if col is SBS_TXTA and escHtml is false.
**
** This comment contains multibyte unicode characters (ü, Æ, ð) in order
** to test the ability of the diff code to handle such characters.
*/
static void sbsWriteText(SbsLine *p, DLine *pLine, int col){
  Blob *pCol = p->apCols[col];
  int n = pLine->n;
  int i;   /* Number of input characters consumed */
  int k;   /* Cursor position */
  int needEndSpan = 0;
  const char *zIn = pLine->z;
  int w = p->width;
  int colorize = p->escHtml;
  if( colorize && p->pRe && re_dline_match(p->pRe, pLine, 1)==0 ){
    colorize = 0;
  }
  for(i=k=0; (p->escHtml || k<w) && i<n; i++, k++){
    char c = zIn[i];
    if( colorize ){
      if( i==p->iStart ){
        int x = strlen(p->zStart);
        blob_append(pCol, p->zStart, x);
        needEndSpan = 1;
        if( p->iStart2 ){
          p->iStart = p->iStart2;
          p->zStart = p->zStart2;
          p->iStart2 = 0;
        }
      }else if( i==p->iEnd ){
        blob_append(pCol, "</span>", 7);
        needEndSpan = 0;
        if( p->iEnd2 ){
          p->iEnd = p->iEnd2;
          p->iEnd2 = 0;
        }
      }
    }
    if( c=='\t' && !p->escHtml ){
      blob_append(pCol, " ", 1);
      while( (k&7)!=7 && (p->escHtml || k<w) ){
        blob_append(pCol, " ", 1);
        k++;
      }
    }else if( c=='\r' || c=='\f' ){
      blob_append(pCol, " ", 1);
    }else if( c=='<' && p->escHtml ){
      blob_append(pCol, "&lt;", 4);
    }else if( c=='&' && p->escHtml ){
      blob_append(pCol, "&amp;", 5);
    }else if( c=='>' && p->escHtml ){
      blob_append(pCol, "&gt;", 4);
    }else if( c=='"' && p->escHtml ){
      blob_append(pCol, "&quot;", 6);
    }else{
      blob_append(pCol, &zIn[i], 1);
      if( (c&0xc0)==0x80 ) k--;
    }
  }
  if( needEndSpan ){
    blob_append(pCol, "</span>", 7);
  }
  if( col==SBS_TXTB ){
    sbsWriteNewlines(p);
  }else if( !p->escHtml ){
    sbsWriteSpace(p, w-k, SBS_TXTA);
  }
}

/*
** Append a column to the final output blob.
*/
static void sbsWriteColumn(Blob *pOut, Blob *pCol, int col){
  blob_appendf(pOut,
    "<td><div class=\"diff%scol\">\n"
    "<pre>\n"
    "%s"
    "</pre>\n"
    "</div></td>\n",
    (col % 3) ? (col == SBS_MKR ? "mkr" : "txt") : "ln",
    blob_str(pCol)
  );
}


/*
** A description of zero or more (up to MX_CSN) areas of difference
** Append a separator line to column iCol
** between two lines of text.
*/
typedef struct LineChange LineChange;
static void sbsWriteSep(SbsLine *p, int len, int col){
  char ch = '.';
struct LineChange {
  int n;            /* Number of change spans */
  if( len<1 ){
    len = 1;
  struct Span {
    ch = ' ';
  }
  blob_appendf(p->apCols[col], "<span class=\"diffhr\">%.*c</span>\n", len, ch);
}

    int iStart1;    /* Byte offset to start of a change on the left */
/*
** Append the appropriate marker into the center column of the diff.
    int iLen1;      /* Length of the left change in bytes */
*/
static void sbsWriteMarker(SbsLine *p, const char *zTxt, const char *zHtml){
  blob_append(p->apCols[SBS_MKR], p->escHtml ? zHtml : zTxt, -1);
}

    int iStart2;    /* Byte offset to start of a change on the right */
/*
** Append a line number to the column.
*/
static void sbsWriteLineno(SbsLine *p, int ln, int col){
  if( p->escHtml ){
    blob_appendf(p->apCols[col], "%d", ln+1);
  }else{
    char zLn[7];
    sqlite3_snprintf(7, zLn, "%5d ", ln+1);
    blob_appendf(p->apCols[col], "%s ", zLn);
  }
    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:
**
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
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







-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
-
-
+
-
-
+
+
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+



-
+

-
-
-
+
+
+
-
-
+
-
-
-
-
+
+
-

-
+

-
-
+
+
-
-
+
+

+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+


-
-
+
+
+
+
+

-
+
-
-
+
-
-
+
-
+






+




-
-
-
-
















+
-
+







+






-
+








-
+





-
+











+


-
-
+
+
-
-
-
-
+
+
+
-
-
-
-
-
-
-
-
+
+
-
-
+



-
-
+
+
-
-
-
-
-
-
+
+
+
+
-
-
-
-
-
+
+







-
-
+
+
-
-
-
-
+
+
+
+

-
-
+
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-

-
-
-
-
+
-




-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+









+
-
-
+
+
+






+
+









+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+




+
+
+
+
+
+
+
+
+








-








-
+










+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+














-
-
-
-
+
+
+







-
-
-
-
+
+
+
+
+





-
-
-


-

+

+



+

+



-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+






+
+




















-
+














-






-
-














-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-












-
-
+
+
+
+
+
+



-
-
+

-
-
-
+
+
+
-
-
+

-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
-
-
-
+
-
-

-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-








-
+
-






-
+






-
-
+
+











-
-
+
+

-
+
-


-


-
-
-
+
+
+
-
-
-
-
+
+
+
+
-
-
-
-
-
+
+
+
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-

-
-
-
-
+
-
-
-
-
-
+
+
-
-
-
-

-
-
-
+
-
-
-






+




-
-
-
-
+
+
+
+






+
-
+
+

+
-
-
+
+
+
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-

-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
+
-
-
-
-
-
-
-
+
-
-
-
-
-
+
+
+
+
+
+
+
+




-
-
-
+
-
-
-










-
+
-
-
-
+
-
-
-


-
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+







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 nt;                    /* Number of target points */
  int ti[3];                 /* Index for start of each 4-byte target */
  unsigned int target[3];    /* 4-byte alignment targets */
  unsigned int probe;        /* probe to compare against target */
  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];
  int iAS, iAE, iBS, iBE;    /* Range of common segment */
  int i, j;                  /* Loop counters */
  int rc = 0;                /* Result code.  1 for success */

  if( nA<6 || nB<6 ) return 0;
  memset(aLCS, 0, sizeof(int)*4);
  ti[0] = i = nB/2-2;
    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;
}

/*
  target[0] = (zB[i]<<24) | (zB[i+1]<<16) | (zB[i+2]<<8) | zB[i+3];
  probe = 0;
  if( nB<16 ){
    nt = 1;
** 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;
  }else{
    ti[1] = i = nB/4-2;
  p->a[0].iStart1 = 0;
  p->a[0].iLen1 = nA;
  p->a[0].iStart2 = 0;
  p->a[0].iLen2 = nB;
    target[1] = (zB[i]<<24) | (zB[i+1]<<16) | (zB[i+2]<<8) | zB[i+3];
    ti[2] = i = (nB*3)/4-2;
  p->a[0].isMin = 0;
    target[2] = (zB[i]<<24) | (zB[i+1]<<16) | (zB[i+2]<<8) | zB[i+3];
    nt = 3;
  while( p->n<MX_CSN-1 ){
    int mxi = -1;
  }
  probe = (zA[0]<<16) | (zA[1]<<8) | zA[2];
  for(i=3; i<nA; i++){
    probe = (probe<<8) | zA[i];
    for(j=0; j<nt; j++){
      if( probe==target[j] ){
        iAS = i-3;
        iAE = i+1;
    int mxLen = -1;
    int x, i;
        iBS = ti[j];
        iBE = ti[j]+4;
        while( iAE<nA && iBE<nB && zA[iAE]==zB[iBE] ){ iAE++; iBE++; }
        while( iAS>0 && iBS>0 && zA[iAS-1]==zB[iBS-1] ){ iAS--; iBS--; }
        if( iAE-iAS > aLCS[1] - aLCS[0] ){
          aLCS[0] = iAS;
          aLCS[1] = iAE;
          aLCS[2] = iBS;
          aLCS[3] = iBE;
          rc = 1;
        }
      }
    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));
  }
  return rc;
    }
    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;
}

/*
** Try to shift iStart as far as possible to the left.
** Return true if the string starts with n spaces
*/
static void sbsShiftLeft(SbsLine *p, const char *z){
  int i, j;
  while( (i=p->iStart)>0 && z[i-1]==z[i] ){
static int allSpaces(const char *z, int n){
  int i;
  for(i=0; i<n && diff_isspace(z[i]); i++){}
    for(j=i+1; j<p->iEnd && z[j-1]==z[j]; j++){}
    if( j<p->iEnd ) break;
  return i==n;
    p->iStart--;
    p->iEnd--;
  }
}
}


/*
** Simplify iStart and iStart2:
** Try to improve the human-readability of the LineChange p.
**
**    *  If iStart is a null-change then move iStart2 into iStart
**    *  Make sure any null-changes are in canonoical form.
** (1)  If the first change span shows a change of indentation, try to
**      move that indentation change to the left margin.
**    *  Make sure all changes are at character boundaries for
**       multi-byte characters.
**
** (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;
static void sbsSimplifyLine(SbsLine *p, const char *z){
  if( p->iStart2==p->iEnd2 ){
    p->iStart2 = p->iEnd2 = 0;

  /* (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;
  }else if( p->iStart2 ){
    while( p->iStart2>0 && (z[p->iStart2]&0xc0)==0x80 ) p->iStart2--;
    while( (z[p->iEnd2]&0xc0)==0x80 ) p->iEnd2++;
  }
  if( p->iStart==p->iEnd ){
    p->iStart = p->iStart2;
    p->iEnd = p->iEnd2;
    p->zStart = p->zStart2;
    p->iStart2 = 0;
    p->iEnd2 = 0;
  }
  if( p->iStart==p->iEnd ){
    p->iStart = p->iEnd = -1;
  }else if( p->iStart>0 ){
    }
    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;
  }

    while( p->iStart>0 && (z[p->iStart]&0xc0)==0x80 ) p->iStart--;
    while( (z[p->iEnd]&0xc0)==0x80 ) p->iEnd++;
  }
}
  /* (2) Try to shift changes so that they begin or end with a
  ** space.  (TBD) */
}


/*
** Write out lines that have been edited.  Adjust the highlight to cover
** only those parts of the line that actually changed.
** 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 sbsWriteLineChange(
static void oneLineChange(
  SbsLine *p,          /* The SBS output line */
  DLine *pLeft,        /* Left line of the change */
  const DLine *pLeft,  /* Left line of the change */
  int lnLeft,          /* Line number for the left line */
  DLine *pRight,       /* Right line of the change */
  const DLine *pRight, /* Right line of the change */
  int lnRight          /* Line number of the right line */
  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 */
  int aLCS[4];         /* Bounds of common middle segment */
  static const char zClassRm[]   = "<span class=\"diffrm\">";
  static const char zClassAdd[]  = "<span class=\"diffadd\">";
  static const char zClassChng[] = "<span class=\"diffchng\">";

  nLeft = pLeft->n;
  zLeft = pLeft->z;
  nRight = pRight->n;
  zRight = pRight->z;
  nShort = nLeft<nRight ? nLeft : nRight;

  nPrefix = 0;
  while( nPrefix<nShort && zLeft[nPrefix]==zRight[nPrefix] ){
    nPrefix++;
  }
  if( nPrefix<nShort ){
    while( nPrefix>0 && (zLeft[nPrefix]&0xc0)==0x80 ) nPrefix--;
  }
  nSuffix = 0;
  if( nPrefix<nShort ){
    while( nSuffix<nShort
    while( nSuffix<nShort && zLeft[nLeft-nSuffix-1]==zRight[nRight-nSuffix-1] ){
           && 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( nPrefix+nSuffix > nShort ){
  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( fossil_isspace(c) ){
       if( diff_isspace(c) ){
         iVal += 5;
       }else if( !fossil_isalnum(c) ){
         iVal += 2;
       }
       c = zLeft[i+nGap-1];
       if( fossil_isspace(c) ){
       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 on the right */
  if( nPrefix+nSuffix==nLeft ){
  /* A single chunk of text inserted */
  if( nCommon==nLeft ){
    sbsWriteLineno(p, lnLeft, SBS_LNA);
    p->iStart2 = p->iEnd2 = 0;
    p->iStart = p->iEnd = -1;
    sbsWriteText(p, pLeft, SBS_TXTA);
    p->n = 1;
    p->a[0].iStart1 = nPrefix;
    p->a[0].iLen1 = 0;
    if( nLeft==nRight && zLeft[nLeft]==zRight[nRight] ){
      sbsWriteMarker(p, "   ", "");
    }else{
      sbsWriteMarker(p, " | ", "|");
    }
    sbsWriteLineno(p, lnRight, SBS_LNB);
    p->iStart = nPrefix;
    p->iEnd = nRight - nSuffix;
    p->a[0].iStart2 = nPrefix;
    p->a[0].iLen2 = nRight - nCommon;
    p->zStart = zClassAdd;
    sbsWriteText(p, pRight, SBS_TXTB);
    improveReadability(zLeft, zRight, p);
    return;
  }

  /* A single chunk of text deleted from the left */
  if( nPrefix+nSuffix==nRight ){
  /* A single chunk of text deleted */
  if( nCommon==nRight ){
    /* Text deleted from the left */
    sbsWriteLineno(p, lnLeft, SBS_LNA);
    p->iStart2 = p->iEnd2 = 0;
    p->iStart = nPrefix;
    p->iEnd = nLeft - nSuffix;
    p->zStart = zClassRm;
    p->n = 1;
    p->a[0].iStart1 = nPrefix;
    p->a[0].iLen1 = nLeft - nCommon;
    p->a[0].iStart2 = nPrefix;
    sbsWriteText(p, pLeft, SBS_TXTA);
    sbsWriteMarker(p, " | ", "|");
    sbsWriteLineno(p, lnRight, SBS_LNB);
    p->iStart = p->iEnd = -1;
    sbsWriteText(p, pRight, SBS_TXTB);
    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 - nSuffix - nPrefix;
  nRightDiff = nRight - nSuffix - nPrefix;
  nLeftDiff = nLeft - nCommon;
  nRightDiff = nRight - nCommon;
  if( p->escHtml
   && nLeftDiff >= 6
   && nRightDiff >= 6
   && textLCS(&zLeft[nPrefix], nLeftDiff, &zRight[nPrefix], nRightDiff, aLCS)
  if( nLeftDiff >= 4
   && nRightDiff >= 4
   && textLineChanges(&zLeft[nPrefix], nLeftDiff,
                      &zRight[nPrefix], nRightDiff, p)>1
  ){
    sbsWriteLineno(p, lnLeft, SBS_LNA);
    p->iStart = nPrefix;
    int i;
    p->iEnd = nPrefix + aLCS[0];
    if( aLCS[2]==0 ){
      sbsShiftLeft(p, pLeft->z);
      p->zStart = zClassRm;
    for(i=0; i<p->n; i++){
    }else{
      p->zStart = zClassChng;
    }
    p->iStart2 = nPrefix + aLCS[1];
    p->iEnd2 = nLeft - nSuffix;
    p->zStart2 = aLCS[3]==nRightDiff ? zClassRm : zClassChng;
    sbsSimplifyLine(p, zLeft);
    sbsWriteText(p, pLeft, SBS_TXTA);
    sbsWriteMarker(p, " | ", "|");
    sbsWriteLineno(p, lnRight, SBS_LNB);
    p->iStart = nPrefix;
    p->iEnd = nPrefix + aLCS[2];
      p->a[i].iStart1 += nPrefix;
      p->a[i].iStart2 += nPrefix;
    if( aLCS[0]==0 ){
      sbsShiftLeft(p, pRight->z);
      p->zStart = zClassAdd;
    }else{
      p->zStart = zClassChng;
    }
    p->iStart2 = nPrefix + aLCS[3];
    p->iEnd2 = nRight - nSuffix;
    p->zStart2 = aLCS[1]==nLeftDiff ? zClassAdd : zClassChng;
    sbsSimplifyLine(p, zRight);
    improveReadability(zLeft, zRight, p);
    sbsWriteText(p, pRight, SBS_TXTB);
    return;
  }

  /* If all else fails, show a single big change between left and right */
  sbsWriteLineno(p, lnLeft, SBS_LNA);
  p->iStart2 = p->iEnd2 = 0;
  p->iStart = nPrefix;
  p->iEnd = nLeft - nSuffix;
  p->zStart = zClassChng;
  sbsWriteText(p, pLeft, SBS_TXTA);
  sbsWriteMarker(p, " | ", "|");
  sbsWriteLineno(p, lnRight, SBS_LNB);
  p->iEnd = nRight - nSuffix;
  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];
  sbsWriteText(p, pRight, SBS_TXTB);
  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\">"
                       "&#xfe19;</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\">&#xfe19;</td>"
       "<td></td><td></td>"
       "<td class=\"diffln difflnr difflne\">&#xfe19;</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], "&gt;\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], "&lt;\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
** (3) Find the length of the longest common subsequence
** (4) Longer common subsequences yield lower scores.
** (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++){}
  zB = pB->z;
  nA = pA->n;
  nB = pB->n;
  while( nA>0 && fossil_isspace(zA[0]) ){ nA--; zA++; }
  while( nA>0 && fossil_isspace(zA[nA-1]) ){ nA--; }
  while( nB>0 && fossil_isspace(zB[0]) ){ nB--; zB++; }
  while( nB>0 && fossil_isspace(zB[nB-1]) ){ nB--; }
    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;
  }
  best = 0;
  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 = (best>avg) ? 0 : (avg - best)*100/avg;
  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.
**
** Values larger than three indicate better matches.
**
** The length of the returned array will be just large enough to cause
** all elements of pLeft and pRight to be consumed.
** 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 *sbsAlignment(
   DLine *aLeft, int nLeft,       /* Text on the left */
   DLine *aRight, int nRight,     /* Text on the right */
   u64 diffFlags                  /* Flags passed into the original diff */
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 nMatch, iMatch;          /* Number of matching lines and match score */
  int mnLen;                   /* MIN(nLeft, nRight) */
  int mxLen;                   /* MAX(nLeft, nRight) */
  int aBuf[100];               /* Stack space for a[] if nRight not to big */

  aM = fossil_malloc( (nLeft+1)*(nRight+1) );
  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;
  }

  /* This algorithm is O(N**2).  So if N is too big, bail out with a
  ** simple (but stupid and ugly) result that doesn't take too long. */
  mnLen = nLeft<nRight ? nLeft : nRight;
  if( nLeft*nRight>100000 && (diffFlags & DIFF_SLOW_SBS)==0 ){
  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
    memset(aM, 4, mnLen);
    if( nLeft>mnLen )  memset(aM+mnLen, 1, nLeft-mnLen);
  ){
    if( (pCfg->diffFlags & DIFF_IGNORE_ALLWS)==0 ){
      unsigned char *aRes;
      aRes = diffBlockAlignmentIgnoreSpace(
                 aLeft, nLeft,aRight, nRight,pCfg,pNResult);
    if( nRight>mnLen ) memset(aM+mnLen, 2, nRight-mnLen);
    return aM;
  }

      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<=63 || (i<j+1 && i>j-1)) && m>p+score ){
        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;
  nMatch = iMatch = 0;
  while( i+j>0 ){
    unsigned char c = aM[k];
    if( c>=3 ){
      assert( i>0 && j>0 );
      i--;
      j--;
      nMatch++;
      iMatch += (c>>2);
      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;
  /* If:
  **   (1) the alignment is more than 25% longer than the longest side, and
  **   (2) the average match cost exceeds 15
  ** Then this is probably an alignment that will be difficult for humans
  ** to read.  So instead, just show all of the right side inserted followed
  ** by all of the left side deleted.
  **
  ** The coefficients for conditions (1) and (2) above are determined by
  ** experimentation.
  */
  mxLen = nLeft>nRight ? nLeft : nRight;
  if( i*4>mxLen*5 && (nMatch==0 || iMatch/nMatch>15) ){
    memset(aM, 4, mnLen);
    if( nLeft>mnLen )  memset(aM+mnLen, 1, nLeft-mnLen);
    if( nRight>mnLen ) memset(aM+mnLen, 2, nRight-mnLen);
  }

  /* 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(int *R){
  return R[3]<=2 || R[3]<=(R[1]+R[2]+R[4]+R[5])/8;
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;
}

/*
** Given a diff context in which the aEdit[] array has been filled
** in, compute a side-by-side diff into pOut.
** Format a diff using a DiffBuilder object
*/
static void sbsDiff(
  DContext *p,       /* The computed diff */
  Blob *pOut,        /* Write the results here */
static void formatDiff(
  DContext *p,           /* The computed diff */
  DiffConfig *pCfg,      /* Configuration options */
  ReCompiled *pRe,   /* Only show changes that match this regex */
  u64 diffFlags      /* Flags controlling the diff */
  DiffBuilder *pBuilder  /* The formatter object */
){
  DLine *A;     /* Left side of the diff */
  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 */
  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 */
  int na, nb;   /* Number of lines shown from A and B */
  int i, j;     /* Loop counters */
  int m, ma, mb;/* Number of lines to output */
  int skip;     /* Number of lines to skip */
  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 */
  static int nChunk = 0; /* Number of chunks of diff output seen so far */
  SbsLine s;    /* Output line buffer */
  int nContext; /* Lines of context above and below each change */
  unsigned int nContext; /* Lines of context above and below each change */
  int showDivider = 0;  /* True to show the divider */
  Blob aCols[5]; /* Array of column blobs */

  memset(&s, 0, sizeof(s));
  s.width = diff_width(diffFlags);
  nContext = diff_context_lines(diffFlags);
  nContext = diff_context_lines(pCfg);
  s.escHtml = (diffFlags & DIFF_HTML)!=0;
  if( s.escHtml ){
    for(i=SBS_LNA; i<=SBS_TXTB; i++){
      blob_zero(&aCols[i]);
      s.apCols[i] = &aCols[i];
    }
  }else{
    for(i=SBS_LNA; i<=SBS_TXTB; i++){
      s.apCols[i] = pOut;
    }
  }
  s.pRe = pRe;
  s.iStart = -1;
  s.iStart2 = 0;
  s.iEnd = -1;
  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; R[r+nr*3]>0 && R[r+nr*3]<nContext*2; nr++){}
    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); */

    /* 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( pRe ){
    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(pRe, &A[xa], R[r+i*3+1]);
        c2 = re_dline_match(pRe, &B[xb], R[r+i*3+2]);
        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;
      }
    }

    /* For the current block comprising nr triples, figure out
    ** how many lines of A and B are to be displayed
    /* Figure out how many lines of A and B are to be displayed
    ** for this change block.
    */
    if( R[r]>nContext ){
    if( R[r]>(int)nContext ){
      na = nb = nContext;
      skip = R[r] - nContext;
    }else{
      na = nb = R[r];
      skip = 0;
    }
    for(i=0; i<nr; i++){
      na += R[r+i*3+1];
      nb += R[r+i*3+2];
    /* Show the initial common area */
    a += skip;
    b += skip;
    }
    if( R[r+nr*3]>nContext ){
      na += nContext;
      nb += nContext;
    m = R[r] - skip;
    if( r ) skip -= nContext;
    if( skip>0 ){
      if( skip<(int)nContext ){
    }else{
      na += R[r+nr*3];
      nb += R[r+nr*3];
    }
    for(i=1; i<nr; i++){
        /* 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++){
      na += R[r+i*3];
      nb += R[r+i*3];
    }
          pBuilder->xCommon(pBuilder, &A[a+j-skip]);
        }

    /* Draw the separator between blocks */
    if( showDivider ){
      if( s.escHtml ){
        char zLn[10];
        sqlite3_snprintf(sizeof(zLn), zLn, "%d", a+skip+1);
        sbsWriteSep(&s, strlen(zLn), SBS_LNA);
        sbsWriteSep(&s, s.width, SBS_TXTA);
        sbsWriteSep(&s, 0, SBS_MKR);
        sqlite3_snprintf(sizeof(zLn), zLn, "%d", b+skip+1);
        sbsWriteSep(&s, strlen(zLn), SBS_LNB);
        sbsWriteSep(&s, s.width, SBS_TXTB);
      }else{
        blob_appendf(pOut, "%.*c\n", s.width*2+16, '.');
      }
    }
    showDivider = 1;
        pBuilder->xSkip(pBuilder, skip, 0);
    nChunk++;
    if( s.escHtml ){
      blob_appendf(s.apCols[SBS_LNA], "<span id=\"chunk%d\"></span>", nChunk);
    }

      }
    }
    /* Show the initial common area */
    a += skip;
    b += skip;
    m = R[r] - skip;
    for(j=0; j<m; j++){
      sbsWriteLineno(&s, a+j, SBS_LNA);
      s.iStart = s.iEnd = -1;
      sbsWriteText(&s, &A[a+j], SBS_TXTA);
      pBuilder->xCommon(pBuilder, &A[a+j]);
      sbsWriteMarker(&s, "   ", "");
      sbsWriteLineno(&s, b+j, SBS_LNB);
      sbsWriteText(&s, &B[b+j], SBS_TXTB);
    }
    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 */

      /* If the gap between the current diff and then next diff within the
      ** same block is not too great, then render them as if they are a
      ** single diff. */
      while( i<nr-1 && smallGap(&R[r+i*3]) ){
      /* 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 = sbsAlignment(&A[a], ma, &B[b], mb, diffFlags);
      alignment = diffBlockAlignment(&A[a], ma, &B[b], mb, pCfg, &nAlign);

      for(j=0; ma+mb>0; j++){
        assert( (int)j<nAlign );
        if( alignment[j]==1 ){
          /* Delete one line from the left */
        switch( alignment[j] ){
          case 1: {
            /* Delete one line from the left */
          sbsWriteLineno(&s, a, SBS_LNA);
          s.iStart = 0;
          s.zStart = "<span class=\"diffrm\">";
          s.iEnd = LENGTH(&A[a]);
            pBuilder->xDelete(pBuilder, &A[a]);
          sbsWriteText(&s, &A[a], SBS_TXTA);
          sbsWriteMarker(&s, " <", "&lt;");
          sbsWriteNewlines(&s);
          assert( ma>0 );
          ma--;
          a++;
        }else if( alignment[j]==3 ){
          /* The left line is changed into the right line */
          sbsWriteLineChange(&s, &A[a], a, &B[b], b);
          assert( ma>0 && mb>0 );
          ma--;
          mb--;
          a++;
          b++;
        }else if( alignment[j]==2 ){
            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;
          /* Insert one line on the right */
          if( !s.escHtml ){
            sbsWriteSpace(&s, s.width + 7, SBS_TXTA);
          }
          sbsWriteMarker(&s, " > ", "&gt;");
          sbsWriteLineno(&s, b, SBS_LNB);
          s.iStart = 0;
          s.zStart = "<span class=\"diffadd\">";
          s.iEnd = LENGTH(&B[b]);
          sbsWriteText(&s, &B[b], SBS_TXTB);
          assert( mb>0 );
          mb--;
          b++;
        }else{
          /* Delete from the left and insert on the right */
          case 4: {
            /* Delete from left then separately insert on the right */
          sbsWriteLineno(&s, a, SBS_LNA);
          s.iStart = 0;
          s.zStart = "<span class=\"diffrm\">";
          s.iEnd = LENGTH(&A[a]);
          sbsWriteText(&s, &A[a], SBS_TXTA);
            pBuilder->xReplace(pBuilder, &A[a], &B[b]);
          sbsWriteMarker(&s, " | ", "|");
          sbsWriteLineno(&s, b, SBS_LNB);
          s.iStart = 0;
          s.zStart = "<span class=\"diffadd\">";
          s.iEnd = LENGTH(&B[b]);
          sbsWriteText(&s, &B[b], SBS_TXTB);
          ma--;
            ma--;
          mb--;
          a++;
          b++;
        }
      }
            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++){
          sbsWriteLineno(&s, a+j, SBS_LNA);
          s.iStart = s.iEnd = -1;
          sbsWriteText(&s, &A[a+j], SBS_TXTA);
          pBuilder->xCommon(pBuilder, &A[a+j]);
          sbsWriteMarker(&s, "   ", "");
          sbsWriteLineno(&s, b+j, SBS_LNB);
          sbsWriteText(&s, &B[b+j], SBS_TXTB);
        }
        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++){
    for(j=0; j<m && j<nContext; j++){
      sbsWriteLineno(&s, a+j, SBS_LNA);
      s.iStart = s.iEnd = -1;
      sbsWriteText(&s, &A[a+j], SBS_TXTA);
      pBuilder->xCommon(pBuilder, &A[a+j]);
      sbsWriteMarker(&s, "   ", "");
      sbsWriteLineno(&s, b+j, SBS_LNB);
      sbsWriteText(&s, &B[b+j], SBS_TXTB);
    }
  }

  if( R[r]>(int)nContext ){
  if( s.escHtml && blob_size(s.apCols[SBS_LNA])>0 ){
    blob_append(pOut, "<table class=\"sbsdiffcols\"><tr>\n", -1);
    for(i=SBS_LNA; i<=SBS_TXTB; i++){
      sbsWriteColumn(pOut, s.apCols[i], i);
      blob_reset(s.apCols[i]);
    }
    blob_append(pOut, "</tr></table>\n", -1);
  }
}
    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.
*/
1442
1443
1444
1445
1446
1447
1448
1449
1450


1451
1452
1453
1454

1455
1456
1457
1458
1459
1460
1461
2398
2399
2400
2401
2402
2403
2404


2405
2406
2407
2408
2409

2410
2411
2412
2413
2414
2415
2416
2417







-
-
+
+



-
+







  int i, j;                  /* Loop counters */
  int k;                     /* Length of a candidate subsequence */
  int iSXb = iS1;            /* Best match so far */
  int iSYb = iS2;            /* Best match so far */

  for(i=iS1; i<iE1-mxLength; i++){
    for(j=iS2; j<iE2-mxLength; j++){
      if( !p->same_fn(&p->aFrom[i], &p->aTo[j]) ) continue;
      if( mxLength && !p->same_fn(&p->aFrom[i+mxLength], &p->aTo[j+mxLength]) ){
      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->same_fn(&p->aFrom[i+k],&p->aTo[j+k]) ){
      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;
      }
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
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







-
+





+
+









+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







){
  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 span */
  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->same_fn(&p->aFrom[i], &p->aTo[j-1]))
    ){
      if( limit++ > 10 ){
        j = 0;
        break;
      }
      j = p->aTo[j-1].iNext;
    }
    if( j==0 ) continue;
    assert( i>=iSXb && i>=iSXp );
    if( i<iEXb && j>=iSYb && j<iEYb ) continue;
    if( i<iEXp && j>=iSYp && j<iEYp ) continue;
    iSX = i;
    iSY = j-1;
    pA = &p->aFrom[iSX-1];
    pB = &p->aTo[iSY-1];
    n = minInt(iSX-iS1, iSY-iS2);
    for(k=0; k<n && p->same_fn(pA,pB); k++, pA--, pB--){}
    iSX -= k;
    iSY -= k;
    iEX = i+1;
    iEY = j;
    pA = &p->aFrom[iEX];
    pB = &p->aTo[iEY];
    n = minInt(iE1-iEX, iE2-iEY);
    for(k=0; k<n && p->same_fn(pA,pB); k++, pA++, pB++){}
    iEX += k;
    iEY += k;
    skew = (iSX-iS1) - (iSY-iS2);
    if( skew<0 ) skew = -skew;
    dist = (iSX+iEX)/2 - mid;
    if( dist<0 ) dist = -dist;
    score = (iEX - iSX)*(sqlite3_int64)span - (skew + dist);
    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;
    }
  }
  if( iSXb==iEXb && (iE1-iS1)*(iE2-iS2)<400 ){
    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;
1611
1612
1613
1614
1615
1616
1617



















































































































1618
1619
1620
1621
1622
1623
1624
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







    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.
**
1643
1644
1645
1646
1647
1648
1649
1650



1651
1652
1653
1654
1655
1656
1657
2720
2721
2722
2723
2724
2725
2726

2727
2728
2729
2730
2731
2732
2733
2734
2735
2736







-
+
+
+







    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 ){
  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);
1679
1680
1681
1682
1683
1684
1685
1686

1687
1688
1689
1690
1691

1692
1693
1694
1695
1696
1697
1698
2758
2759
2760
2761
2762
2763
2764

2765
2766
2767
2768
2769

2770
2771
2772
2773
2774
2775
2776
2777







-
+




-
+







*/
static void diff_all(DContext *p){
  int mnE, iS, iE1, iE2;

  /* Carve off the common header and footer */
  iE1 = p->nFrom;
  iE2 = p->nTo;
  while( iE1>0 && iE2>0 && p->same_fn(&p->aFrom[iE1-1], &p->aTo[iE2-1]) ){
  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->same_fn(&p->aFrom[iS],&p->aTo[iS]); iS++){}
  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 ){
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
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







-
+












-
+












-
+












-
+















-
+
+
+

-
-
-
-
+
+
+
+
+
+
+
+
+





+
+
+
+
+
+

-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+















-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+









-
+
-




-
+




-
+





-
-
+
+

-
+


-
+

-
+




-
+










-
+


-
+








-
+



-
+




-
+





+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+
















+
+



-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+



+
+









+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



+
+


+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
+
+
+
+
+




+

-
+

-
+
+
+
+
+
+
+
+
+
+
+
+

-
+

-

-
+


-
+




+


-
+


-


+
+
-
+


-
-
+

+
-
+







    lnFrom += cpy;
    lnTo += cpy;

    /* Shift insertions toward the beginning of the file */
    while( cpy>0 && del==0 && ins>0 ){
      DLine *pTop = &p->aFrom[lnFrom-1];  /* Line before start of insert */
      DLine *pBtm = &p->aTo[lnTo+ins-1];  /* Last line inserted */
      if( p->same_fn(pTop, pBtm)==0 ) break;
      if( 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->same_fn(pTop, pBtm)==0 ) break;
      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->same_fn(pTop, pBtm)==0 ) break;
      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->same_fn(pTop, pBtm)==0 ) break;
      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.
** 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(u64 diffFlags){
  int n = diffFlags & DIFF_CONTEXT_MASK;
  if( n==0 && (diffFlags & DIFF_CONTEXT_EX)==0 ) n = 5;
  return n;
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(u64 diffFlags){
  int w = (diffFlags & DIFF_WIDTH_MASK)/(DIFF_CONTEXT_MASK+1);
  if( w==0 ) w = 80;
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 and pB.
** If pOut is not NULL then a unified diff is appended there.  It
** is assumed that pOut has already been initialized.  If pOut is
** NULL, then a pointer to an array of integers is returned.
** The integers come in triples.  For each triple,
** the elements are the number of lines copied, the number of
** lines deleted, and the number of lines inserted.  The vector
** is terminated by a triple of all zeros.
** 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 */
  ReCompiled *pRe, /* Only output changes where this Regexp matches */
  DiffConfig *pCfg /* Configuration options */
  u64 diffFlags    /* DIFF_* flags defined above */
){
  int ignoreWs; /* Ignore whitespace */
  DContext c;

  if( diffFlags & DIFF_INVERT ){
  if( pCfg->diffFlags & DIFF_INVERT ){
    Blob *pTemp = pA_Blob;
    pA_Blob = pB_Blob;
    pB_Blob = pTemp;
  }
  ignoreWs = (diffFlags & DIFF_IGNORE_ALLWS)!=0;
  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( (diffFlags & DIFF_IGNORE_ALLWS)==DIFF_IGNORE_ALLWS ){
    c.same_fn = same_dline_ignore_allws;
  if( (pCfg->diffFlags & DIFF_IGNORE_ALLWS)==DIFF_IGNORE_ALLWS ){
    c.xDiffer = compare_dline_ignore_allws;
  }else{
    c.same_fn = same_dline;
    c.xDiffer = compare_dline;
  }
  c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob),
                             &c.nFrom, diffFlags);
                             &c.nFrom, pCfg->diffFlags);
  c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob),
                           &c.nTo, diffFlags);
                           &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, diffFlags);
      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, diffFlags);
    if( pOut ) diff_errmsg(pOut, DIFF_WHITESPACE_ONLY, pCfg->diffFlags);
    return 0;
  }
  if( (diffFlags & DIFF_NOTTOOBIG)!=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, diffFlags);
      if( pOut ) diff_errmsg(pOut, DIFF_TOO_MANY_CHANGES, pCfg->diffFlags);
      return 0;
    }
  }
  if( (diffFlags & DIFF_NOOPT)==0 ){
  if( (pCfg->diffFlags & DIFF_NOOPT)==0 ){
    diff_optimize(&c);
  }

  if( pOut ){
    if( diffFlags & DIFF_NUMSTAT ){
    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( diffFlags & DIFF_SIDEBYSIDE ){
      sbsDiff(&c, pOut, pRe, diffFlags);
        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, pRe, diffFlags);
      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.    DIFF_CONTEXT_MASK
**   --html                     Format for HTML        DIFF_HTML
**   --invert                   Invert the diff        DIFF_INVERT
**   -n|--linenum               Show line numbers      DIFF_LINENO
**   --noopt                    Disable optimization   DIFF_NOOPT
**   --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.     DIFF_WIDTH_MASK
**   -y|--side-by-side          Side-by-side diff.     DIFF_SIDEBYSIDE
**   -Z|--ignore-trailing-space Ignore eol-whitespaces DIFF_IGNORE_EOLWS
**   --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
*/
u64 diff_options(void){
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("unified",0,0)!=0 ) diffFlags &= ~DIFF_SIDEBYSIDE;
  if( (z = find_option("context","c",1))!=0 && (f = atoi(z))>=0 ){
    if( f > DIFF_CONTEXT_MASK ) f = DIFF_CONTEXT_MASK;
    diffFlags |= f + DIFF_CONTEXT_EX;
  }
  if( (z = find_option("width","W",1))!=0 && (f = atoi(z))>0 ){
    f *= DIFF_CONTEXT_MASK+1;
    if( f > DIFF_WIDTH_MASK ) f = DIFF_CONTEXT_MASK;
    diffFlags |= f;
  }
  if( find_option("html",0,0)!=0 ) diffFlags |= DIFF_HTML;
    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) ){
  return diffFlags;
}

        diffFlags |= DIFF_INCBINARY;
      }
    }
/*
** COMMAND: test-rawdiff
**
** Usage: %fossil test-rawdiff FILE1 FILE2
**
** Show a minimal sequence of Copy/Delete/Insert operations needed to convert
** FILE1 into FILE2.  This command is intended for use in testing and debugging
** the built-in difference engine of Fossil.
*/
void test_rawdiff_cmd(void){
  Blob a, b;
  int r;
  int i;
  int *R;
  u64 diffFlags = diff_options();
  if( g.argc<4 ) usage("FILE1 FILE2 ...");
  blob_read_from_file(&a, g.argv[2], ExtFILE);
  for(i=3; i<g.argc; i++){
    if( i>3 ) fossil_print("-------------------------------\n");
    blob_read_from_file(&b, g.argv[i], ExtFILE);
    R = text_diff(&a, &b, 0, 0, diffFlags);
    for(r=0; R[r] || R[r+1] || R[r+2]; r += 3){
      fossil_print(" copy %4d  delete %4d  insert %4d\n", R[r], R[r+1], R[r+2]);
    }
  }
    /* free(R); */
    blob_reset(&b);
  }
  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 [options] FILE1 FILE2
** Usage: %fossil xdiff [options] FILE1 FILE2
**
** Print the difference between two files.  The usual diff options apply.
** 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 test_diff_cmd(void){
void xdiff_cmd(void){
  Blob a, b, out;
  u64 diffFlag;
  const char *zRe;           /* Regex filter for diff output */
  ReCompiled *pRe = 0;       /* Regex filter for diff output */
  DiffConfig DCfg;

  if( find_option("tk",0,0)!=0 ){
    diff_tk("test-diff", 2);
    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(&pRe, zRe, 0);
    const char *zErr = re_compile(&DCfg.pRe, zRe, 0);
    if( zErr ) fossil_fatal("regex error: %s", zErr);
  }
  diffFlag = diff_options();
  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], diffFlag);
  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);
  blob_zero(&out);
  text_diff(&a, &b, &out, pRe, diffFlag);
  text_diff(&a, &b, &out, &DCfg);
  blob_write_to_file(&out, "-");
  diff_end(&DCfg, 0);
  re_free(pRe);
  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.
*/

2105
2106
2107
2108
2109
2110
2111
2112

2113
2114

2115
2116
2117
2118
2119
2120
2121
3289
3290
3291
3292
3293
3294
3295

3296
3297

3298
3299
3300
3301
3302
3303
3304
3305







-
+

-
+







** will release it when it is finished with it.
*/
static int annotation_start(Annotator *p, Blob *pInput, u64 diffFlags){
  int i;

  memset(p, 0, sizeof(*p));
  if( (diffFlags & DIFF_IGNORE_ALLWS)==DIFF_IGNORE_ALLWS ){
    p->c.same_fn = same_dline_ignore_allws;
    p->c.xDiffer = compare_dline_ignore_allws;
  }else{
    p->c.same_fn = same_dline;
    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 );
2222
2223
2224
2225
2226
2227
2228

2229

2230
2231
2232
2233
2234
2235
2236
3406
3407
3408
3409
3410
3411
3412
3413

3414
3415
3416
3417
3418
3419
3420
3421







+
-
+








  if( zLimit ){
    if( strcmp(zLimit,"none")==0 ){
      iLimit = 0;
      mxTime = 0;
    }else if( sqlite3_strglob("*[0-9]s", zLimit)==0 ){
      iLimit = 0;
      mxTime =
      mxTime = current_time_in_milliseconds() + 1000.0*atof(zLimit);
        (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 */
2311
2312
2313
2314
2315
2316
2317
2318


2319
2320
2321
2322
2323
2324
2325
3496
3497
3498
3499
3500
3501
3502

3503
3504
3505
3506
3507
3508
3509
3510
3511







-
+
+







    }
    p->nVers++;
    cnt++;
  }

  if( p->nVers==0 ){
    if( zRevision ){
      fossil_fatal("file %s does not exist in check-in %s", zFilename, 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);
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377




2378
2379
2380
2381
2382




2383
2384
2385
2386
2387
2388
2389
3553
3554
3555
3556
3557
3558
3559




3560
3561
3562
3563
3564




3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575







-
-
-
-
+
+
+
+

-
-
-
-
+
+
+
+







** 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:
**                           "none"  No limit
**                           "Xs"    As much as can be computed in X seconds
**                           "N"     N 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 checkin.  If unspecified, the root
**                           check-in over the entire repository is used.
**                           Specify "origin=trunk" or similar for a reverse
**                           annotation
**    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 */
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
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







-
+








+






+







  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;
  load_control();
  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"));
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
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







-
+

-
+










-
+





-
+



-
+





-
+







  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&ci=%!S&orig=%!S",zFilename,zCI,zOrigin);
    zLink = href("%R/finfo?name=%t&from=%!S&to=%!S",zFilename,zCI,zOrigin);
  }else{
    zLink = href("%R/finfo?name=%t&ci=%!S",zFilename,zCI);
    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 />
  @ <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&ci=%!S", zFilename, zCI))%h(zFilename)</a>
    @ %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&ci=%!S", zFilename, zCI))%h(zFilename)</a>
    @ %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&ci=%!S", zFilename, zCI))%h(zFilename)</a>
    @ %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;
2524
2525
2526
2527
2528
2529
2530
2531

2532
2533
2534
2535
2536
2537

2538
2539
2540
2541
2542
2543
2544
3712
3713
3714
3715
3716
3717
3718

3719
3720
3721
3722
3723
3724

3725
3726
3727
3728
3729
3730
3731
3732







-
+





-
+







        sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%*s%4d:",szHash+12,"",i+1);
      }
    }
    @ %s(zPrefix) %h(z)

  }
  @ </pre>
  style_footer();
  style_finish_page();
}

/*
** COMMAND: annotate
** COMMAND: blame
** COMMAND: praise
** 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
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
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







-
+




-
-
+
+



-
+







** 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 the amount of analysis:
**   -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.
**                               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
** 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 */

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);

Changes to 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
1
2
3
4
5
6
7
8
9
10
11
12

13
14
15
16
17
18
19
20
21
22
23

24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74












-
+










-
+












+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







# 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 {
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      #888888
  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 {}]
68
69
70
71
72
73
74


75
76
77



78
79
80
81
82
83
84
85














86
87
88
89
90
91
92














93
94
95









96
97
98





99
100
101




102



103
104
105







106
107
108


109
110
111
112
113
114
115
116
117
118
119
























120
121
122
123
124
125
126
127
128
129
130
131
132























133
134
135
136
137
138
139
99
100
101
102
103
104
105
106
107



108
109
110








111
112
113
114
115
116
117
118
119
120
121
122
123
124







125
126
127
128
129
130
131
132
133
134
135
136
137
138



139
140
141
142
143
144
145
146
147
148


149
150
151
152
153



154
155
156
157
158
159
160
161



162
163
164
165
166
167
168



169
170











171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194













195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224







+
+
-
-
-
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+

-
-
+
+
+
+
+
-
-
-
+
+
+
+

+
+
+
-
-
-
+
+
+
+
+
+
+
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







    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 0 ln 0 mkr 0}
  while {[set line [getLine $difftxt $N ii]] != -1} {
    set fn2 {}
  array set widths {txt 3 ln 3 mkr 1}
  
  
    if {![regexp {^=+ (.*?) =+ versus =+ (.*?) =+$} $line all fn fn2]
     && ![regexp {^=+ (.*?) =+$} $line all fn]
    } {
      continue
    }
    set errMsg ""
    set line [getLine $difftxt $N ii]
    if {[string compare -length 6 $line "<table"]
  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] {
     && ![regexp {<p[^>]*>(.+)} $line - errMsg]} {
      continue
    }
    incr nDiffs
    set idx [expr {$nDiffs > 1 ? [.txtA index end] : "1.0"}]
    .wfiles.lb insert end $fn

      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
      }
    foreach c [cols] {
      if {$nDiffs > 1} {
        $c insert end \n -
      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
      }
      if {[colType $c] eq "txt"} {
        $c insert end $fn\n fn
      COM {
        set x [lindex $line 1]
        incr n1
        incr n2
        .lnA insert end $n1\n -
        if {$fn2!=""} {set fn $fn2}
      } else {
        $c insert end \n fn
        .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
      $c insert end \n -

      if {$errMsg ne ""} continue
        .lnA insert end \n -
        .txtA insert end \n -
        .mkr insert end >\n -
        .lnB insert end $n2\n -
        .txtB insert end $x add \n -
      }
      DEL {
      while {[getLine $difftxt $N ii] ne "<pre>"} continue
      set type [colType $c]
      set str {}
        set x [lindex $line 1]
        incr n1
      while {[set line [getLine $difftxt $N ii]] ne "</pre>"} {
        set len [string length [dehtml $line]]
        if {$len > $widths($type)} {
          set widths($type) $len
        }
        append str $line\n
      }

      set re {<span class="diff([a-z]+)">([^<]*)</span>}
      # Use \r as separator since it can't appear in the diff output (it gets
      # converted to a space).
        .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
      set str [regsub -all $re $str "\r\\1\r\\2\r"]
      foreach {pre class mid} [split $str \r] {
        if {$class ne ""} {
          $c insert end [dehtml $pre] - [dehtml $mid] [list $class -]
        } else {
          $c insert end [dehtml $pre] -
        }
      }
    }

    if {$errMsg ne ""} {
      foreach c {.txtA .txtB} {$c insert end [string trim $errMsg] err}
      foreach c [cols] {$c insert end \n -}
            } 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)
328
329
330
331
332
333
334
335
336




337
338
339
340
341
342
343
413
414
415
416
417
418
419


420
421
422
423
424
425
426
427
428
429
430







-
-
+
+
+
+







text .mkr

foreach c [cols] {
  set keyPrefix [string toupper [colType $c]]_COL_
  if {[tk windowingsystem] eq "win32"} {$c config -font {courier 9}}
  $c config -bg $CFG(${keyPrefix}BG) -fg $CFG(${keyPrefix}FG) -borderwidth 0 \
    -padx $CFG(PADX) -yscroll sync-y
  $c tag config hr -spacing1 $CFG(HR_PAD_TOP) -spacing3 $CFG(HR_PAD_BTM) \
     -foreground $CFG(HR_FG)
  $c tag config 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

Changes to src/diffcmd.c.

17
18
19
20
21
22
23







24
25
26
27
28
29
30
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37







+
+
+
+
+
+
+







**
** 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"
100
101
102
103
104
105
106
107
108



























109
110
111
112
113






114
115
116
117
118
119
120



121
122
123
124











125

























126
127

128
129
130
131
132
133
134
135


136
137
138
139
140
141
142



143
144
145





















































































































































































































































































































































146

147

148

149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175

176

177
178

179
180
181
182
183
184
185

186
187
188
189
190
191
192
193

194
195
196
197
198
199
200
201
202

203
204
205
206


207
208
209


210
211
212
213
214
215
216
217
218
219
220

221
222

223
224

225
226
227
228
229


230
231
232
233
234
235
236
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144



145
146
147
148
149
150


151
152
153
154

155
156
157
158



159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196

197
198
199
200
201
202
203


204
205
206
207
208
209



210
211
212
213
214

215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
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









+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


-
-
-
+
+
+
+
+
+
-
-




-
+
+
+

-
-
-
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+






-
-
+
+




-
-
-
+
+
+


-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
-
+
-
+







-
-
-
-
-
-
-
-
-
-



-


-
-
-
-
+
-
+

-
+






-
+







-
+





-
-
-
-
+
-

-
-
+
+

-
-
+
+








-


+

-
+

-
+



-
-
+
+







  }
  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, u64 diffFlags){
  if( (diffFlags & (DIFF_SIDEBYSIDE|DIFF_BRIEF|DIFF_NUMSTAT))==0 ){
    char *z = mprintf("Index: %s\n%.66c\n", zFile, '=');
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, '=');
    fossil_print("%s", z);
    fossil_free(z);
  }
}

/*
** Print the +++/--- filename lines for a diff operation.
** Print the +++/--- filename lines or whatever filename information
** is appropriate for the output format.
**
*/
void diff_print_filenames(const char *zLeft, const char *zRight, u64 diffFlags){
  char *z = 0;
  if( diffFlags & DIFF_BRIEF ){
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 &lrarr; %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(diffFlags);
    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);
      z = mprintf("%.*c %.*s %.*c\n",
                 x/2, '=', n1, zLeft, (x+1)/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;
      z = mprintf("%.*c %.*s %.*c versus %.*c %.*s %.*c\n",
                  (w-n1+10)/2, '=', n1, zLeft, (w-n1+1)/2, '=',
                  (w-n2)/2, '=', n2, zRight, (w-n2+1)/2, '=');
      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{
    z = mprintf("--- %s\n+++ %s\n", zLeft, zRight);
    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("%s", z);
    fossil_print("]\n");
  fossil_free(z);
  }
}

/*
** 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.
**
** If fSwapDiff is 1, show the set of edits to transform zFile2 into pFile1
** instead of the opposite.
**
** 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(
  Blob *pFile1,             /* In memory content to compare from */
  int isBin1,               /* Does the 'from' content appear to be binary */
  const char *zFile2,       /* On disk content to compare to */
  const char *zName,        /* Display name of the file */
  const char *zDiffCmd,     /* Command for comparison */
  const char *zBinGlob,     /* Treat file names matching this as binary */
  int fIncludeBinary,       /* Include binary files for external diff */
  u64 diffFlags,            /* Flags to control the diff */
  DiffConfig *pCfg,         /* Flags to control the diff */
  int fSwapDiff             /* Diff from Zfile2 to Pfile1 */
  Blob *pOut            /* Blob to store diff output */
){
  if( zDiffCmd==0 ){
  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( file_size(zFile2, ExtFILE)<0 ){
    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( diffFlags & DIFF_BRIEF ){
    if( pCfg->diffFlags & DIFF_BRIEF ){
      if( blob_compare(pFile1, &file2) ){
        fossil_print("CHANGED  %s\n", zName);
      }
    }else{
      blob_zero(&out);
      if( fSwapDiff ){
        text_diff(&file2, pFile1, &out, 0, diffFlags);
      }else{
        text_diff(pFile1, &file2, &out, 0, diffFlags);
      text_diff(pFile1, &file2, &out, pCfg);
      }
      if( blob_size(&out) ){
        if( diffFlags & DIFF_NUMSTAT ){
          fossil_print("%s %s\n", blob_str(&out), zName);
        if( pCfg->diffFlags & DIFF_NUMSTAT ){
          blob_appendf(pOut, "%s %s\n", blob_str(&out), zName);
        }else{
          diff_print_filenames(zName, zName2, diffFlags);
          fossil_print("%s\n", blob_str(&out));
          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{
    int cnt = 0;
    Blob nameFile1;    /* Name of temporary file to old pFile1 content */
    Blob cmd;          /* Text of command to run */
    int useTempfile = 1;

    if( !fIncludeBinary ){
    if( (pCfg->diffFlags & DIFF_INCBINARY)==0 ){
      Blob file2;
      if( isBin1 ){
      if( looks_like_binary(pFile1) ){
        fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY);
        return;
      }
      if( zBinGlob ){
        Glob *pBinary = glob_create(zBinGlob);
      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);
      }
244
245
246
247
248
249
250
251
252
253
254








255
256



257
258
259
260
261
262
263




264
265
266


267
268
269
270
271
272
273

274
275
276
277
278
279
280
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







-
-
-
-
+
+
+
+
+
+
+
+
-
-
+
+
+



-
-
-
-
+
+
+
+

-
-
+
+






-
+







        return;
      }
      blob_reset(&file2);
    }

    /* Construct a temporary file to hold pFile1 based on the name of
    ** zFile2 */
    blob_zero(&nameFile1);
    do{
      blob_reset(&nameFile1);
      blob_appendf(&nameFile1, "%s~%d", zFile2, cnt++);
    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;
    }while( file_access(blob_str(&nameFile1),F_OK)==0 );
    blob_write_to_file(pFile1, blob_str(&nameFile1));
    }
#endif
    if( useTempfile ) blob_write_to_file(pFile1, blob_str(&nameFile1));

    /* Construct the external diff command */
    blob_zero(&cmd);
    blob_append(&cmd, zDiffCmd, -1);
    if( fSwapDiff ){
      blob_append_escaped_arg(&cmd, zFile2);
      blob_append_escaped_arg(&cmd, blob_str(&nameFile1));
    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));
      blob_append_escaped_arg(&cmd, zFile2);
      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 */
    file_delete(blob_str(&nameFile1));
    if( useTempfile ) file_delete(blob_str(&nameFile1));
    blob_reset(&nameFile1);
    blob_reset(&cmd);
  }
}

/*
** Show the difference between two files, both in memory.
288
289
290
291
292
293
294
295
296
297
298
299
300
301

302
303
304


305
306
307
308
309


310
311
312

313
314
315
316
317
318
319
320
321
322
323


324
325
326


327
328
329
330
331


332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351















352
353
354
355
356
357



358
359
360
361
362
363
364


365
366
367
368
369
370
371
372



















373
374
375
376
377
378
379
380
381
382
383
384
385
386

387
388
389
390
391
392



393
394
395
396
397
398
399
400
401


402
403
404
405
406
407
408
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







-
-

-
-
-
-
+

-
-
+
+



-
-
+
+


-
+









-
-
+
+

-
-
+
+



-
-
+
+









-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



-
-
-
+
+
+





-
-
+
+

-
-





+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+













-
+

-
-
-
-
-
+
+
+







-
-
+
+







** 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 */
  int isBin1,               /* Does the 'from' content appear to be binary */
  int isBin2,               /* Does the 'to' content appear to be binary */
  const char *zName,        /* Display name of the file */
  const char *zDiffCmd,     /* Command for comparison */
  const char *zBinGlob,     /* Treat file names matching this as binary */
  int fIncludeBinary,       /* Include binary files for external diff */
  u64 diffFlags             /* Diff flags */
  DiffConfig *pCfg          /* Diff flags */
){
  if( diffFlags & DIFF_BRIEF ) return;
  if( zDiffCmd==0 ){
  if( pCfg->diffFlags & DIFF_BRIEF ) return;
  if( pCfg->zDiffCmd==0 ){
    Blob out;      /* Diff output text */

    blob_zero(&out);
    text_diff(pFile1, pFile2, &out, 0, diffFlags);
    if( diffFlags & DIFF_NUMSTAT ){
    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, diffFlags);
      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;
    Blob prefix1;
    Blob prefix2;
    int useTempfile1 = 1;
    int useTempfile2 = 1;

    if( !fIncludeBinary ){
      if( isBin1 || isBin2 ){
    if( (pCfg->diffFlags & DIFF_INCBINARY)==0 ){
      if( looks_like_binary(pFile1) || looks_like_binary(pFile2) ){
        fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY);
        return;
      }
      if( zBinGlob ){
        Glob *pBinary = glob_create(zBinGlob);
      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 a prefix for the temporary file names */
    blob_zero(&prefix1);
    blob_zero(&prefix2);
    blob_appendf(&prefix1, "%s-v1", zName);
    blob_appendf(&prefix2, "%s-v2", zName);

    /* Construct a temporary file names */
    file_tempname(&temp1, blob_str(&prefix1));
    file_tempname(&temp2, blob_str(&prefix2));
    blob_write_to_file(pFile1, blob_str(&temp1));
    blob_write_to_file(pFile2, blob_str(&temp2));
    /* 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, zDiffCmd, -1);
    blob_append_escaped_arg(&cmd, blob_str(&temp1));
    blob_append_escaped_arg(&cmd, blob_str(&temp2));
    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 */
    file_delete(blob_str(&temp1));
    file_delete(blob_str(&temp2));
    if( useTempfile1 ) file_delete(blob_str(&temp1));
    if( useTempfile2 ) file_delete(blob_str(&temp2));

    blob_reset(&prefix1);
    blob_reset(&prefix2);
    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.
*/
static void diff_against_disk(
void diff_against_disk(
  const char *zFrom,        /* Version to difference from */
  const char *zDiffCmd,     /* Use this diff command.  NULL for built-in */
  const char *zBinGlob,     /* Treat file names matching this as binary */
  int fIncludeBinary,       /* Treat file names matching this as binary */
  u64 diffFlags,            /* Flags controlling diff output */
  FileDirList *pFileDir     /* Which files to diff */
  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 = (diffFlags & (DIFF_VERBOSE|DIFF_NUMSTAT))!=0;
  isNumStat = (diffFlags & DIFF_NUMSTAT)!=0;
  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) ){
434
435
436
437
438
439
440




441
442
443
444
445
446
447
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863







+
+
+
+







      "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);
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
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







+


+






+




+




+





-

-
-
+
+








+
+
-
-
-
+
+
+
+
-
+



















-
-
-
-
+






+
+
+






-
+
-




















-
+
-
-
-


-









-
-
+
+












-
-
-
+
-

















-
+
-
-
-




-
+







+
+
-
+









+


-
+


+

-
+
-





-
+
+


+

-
+
-










-
+


-
+
-







      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;
      int isBin;
      if( !isLink != !file_islink(zFullName) ){
        diff_print_index(zPathname, diffFlags);
        diff_print_filenames(zPathname, zPathname, diffFlags);
        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
      isBin = fIncludeBinary ? 0 : looks_like_binary(&content);
      diff_print_index(zPathname, diffFlags);
      diff_file(&content, isBin, zFullName, zPathname, zDiffCmd,
       || !file_same_as_blob(&content, zFullName)
      ){
        diff_print_index(zPathname, pCfg, pOut);
        diff_file(&content, zFullName, zPathname, pCfg, pOut);
                zBinGlob, fIncludeBinary, diffFlags, 0);
      }
      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(
  const char *zDiffCmd,     /* Use this diff command.  NULL for built-in */
  const char *zBinGlob,     /* Treat file names matching this as binary */
  int fIncludeBinary,       /* Treat file names matching this as binary */
  u64 diffFlags,            /* Flags controlling diff output */
  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, 0, zFullName, zFile,
    diff_file(&content, zFullName, zFile, pCfg, 0);
              zDiffCmd, zBinGlob, fIncludeBinary, diffFlags, 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,
  const char *zDiffCmd,
  DiffConfig *pCfg
  const char *zBinGlob,
  int fIncludeBinary,
  u64 diffFlags
){
  Blob f1, f2;
  int isBin1, isBin2;
  int rid;
  const char *zName;
  if( pFrom ){
    zName = pFrom->zName;
  }else if( pTo ){
    zName = pTo->zName;
  }else{
    zName = DIFF_NO_NAME;
  }
  if( diffFlags & DIFF_BRIEF ) return;
  diff_print_index(zName, diffFlags);
  if( 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);
  }
  isBin1 = fIncludeBinary ? 0 : looks_like_binary(&f1);
  isBin2 = fIncludeBinary ? 0 : looks_like_binary(&f2);
  diff_file_mem(&f1, &f2, isBin1, isBin2, zName, zDiffCmd,
  diff_file_mem(&f1, &f2, zName, pCfg);
                zBinGlob, fIncludeBinary, diffFlags);
  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,
  const char *zDiffCmd,
  DiffConfig *pCfg,
  const char *zBinGlob,
  int fIncludeBinary,
  u64 diffFlags,
  FileDirList *pFileDir
){
  Manifest *pFrom, *pTo;
  ManifestFile *pFromFile, *pToFile;
  int asNewFlag = (diffFlags & (DIFF_VERBOSE|DIFF_NUMSTAT))!=0 ? 1 : 0;
  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( (diffFlags & DIFF_NUMSTAT)==0 ){
        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, zDiffCmd, zBinGlob,
          diff_manifest_entry(pFromFile, 0, pCfg);
                              fIncludeBinary, diffFlags);
        }
      }
      pFromFile = manifest_file_next(pFrom,0);
    }else if( cmp>0 ){
      if( file_dir_match(pFileDir, pToFile->zName) ){
        if( (diffFlags & DIFF_NUMSTAT)==0 ){
        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, zDiffCmd, zBinGlob,
          diff_manifest_entry(0, pToFile, pCfg);
                              fIncludeBinary, diffFlags);
        }
      }
      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( diffFlags & DIFF_BRIEF ){
        if( pCfg->diffFlags & DIFF_BRIEF ){
          fossil_print("CHANGED %s\n", pFromFile->zName);
        }else{
          diff_manifest_entry(pFromFile, pToFile, zDiffCmd, zBinGlob,
          diff_manifest_entry(pFromFile, pToFile, pCfg);
                              fIncludeBinary, diffFlags);
        }
      }
      pFromFile = manifest_file_next(pFrom,0);
      pToFile = manifest_file_next(pTo,0);
    }
  }
  manifest_destroy(pFrom);
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
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







+
+

-
+

+




+
+
+
+














+
-
+


-
+
















-
+







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

















-
-
+
+


+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
-
-
-
-
+
+
-
-
-
-
+
+
+

-
-
-
-
-
+
+
+
+
+
-
-





-
-
+
+
+
+
+


-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+



-
-




-
-
-

-

+

-
+




-


-
+


-
-
-
-
-
-














+
+
-
-
-
-
+
+
+
+
+
+
+



-
-
+
-
-
-
-







** (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 --html -y -i -v",
  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, "}\n%s", builtin_file("diff.tcl", 0));
  blob_appendf(&script, "%s", builtin_file("diff.tcl", 0));
  if( zTempFile ){
    blob_write_to_file(&script, zTempFile);
    fossil_print("To see diff, run: tclsh \"%s\"\n", 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("tclsh \"%s\"", zTempFile);
    zCmd = mprintf("%$ %$", zTclsh, zTempFile);
    fossil_system(zCmd);
    file_delete(zTempFile);
    fossil_free(zCmd);
  }
  blob_reset(&script);
}

/*
** Returns non-zero if files that may be binary should be used with external
** diff programs.
*/
int diff_include_binary_files(void){
  const char* zArgIncludeBinary = find_option("diff-binary", 0, 1);

  /* Command line argument have priority on settings */
  if( zArgIncludeBinary ){
    return is_truth(zArgIncludeBinary);
  }else{
    return db_get_boolean("diff-binary", 1);
  }
}

/*
** 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 the unsaved changes
** 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
**
** If the "--from VERSION" or "-r VERSION" option is used it specifies
** the source check-in for the diff operation.  If not specified, the
** source check-in is the base check-in for the current check-out.
** 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"
**
** If the "--to VERSION" option appears, it specifies the check-in from
** which the second version of the file or files is taken.  If there is
** no "--to" option then the (possibly edited) files in the current check-out
** 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)
** are used.
**
** The "--checkin VERSION" option shows the changes made by
** check-in VERSION relative to its primary parent.
** 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 the 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 "-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 "-N" or "--new-file" option causes the complete text of added or
** deleted files to be displayed.
**
** 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 external diff program.
** This option overrides the "binary-glob" setting.
** 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
**   --checkin VERSION          Show diff of all changes in VERSION
**   --command PROG             External diff program - overrides "diff-command"
**   --context|-c N             Use N lines of context
**   --diff-binary BOOL         Include binary files when using external commands
**   --exec-abs-paths           Force absolute path names with external commands.
**   --exec-rel-paths           Force relative path names with external commands.
**   --from|-r VERSION          Select VERSION as source for the diff
**   --internal|-i              Use internal diff logic
**   --new-file|-N              Show complete text of added and deleted files
**   --numstat                  Show only the number of lines delete and added
**   --side-by-side|-y          Side-by-side diff
**   --strip-trailing-cr        Strip trailing CR
**   --tk                       Launch a Tcl/Tk GUI for display
**   --to VERSION               Select VERSION as target for the diff
**   --undo                     Diff against the "undo" buffer
**   --unified                  Unified diff
**   -v|--verbose               Output complete text of added or deleted files
**   --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
**   -w|--ignore-all-space      Ignore white space when comparing lines
**   -W|--width <num>           Width of lines in side-by-side diff
**   -Z|--ignore-trailing-space Ignore changes to end-of-line whitespace
**   -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 */
  int isInternDiff;          /* True for internal diff */
  int verboseFlag;           /* True if -v or --verbose flag is used */
  const char *zFrom;         /* Source version number */
  const char *zTo;           /* Target version number */
  const char *zCheckin;      /* Check-in version number */
  const char *zBranch;       /* Branch to diff */
  const char *zDiffCmd = 0;  /* External diff command. NULL for internal diff */
  const char *zBinGlob = 0;  /* Treat file names matching this as binary */
  int fIncludeBinary = 0;    /* Include binary files for external diff */
  int againstUndo = 0;       /* Diff against files in the undo buffer */
  u64 diffFlags = 0;         /* Flags to control the DIFF */
  FileDirList *pFileDir = 0; /* Restrict the diff to these files */
  DiffConfig DCfg;           /* Diff configuration object */

  if( find_option("tk",0,0)!=0 ){
  if( find_option("tk",0,0)!=0 || has_option("tclsh") ){
    diff_tk("diff", 2);
    return;
  }
  isGDiff = g.argv[1][0]=='g';
  isInternDiff = find_option("internal","i",0)!=0;
  zFrom = find_option("from", "r", 1);
  zTo = find_option("to", 0, 1);
  zCheckin = find_option("checkin", 0, 1);
  zCheckin = find_option("checkin", "ci", 1);
  zBranch = find_option("branch", 0, 1);
  againstUndo = find_option("undo",0,0)!=0;
  diffFlags = diff_options();
  verboseFlag = find_option("verbose","v",0)!=0;
  if( !verboseFlag ){
    verboseFlag = find_option("new-file","N",0)!=0; /* deprecated */
  }
  if( verboseFlag ) diffFlags |= DIFF_VERBOSE;
  if( 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");
    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);
  }
  if( !isInternDiff ){
    zDiffCmd = find_option("command", 0, 1);
  diff_options(&DCfg, isGDiff, 0);
    if( zDiffCmd==0 ) zDiffCmd = diff_command_external(isGDiff);
  }
  zBinGlob = diff_get_binary_glob();
  fIncludeBinary = diff_include_binary_files();
  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));
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
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







+





-
+
-

-
+
-

-
+
-














+
+
+
+
+











+
+




+

+
-
+

      "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(zDiffCmd, zBinGlob, fIncludeBinary,
    diff_against_undo(&DCfg, pFileDir);
                      diffFlags, pFileDir);
  }else if( zTo==0 ){
    diff_against_disk(zFrom, zDiffCmd, zBinGlob, fIncludeBinary,
    diff_against_disk(zFrom, &DCfg, pFileDir, 0);
                      diffFlags, pFileDir);
  }else{
    diff_two_versions(zFrom, zTo, zDiffCmd, zBinGlob, fIncludeBinary,
    diff_two_versions(zFrom, zTo, &DCfg, pFileDir);
                      diffFlags, 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, 0, 0, 0, DIFF_VERBOSE, 0);
  diff_two_versions(zFrom, zTo, &DCfg, 0);
}

Changes to src/dispatch.c.

29
30
31
32
33
34
35

36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51















52
53
54
55
56
57
58
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43









44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65







+







-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







** 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_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 */
71
72
73
74
75
76
77

78
79
80
81
82
83
84
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92







+







**
** 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
120
121
122
123
124
125
126

127


128
129
130
131
132
133
134
128
129
130
131
132
133
134
135

136
137
138
139
140
141
142
143
144







+
-
+
+







     * 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 */
            return 2;  /* Confirmed ambiguous prefix */
          }
        }
      }
    }
    if( mid>=0 ){
      *ppCmd = &aCommand[mid];
      return 0;  /* Prefix match */
    }
193
194
195
196
197
198
199


200
201
202
203
204
205
206
207

208
209





210
211
212

213
214
215
216
217


















































































































218
219
220
221
222
223















224
225

226
227
228
229







































































230
231
232
233
234



































































235




236
237





238













239
240
241
242




























































































243
244
245
246
247
248
249
250
251
252
253
254

255






256
257

258
259
260
261
262
263
264

265
266
267
268
269
270


271
272
273



274
275
276
277
278
279
280
281
282
283
284
285

286
287
288
289
290









291
292

293
294
295
296
297
298
299
300
301
302
303
304
305





















































































306


































307
308


309

310
311
312
313
314
315
316
317










318
319
320
321
322

323
324
325
326

327
328
329
330


331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346












347
348

349
350

351
352
353
354
355


356
357
358
359
360
361





362
363
364
365
366
367


368
369


























370
371
372
373
374
375
376
377
378

379
380
381
382
383
384
385
386
387
388
389
390
391
392
393

394
395
396
397
398
399
400
401
402
403
404
405
406
407
408

409
410
411
412
413
414
415
416
417
418

419
420
421
422
423
424
425
426
427





428


429


































430
431
432
433
434
435
436
































437
438
439
440
441
442
443
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218

219
220

221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372




373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
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







+
+







-
+

-
+
+
+
+
+



+





+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+






+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
-
-
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+












+
-
+
+
+
+
+
+

-
+
-
-


-


+





-
+
+



+
+
+




-
-
-
-
-
-
-
-
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+

-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
-
+








+
+
+
+
+
+
+
+
+
+





+




+


-
-
+
+
















+
+
+
+
+
+
+
+
+
+
+
+

-
+

-
+




-
+
+






+
+
+
+
+






+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+









+















+















+









-
+









+
+
+
+
+

+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







      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.
** match the prefix zPrefix and the eType CMDFLAGS_ bits.
*/
void dispatch_matching_names(const char *zPrefix, Blob *pList){
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, "&lt;", 4);
      z += i+1;
      n -= i+1;
      i = 0;
    }else if( c=='>' ){
      if( i ) blob_append(pOut, z, i);
      blob_append(pOut, "&gt;", 4);
      z += i+1;
      n -= i+1;
      i = 0;
    }else if( c=='&' ){
      if( i ) blob_append(pOut, z, i);
      blob_append(pOut, "&amp;", 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 *s;
  char *d;
  char *z;

  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;
    }
  /* Transform "%fossil" into just "fossil" */
  z = s = d = mprintf("%s", zHelp);
  while( *s ){
    if( *s=='%' && strncmp(s, "%fossil", 7)==0 ){
      s++;
    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;
      *d++ = *s++;
    }
    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;
  *d = 0;

  blob_appendf(pHtml, "<pre>\n%h\n</pre>\n", z);
  fossil_free(z);
      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.
**    -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.
**    -w|--www          Show WWW pages
**    -s|--settings     Show settings.
**    -h|--html         Transform output to HTML.
*/
void test_all_help_cmd(void){
  int i;
  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;
    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;
  }
  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_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 ){
  display_all_help(mask, useHtml, rawOut);
    fossil_print("-->\n");
    fossil_print("<!-- start_all_help -->\n");
  }else{
    fossil_print("---\n");
  }
}

/*
** 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 & mask)==0 ) continue;
    if( (aCommand[i].eCmdFlags & eFlg)!=0 ) n++;
    fossil_print("# %s\n", aCommand[i].zName);
    if( useHtml ){
      Blob html;
      blob_zero(&html);
      help_to_html(aCommand[i].zHelp, &html);
      fossil_print("%s\n\n", blob_str(&html));
      blob_reset(&html);
    }else{
      fossil_print("%s\n\n", aCommand[i].zHelp);
    }
  }
  if( useHtml ){
    fossil_print("<!-- end_all_help -->\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("---\n");
  }
      fossil_print("   %s\n", az[j]);
    }
  version_cmd();
  }
}

/*
** 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", "%s/help", g.zTop);
    rc = dispatch_name_search(zCmd, CMDFLAG_ANY, &pCmd);
    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{
        @ <blockquote>
        @ <div class="helpPage">
        help_to_html(pCmd->zHelp, cgi_output_blob());
        @ </blockquote>
        @ </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></li>
    }
      @ <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_footer();
  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;
    @ <h2>%s(aCommand[i].zName):</h2>
    @ <blockquote>
    help_to_html(aCommand[i].zHelp, cgi_output_blob());
    @ </blockquote>
  }
  style_footer();
    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;
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





















































































































































































































































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







-
-
+
+
+
+
+
-
-
-
+
+
-
-
-
-
-
-
+
+
+
+
+
+








-
+

+

+

+
-
+



+







-
+







-
+
-


-
-
+
+

+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



+

+
+


-

-
-
-
-
+
+
-
-
-
-
+
+
+
+
-
-




-
-
+
+



-
+



-
+



-
+



+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+


+
+
+
+
+
+
+
+
+
+
+
+
+
-
+


+
+
-
+


-

-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
-
-








+
+
+
+
+
-
-
+
+


+

+
-
-
+
+
-
-
-
+
-
-
-
+
+
-
-
+
+












+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
  multi_column_list(aCmd, nCmd);
}


/*
** List of commands starting with zPrefix, or all commands if zPrefix is NULL.
*/
static void command_list(const char *zPrefix, int cmdMask){
  int i, nCmd;
static void command_list(int cmdMask, int verboseFlag, int useHtml){
  if( verboseFlag ){
    display_all_help(cmdMask, useHtml, 0);
  }else{
    int i, nCmd;
  int nPrefix = zPrefix ? strlen(zPrefix) : 0;
  const char *aCmd[MX_COMMAND];
  for(i=nCmd=0; i<MX_COMMAND; i++){
    const char *aCmd[MX_COMMAND];
    for(i=nCmd=0; i<MX_COMMAND; i++){
    const char *z = aCommand[i].zName;
    if( (aCommand[i].eCmdFlags & cmdMask)==0 ) continue;
    if( zPrefix && memcmp(zPrefix, z, nPrefix)!=0 ) continue;
    aCmd[nCmd++] = aCommand[i].zName;
  }
  multi_column_list(aCmd, nCmd);
      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 
@   --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()
@   --user|-U USER          Make the default user be USER
@   -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 TOPIC
** Usage: %fossil help [OPTIONS] [TOPIC]
**    or: %fossil TOPIC --help
**
** Display information on how to use TOPIC, which may be a command, webpage, or
** setting.  Webpage names begin with "/".  To display a list of available
** topics, use one of:
** 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:
**    %fossil help                Show common commands
**    %fossil help -a|--all       Show both common and auxiliary commands
**    %fossil help -o|--options   Show command-line options common to all cmds
**    %fossil help -s|--setting   Show setting names
**    %fossil help -t|--test      Show test commands only
**    %fossil help -x|--aux       Show auxiliary commands only
**    %fossil help -w|--www       Show list of webpages
**
**    -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 char *zCmdOrPagePlural;
  const CmdOrPage *pCmd = 0;
  if( g.argc<3 ){
    z = g.argv[0];
    fossil_print(
      "Usage: %s help TOPIC\n"
  int useHtml = 0;
  const char *zTopic;
      "Common commands:  (use \"%s help help\" for more options)\n",
      z, z);
    command_list(0, CMDFLAG_1ST_TIER);
    version_cmd();
  Blob txt;
  verboseFlag = find_option("verbose","v",0)!=0;
  commandsFlag = find_option("commands","c",0)!=0;
  useHtml = find_option("html","h",0)!=0;
    return;
  }
  if( find_option("options","o",0) ){
    fossil_print("%s", zOptions);
    return;
  }
  if( find_option("all","a",0) ){
    command_list(0, CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER);
  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(0, CMDFLAG_WEBPAGE);
    command_list(CMDFLAG_WEBPAGE, verboseFlag, useHtml);
    return;
  }
  else if( find_option("aux","x",0) ){
    command_list(0, CMDFLAG_2ND_TIER);
    command_list(CMDFLAG_2ND_TIER, verboseFlag, useHtml);
    return;
  }
  else if( find_option("test","t",0) ){
    command_list(0, CMDFLAG_TEST);
    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 |
    command_list(0, CMDFLAG_SETTING);
                     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 = ('/' == *g.argv[2]) ? 1 : 0;
  isPage = ('/' == zTopic[0]) ? 1 : 0;
  if(isPage){
    zCmdOrPage = "page";
  }else if( commandsFlag ){
    mask = CMDFLAG_COMMAND;
    zCmdOrPagePlural = "pages";
    zCmdOrPage = "command";
  }else{
    zCmdOrPage = "command or setting";
    zCmdOrPagePlural = "commands and settings";
  }
  rc = dispatch_name_search(g.argv[2], CMDFLAG_ANY|CMDFLAG_PREFIX, &pCmd);
  if( rc==1 ){
    fossil_print("unknown %s: %s\nConsider using:\n", zCmdOrPage, g.argv[2]);
    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");
  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_exit(1);
  }else if( rc==2 ){
    fossil_print("ambiguous %s prefix: %s\nMatching %s:\n",
    fossil_print("   fossil help -o        ;# show global options\n");
                 zCmdOrPage, g.argv[2], zCmdOrPagePlural);
    command_list(g.argv[2], 0xff);
    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\n\n",
         pCmd->zName,
    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);
  while( *z ){
    if( *z=='%' && strncmp(z, "%fossil", 7)==0 ){
  if( useHtml ){
    help_to_html(z, &txt);
      fossil_print("%s", g.argv[0]);
      z += 7;
    }else{
  }else{
      putchar(*z);
      z++;
    }
    help_to_text(z, &txt);
  }
  }
  putchar('\n');
  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
7

8
9
10
11
12
13
14
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/
36
37
38
39
40
41
42
43
44





45
46
47
48
49
50
51






52
53
54
55
56
57
58
59

60
61





62

63
64
65
66
67
68
69

70
71
72
73
74
75
76
36
37
38
39
40
41
42


43
44
45
46
47
48
49





50
51
52
53
54
55
56
57
58
59
60
61
62
63
64


65
66
67
68
69
70
71
72
73
74
75
76
77

78
79
80
81
82
83
84
85







-
-
+
+
+
+
+


-
-
-
-
-
+
+
+
+
+
+








+
-
-
+
+
+
+
+

+






-
+







  int i;
  int n;
  const unsigned char *x;

  /* A table of mimetypes based on file content prefixes
  */
  static const struct {
    const char *zPrefix;       /* The file prefix */
    int size;                  /* Length of the prefix */
    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, "image/gif"  },
    { "GIF89a",                  6, "image/gif"  },
    { "\211PNG\r\n\032\n",       8, "image/png"  },
    { "\377\332\377",            3, "image/jpeg" },
    { "\377\330\377",            3, "image/jpeg" },
    { "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( n>=aMime[i].size && memcmp(x, aMime[i].zPrefix, aMime[i].size)==0 ){
      return aMime[i].zMimetype;
    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 mime-type
** 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"            },
145
146
147
148
149
150
151
152






153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168

169
170
171
172
173
174
175
154
155
156
157
158
159
160

161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190







-
+
+
+
+
+
+
















+







  { "ips",        3, "application/x-ipscript"            },
  { "ipx",        3, "application/x-ipix"                },
  { "jad",        3, "text/vnd.sun.j2me.app-descriptor"  },
  { "jar",        3, "application/java-archive"          },
  { "jpe",        3, "image/jpeg"                        },
  { "jpeg",       4, "image/jpeg"                        },
  { "jpg",        3, "image/jpeg"                        },
  { "js",         2, "application/x-javascript"          },
  { "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"                        },
188
189
190
191
192
193
194

195
196
197
198
199
200
201
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217







+







  { "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"},
265
266
267
268
269
270
271

272
273

274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298







+


+







  { "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"                   },
303
304
305
306
307
308
309















































































































310

311
312
313
314
315
316
317
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438

439
440
441
442
443
444
445
446







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+







      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 mime-type of a document based on its name.
** 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];
328
329
330
331
332
333
334
335

336
337




338
339
340
341
342
343
344
457
458
459
460
461
462
463

464
465
466
467
468
469
470
471
472
473
474
475
476
477







-
+


+
+
+
+







#endif

  z = zName;
  for(i=0; zName[i]; i++){
    if( zName[i]=='.' ) z = &zName[i+1];
  }
  len = strlen(z);
  if( len<sizeof(zSuffix)-1 ){
  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;
362
363
364
365
366
367
368

369
370
371
372
373
374
375
376
377
378
379
380
381



382
383
384
385
386






































387
388
389
390
391
392
393





394

395
396
397
398

399
400
401
402
403
404
405
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







+













+
+
+



-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







+
+
+
+
+
-
+



-
+







** 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 table to guess at the appropriate mimetype
  @ for each document.</p>
  @ 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>%h(aMime[i].zSuffix)<td>%h(aMime[i].zMimetype)</tr>
    @ <tr><td>%s(zFlag)%h(aMime[i].zSuffix)<td>%h(aMime[i].zMimetype)</tr>
  }
  @ </tbody></table>
  style_table_sorter();
  style_footer();
  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
420
421
422
423
424
425
426
427

428
429
430
431
432
433
434
598
599
600
601
602
603
604

605
606
607
608
609
610
611
612







-
+







  int seenTitle = 0;

  while( fossil_isspace(zIn[0]) ) zIn++;
  if( fossil_strnicmp(zIn,"<div",4)!=0 ) return 0;
  zIn += 4;
  while( zIn[0] ){
    if( fossil_isspace(zIn[0]) ) zIn++;
    if( zIn[0]=='>' ) return 0;
    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++;
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
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







+
+
+
-
+
+
+
+
+













-
+

+
+
+
+
+
+
+







    }
    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 */
      blob_append(pTitle, zValue, nValue);
      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 = db_is_writeable("repository");
  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"
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
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





-
-
+
+
+

-
-
+
+
+
+
+
+

-
+








-
-
+
+
+




+
+
+
+
+
+
+
+
+
+
+
+




+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+








-
+






+
+
+




-
+
+



-
+
+
+
+
+
+







  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="$ROOT/..."
**       action="$ROOT/..."
**       href=".../doc/$CURRENT/..."
**
** Convert $ROOT to the root URI of the repository.  Allow ' in place of "
** and any case for href or action.
** 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.
*/
static void convert_href_and_output(Blob *pIn){
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-7]," href=", 6)==0 ||
           fossil_strnicmp(&z[i-9]," action=", 8)==0)
     && ((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, or the name of a branch (meaning the most recent
** 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.
** 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.
** 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
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
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







+














+
+
+
+
+
+
+
+
+
+




+







#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++; }
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
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







+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+







        }
      }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);
        if( unversioned_content(zName, &filebody)==0 ){
          rid = 1;
          zDfltTitle = zName;
        }
          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 ){
      /* Read from the local checkout */
    }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 */
      }
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
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







-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

















-
+







  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));
  }
  if( fossil_strcmp(zMime, "text/x-fossil-wiki")==0 ){
    Blob tail;
    style_adunit_config(ADUNIT_RIGHT_OK);
    if( wiki_find_title(&filebody, &title, &tail) ){
      style_header("%s", blob_str(&title));
      wiki_convert(&tail, 0, WIKI_BUTTONS);
    }else{
      style_header("%s", zDfltTitle);
      wiki_convert(&filebody, 0, WIKI_BUTTONS);
    }
    style_footer();
  cgi_check_for_malice();
  }else if( fossil_strcmp(zMime, "text/x-markdown")==0 ){
    Blob tail = BLOB_INITIALIZER;
    markdown_to_html(&filebody, &title, &tail);
    if( blob_size(&title)>0 ){
      style_header("%s", blob_str(&title));
    }else{
      style_header("%s", nMiss>=count(azSuffix)?
                        "Not Found" : zDfltTitle);
    }
    convert_href_and_output(&tail);
    style_footer();
  }else if( fossil_strcmp(zMime, "text/plain")==0 ){
    style_header("%s", zDfltTitle);
  document_render(&filebody, zMime, zDfltTitle, zName);
    @ <blockquote><pre>
    @ %h(blob_str(&filebody))
    @ </pre></blockquote>
    style_footer();
  }else if( fossil_strcmp(zMime, "text/html")==0
            && doc_is_embedded_html(&filebody, &title) ){
    if( blob_size(&title)==0 ) blob_append(&title,zName,-1);
    style_header("%s", blob_str(&title));
    convert_href_and_output(&filebody);
    style_footer();
#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(&filebody, &title, &tail) ){
        style_header("%s", blob_str(&title));
        Th_Render(blob_str(&tail));
        blob_reset(&tail);
      }else{
        style_header("%h", zName);
        Th_Render(blob_str(&filebody));
      }
    }else{
      Th_Render(blob_str(&filebody));
    }
    if( !raw ){
      style_footer();
    }
#endif
  }else{
    cgi_set_content_type(zMime);
    cgi_set_content(&filebody);
  }
  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_footer();
  style_finish_page();
  return;
}

/*
** The default logo.
*/
static const unsigned char aLogo[] = {
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
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+













+

-
+
+

-
+

  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");
  style_header("Document Search%s", isSearch ? " Results" : "");
  cgi_check_for_malice();
  search_screen(SRCH_DOC, 0);
  style_footer();
  style_finish_page();
}

Changes to src/encode.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








52
53
54
55
56
57
58
24
25
26
27
28
29
30


31
32
33
34

35
36
37









38
39
40
41
42
43
44
45
46
47
48
49
50


51
52
53
54
55
56
57
58
59
60
61
62
63
64
65







-
-
+
+


-
+
+

-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+




-
-
+
+
+
+
+
+
+
+







** Make the given string safe for HTML by converting every "<" into "&lt;",
** every ">" into "&gt;" and every "&" into "&amp;".  Return a pointer
** to a new string obtained from malloc().
**
** We also encode " as &quot; and ' as &#39; so they can appear as an argument
** to markup.
*/
char *htmlize(const char *zIn, int n){
  int c;
char *htmlize(const char *z, int n){
  unsigned char c;
  int i = 0;
  int count = 0;
  char *zOut;
  unsigned char *zOut;
  const unsigned char *zIn = (const unsigned char*)z;

  if( n<0 ) n = strlen(zIn);
  while( i<n && (c = zIn[i])!=0 ){
    switch( c ){
      case '<':   count += 4;       break;
      case '>':   count += 4;       break;
      case '&':   count += 5;       break;
      case '"':   count += 6;       break;
      case '\'':  count += 5;       break;
      default:    count++;          break;
  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+1 );
  while( n-->0 && (c = *zIn)!=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;
84
85
86
87
88
89
90
91
92
93
94

95
96
97
98
99
100
101
91
92
93
94
95
96
97

98
99

100
101
102
103
104
105
106
107







-


-
+







        zOut[i++] = '9';
        zOut[i++] = ';';
        break;
      default:
        zOut[i++] = c;
        break;
    }
    zIn++;
  }
  zOut[i] = 0;
  return zOut;
  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;
123
124
125
126
127
128
129





130
131
132
133
134
135
136
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147







+
+
+
+
+







        blob_append(p, "&quot;", 6);
        j = i+1;
        break;
      case '\'':
        if( j<i ) blob_append(p, zIn+j, i-j);
        blob_append(p, "&#39;", 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);
}


197
198
199
200
201
202
203






























204
205
206
207
208
209
210
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







** 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';
312
313
314
315
316
317
318
319
320



321
322
323
324
325
326
327
353
354
355
356
357
358
359


360
361
362
363
364
365
366
367
368
369







-
-
+
+
+







}

/*
** Decode a fossilized string in-place.
*/
void defossilize(char *z){
  int i, j, c;
  for(i=0; (c=z[i])!=0 && c!='\\'; i++){}
  if( c==0 ) return;
  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;
374
375
376
377
378
379
380
381









382
383


384
385
386
387
388
389
390

391
392
393

394
395
396
397
398
399
400
401
402

403

404
405



406




407
408


409
410
411

412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428



429




430
431
432
433
434
435
436
416
417
418
419
420
421
422

423
424
425
426
427
428
429
430
431
432

433
434
435
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







-
+
+
+
+
+
+
+
+
+

-
+
+






-
+


-
+









+
-
+
-
-
+
+
+

+
+
+
+
-
-
+
+


-
+

















+
+
+

+
+
+
+







        || (c&0xFFFFF800)==0xD800
        || (c&0xFFFFFFFE)==0xFFFE ){  c = 0xFFFD; }
  }
  return c;
}

/*
** Encode a UTF8 string for JSON.  All special characters are escaped.
** 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).
*/
void blob_append_json_string(Blob *pBlob, const char *zStr){
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 = fossil_utf8_read(&z))!=0 ){
  while( (c = *(z++))!=0 ){
    if( c=='\\' || c=='"' ){
      n += 2;
    }else if( c<' ' || c>=0x7f ){
    }else if( c<' ' ){
      if( c=='\n' || c=='\r' ){
        n += 2;
      }else{
        n += 6;
      }
    }else{
      n++;
    }
  }
  if(fAddQuotes){
  i = blob_size(pBlob);
    n += 2;
  blob_resize(pBlob, i+n);
  zOut = blob_buffer(pBlob);
  }
  zOut = fossil_malloc(n+1);
  if( zOut==0 ) return 0;
  z = (const unsigned char*)zStr;
  i = 0;
  if(fAddQuotes){
    zOut[i++] = '"';
  }
  while( (c = fossil_utf8_read(&z))!=0 ){
    if( c=='\\' ){
  while( (c = *(z++))!=0 ){
    if( c=='\\' || c=='"' ){
      zOut[i++] = '\\';
      zOut[i++] = c;
    }else if( c<' ' || c>=0x7f ){
    }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+/";
641
642
643
644
645
646
647



648
649
650
651
652
653
654
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721







+
+
+







/*
** 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;
}

Changes to src/etag.c.

22
23
24
25
26
27
28


29
30
31
32
33
34
35
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37







+
+







** 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
58
59
60
61
62
63
64


65
66
67
68
69


















70
71
72
73
74
75
76
77
78
79




80

81
82
83

84
85
86
87




88
89
90
91
92

93

94
95
96
97
98
99

100

101
102
103

104
105
106
107
108
109
110
111
112
113
114
115

















116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
















134
135
136
137
138
139
140
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96

97
98
99
100
101
102
103
104

105
106
107

108




109
110
111
112
113
114
115
116
117
118

119
120
121
122
123
124
125
126

127
128
129

130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200







+
+





+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





-




+
+
+
+
-
+


-
+
-
-
-
-
+
+
+
+





+
-
+






+
-
+


-
+












+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







/*
** 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){
  sqlite3_int64 mtime;
  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 = 86400;
  iMaxAge = 10 * 365 * 24 * 60 * 60;
  md5sum_init();

  /* Always include the mtime of the executable as part of the hash */
  /* Always include the executable ID as part of the hash */
  mtime = file_mtime(g.nameOfExe, ExtFILE);
  sqlite3_snprintf(sizeof(zBuf),zBuf,"mtime: %lld\n", mtime);
  md5sum_step_text(zBuf, -1);
  
  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;
  }
  }else if( eFlags & ETAG_DATA ){
  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;
  }
  }else if( eFlags & ETAG_CONFIG ){
  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("data: ", -1);
    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.
148
149
150
151
152
153
154
155

156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173

174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189

190
191
192
193
194
195
196
208
209
210
211
212
213
214

215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232

233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248

249
250
251
252
253
254
255
256







-
+

















-
+















-
+








  /* 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 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 zETag;
  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:
206
207
208
209
210
211
212








266
267
268
269
270
271
272
273
274
275
276
277
278
279
280







+
+
+
+
+
+
+
+
  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;
}

Changes to src/event.c.

61
62
63
64
65
66
67
68

69
70
71
72
73
74
75
61
62
63
64
65
66
67

68
69
70
71
72
73
74
75







-
+







**  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;             /* UUID corresponding to rid */
  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 */
108
109
110
111
112
113
114

115
116
117
118

119
120
121
122
123
124
125
126
127
128
129
130
131
132

133
134
135
136
137
138
139
108
109
110
111
112
113
114
115
116
117
118

119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141







+



-
+














+







      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_footer();
    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);
209
210
211
212
213
214
215
216

217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233



234
235
236
237
238
239
240
211
212
213
214
215
216
217

218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233


234
235
236
237
238
239
240
241
242
243







-
+















-
-
+
+
+







    }else{
      @ <div>
    }
    blob_init(&comment, pTNote->zComment, -1);
    wiki_convert(&comment, 0, WIKI_INLINE);
    blob_reset(&comment);
    @ </div>
    @ </blockquote><hr />
    @ </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>");
  style_footer();
  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.
**
267
268
269
270
271
272
273



274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284



285
286
287
288
289
290
291







+
+
+





-
-
-







  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( zMimetype && zMimetype[0] ){
    blob_appendf(&event, "N %s\n", zMimetype);
  }
  if( zClr && zClr[0] ){
    blob_appendf(&event, "T +bgcolor * %F\n", zClr);
  }
  if( zTags && zTags[0] ){
    Blob tags, one;
    int i, j;
    Stmt q;
324
325
326
327
328
329
330
331

332
333
334
335
336
337
338
327
328
329
330
331
332
333

334
335
336
337
338
339
340
341







-
+







    blob_appendf(&event, "U %F\n", login_name());
  }
  blob_appendf(&event, "W %d\n%s\n", strlen(zBody), zBody);
  md5sum_blob(&event, &cksum);
  blob_appendf(&event, "Z %b\n", &cksum);
  blob_reset(&cksum);
  nrid = content_put(&event);
  db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid);
  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);
355
356
357
358
359
360
361




362
363
364
365
366
367
368
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375







+
+
+
+







**    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;
408
409
410
411
412
413
414

415
416
417
418









419
420
421
422
423
424
425
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442







+




+
+
+
+
+
+
+
+
+







  /* 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";
449
450
451
452
453
454
455
456

457
458
459
460
461
462
463
464

465
466
467
468
469
470
471
466
467
468
469
470
471
472

473

474
475
476
477
478
479

480
481
482
483
484
485
486
487







-
+
-






-
+







        "   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) ){
  if( P("submit")!=0 && (zBody!=0 && zComment!=0) && cgi_csrf_safe(2) ){
    login_verify_csrf_secret();
    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_footer();
      style_finish_page();
      return;
    }
    cgi_redirectf("%R/technote?name=%T", zId);
  }
  if( P("cancel")!=0 ){
    cgi_redirectf("%R/technote?name=%T", zId);
    return;
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
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







+

-
+









-
+




-
+










-
+






-
+


-
+
+

-
+









-
-
+
+

-
+



-
+







    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 />
    @ </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)" />
  @ <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)" />
  @   <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) />
  @ <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)" />
  @   <input type="text" name="g" size="40" value="%h(zTags)">
  @ </td></tr>

  @ <tr><th align="right" valign="top">Markup Style:</th>
  @ <tr><th align="right" valign="top">\
  @ %z(href("%R/markup_help"))Markup Style</a>:</th>
  @ <td valign="top">
  mimetype_option_menu(zMimetype);
  mimetype_option_menu(zMimetype, "mimetype");
  @ </td></tr>

  @ <tr><th align="right" valign="top">Page&nbsp;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" />
  @ <input type="submit" name="cancel" value="Cancel">
  @ <input type="submit" name="preview" value="Preview">
  if( P("preview") ){
    @ <input type="submit" name="submit" value="Submit" />
    @ <input type="submit" name="submit" value="Submit">
  }
  @ </td></tr></table>
  @ </div></form>
  style_footer();
  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.

Changes to src/export.c.

26
27
28
29
30
31
32
33

34
35

36
37
38
39
40
41
42
43
44
45




46
47
48
49
50
51
52
26
27
28
29
30
31
32

33


34





35




36
37
38
39
40
41
42
43
44
45
46







-
+
-
-
+
-
-
-
-
-

-
-
-
-
+
+
+
+







*/
static struct {
  const char *zTrunkName;     /* Name of trunk branch */
} gexport;

#if INTERFACE
/*
** struct mark_t
** Each line in a git-fast-export "marK" file is an instance of
**   holds information for translating between git commits
**   and fossil commits.
** this object.
**   -git_name: This is the mark name that identifies the commit to git.
**              It will always begin with a ':'.
**   -rid: The unique object ID that identifies this commit within the
**         repository database.
**   -uuid: The SHA-1/SHA-3 of artifact corresponding to rid.
*/
struct mark_t{
  char *name;
  int rid;
  char uuid[65];
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.
*/
167
168
169
170
171
172
173
174

175
176
177
178
179
180
181
161
162
163
164
165
166
167

168
169
170
171
172
173
174
175







-
+








  printf(" %s <%s>", zName, zEmail);
  free(zName);
  free(zEmail);
  db_reset(&q);
}

#define REFREPLACEMENT	'_'
#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.
*/
208
209
210
211
212
213
214
215

216
217
218
219
220
221
222
202
203
204
205
206
207
208

209
210
211
212
213
214
215
216







-
+







      case '^':
      case ':':
      case '?':
      case '*':
      case '[':
      case '\\':
        zEncoded[w]=REFREPLACEMENT;
	break;
        break;
    }
  }
  /* Cannot begin with a . or / */
  if( zEncoded[0]=='.' || zEncoded[0] == '/' ) zEncoded[0]=REFREPLACEMENT;
  if( i>0 ){
    i--; w--;
    /* Or end with a . or / */
295
296
297
298
299
300
301

302

303
304
305
306
307
308
309






310
311
312
313
314
315
316
289
290
291
292
293
294
295
296

297







298
299
300
301
302
303
304
305
306
307
308
309
310







+
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+







      return NULL;
    }
  }
  return zMark;
}

/*
** Parse a single line of the mark file.  Store the result in the mark object.
** parse_mark()
**
**   Create a new (mark,rid,uuid) entry in the 'xmark' table given a line
**   from a marks file.  Return the cross-ref information as a struct mark_t
**   in *mark.
**   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.
** "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;
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378














379
380
381
382
383
384
385
353
354
355
356
357
358
359













360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380







-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+








  /* insert a cross-ref into the 'xmark' table */
  insert_commit_xref(mark->rid, mark->name, mark->uuid);
  return 0;
}

/*
** import_marks()
**   Import the marks specified in file 'f' into the 'xmark' table.
**   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.
** 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 */
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
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







-
-
+
+









-
+












-
-
-
-
+
+
+
+



+
+
+
+
+







      do{
        export_mark(f, rid, 'c');
      }while( (rid = bag_next(vers, rid))!=0 );
    }
  }
}

/*
** COMMAND: export
/* 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 checkout.  Or use the -R or --repository
** 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
**   --repository|-R REPOSITORY   export the given REPOSITORY
**   --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;

505
506
507
508
509
510
511
512


513
514
515
516
517
518
519
505
506
507
508
509
510
511

512
513
514
515
516
517
518
519
520







-
+
+








  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 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");
752
753
754
755
756
757
758
759

760
761
762
763
764
765
766
753
754
755
756
757
758
759

760
761
762
763
764
765
766
767







-
+







**        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 arrise when there
** 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
833
834
835
836
837
838
839































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
*/
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.

36
37
38
39
40
41
42
43

44
45
46
47
48
49
50
51
52
53
54
55



56
57
58
59


60
61
62
63




64

65
66
67
68
69
70
71
36
37
38
39
40
41
42

43
44
45
46
47
48
49
50
51




52
53
54
55



56
57
58



59
60
61
62
63
64
65
66
67
68
69
70
71







-
+








-
-
-
-
+
+
+

-
-
-
+
+

-
-
-
+
+
+
+

+







# include <sys/utime.h>
#else
# include <sys/time.h>
#endif

#if INTERFACE

/* Many APIs take a eFType argument which must be one of ExtFILE, RepoFILE,
/* 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.
**
** If RepoFILE is used and if the allow-symlinks setting is true and if
** the object is a symbolic link, then the object is treated like an ordinary
** file whose content is name of the object to which the symbolic link
** points.
**   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.
**
** If ExtFILE is used or allow-symlinks is false, then operations on a
** symbolic link are the same as operations on the object to which the
** symbolic link points.
**   SymFILE      Symbolic links always appear to be files whose name is
**                the target pathname of the symbolic link.
**
** SymFILE is like RepoFILE except that it always uses the target filename of
** a symbolic link as the content, instead of the content of the object
** that the symlink points to.  SymFILE acts as if allow-symlinks is always ON.
**   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
132
133
134
135
136
137
138

139


140
141

142
143
144
145
146
147
148
132
133
134
135
136
137
138
139

140
141
142
143
144
145
146
147
148
149
150
151







+
-
+
+


+







  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())
  if( eFType>=RepoFILE && (eFType==SymFILE || 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;
199
200
201
202
203
204
205
206

207
208
209
210
211
212
213
202
203
204
205
206
207
208

209
210
211
212
213
214
215
216







-
+








/*
** 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 : fx.fileStat.st_mode;
  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
**
238
239
240
241
242
243
244
245

246
247
248
249
250
251
252
241
242
243
244
245
246
247

248
249
250
251
252
253
254
255







-
+







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>=sizeof(zBuf) ){
    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++){
292
293
294
295
296
297
298
299

300
301
302
303
304
305
306
295
296
297
298
299
300
301

302
303
304
305
306
307
308
309







-
+







** 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, RepoFILE) ){
  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;
314
315
316
317
318
319
320
321


322
323
324
325
326
327








































































328
329
330
331
332
333
334
317
318
319
320
321
322
323

324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410







-
+
+






+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







  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 RepoFILE - that zFilename is always a file under management.
** 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){
344
345
346
347
348
349
350








































351
352
353
354
355
356
357
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







    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;
376
377
378
379
380
381
382

383
384
385
386
387
388
389
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506







+







#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;
}

/*
435
436
437
438
439
440
441



















442
443
444
445
446
447
448
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







  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,
478
479
480
481
482
483
484

485
486
487
488
489
490
491
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628







+







  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
**
508
509
510
511
512
513
514
515




516
517
518
519
520
521
522
645
646
647
648
649
650
651

652
653
654
655
656
657
658
659
660
661
662







-
+
+
+
+







** 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) ){
  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;
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
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+




















-
+







#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(zName, 0755);
    rc = mkdir(zMbcs, 0755);
#endif
    fossil_path_free(zMbcs);
    return rc;
  }
  return 0;
}

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
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







-
+



















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+












+
-
+
+
+
+









+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







  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 ){
  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);
      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 "/"
768
769
770
771
772
773
774

















775
776
777
778
779
780
781
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







        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){
801
802
803
804
805
806
807

808
809
810
811
812
813
814
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101







+







** 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] = '/';
  }
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
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







-
+
+
+
+
+
+




+

+
+
+
+
+
-
+
+













+
+
+

-
+
+
+
+
+





-
+

-
+




+







}

/*
** COMMAND: test-simplify-name
**
** Usage: %fossil test-simplify-name FILENAME...
**
** Print the simplified versions of each 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]);
      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().
*/
void file_getcwd(char *zBuf, int nBuf){
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_panic("pwd too big: max %d", nBuf-1);
      fossil_fatal("pwd too big: max %d", nBuf-1);
    }else{
      fossil_panic("cannot find current working directory; %s",
      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){
929
930
931
932
933
934
935


936
937
938
939
940
941
942
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251







+
+







** 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];
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
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+














+






+
-
+
-





+







      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, blob_buffer(&x));
  fossil_print("[%s] -> [%s]\n", zPath, zFull);
  blob_reset(&x);
  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);
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
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







+
+
+
+
+












-

-
+
-
-
+
+
+





+




-
+


-
-



+

+

+

+
+
+
+
+
+
+
+
+
+







  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.
**     --open-config                Open the configuration database first
**     --slash                      Trailing slashes, if any, are retained.
**     --reset                      Reset cached stat() info for each file.
**     --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, 0);
  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());
  fossil_print("db_allow_symlinks_by_default() = %d\n",
               db_allow_symlinks_by_default());
  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...
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
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







-
+






-
+



-
+







  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 checkout tree: %s",
        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 checkout tree, the only things we can do at this point
      ** 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 checkout tree is open, either directly or indirectly,
      ** 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;
    }
1304
1305
1306
1307
1308
1309
1310
1311

1312
1313
1314
1315
1316
1317
1318
1816
1817
1818
1819
1820
1821
1822

1823
1824
1825
1826
1827
1828
1829
1830







-
+







    return 1;
  }

  if( nFull<=nLocalRoot || xCmp(zLocalRoot, zFull, nLocalRoot) ){
    blob_reset(&localRoot);
    blob_reset(&full);
    if( errFatal ){
      fossil_fatal("file outside of checkout tree: %s", zOrigName);
      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);
    }
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
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







-
+
















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+








/*
** COMMAND: test-tree-name
**
** Test the operation of the tree name generator.
**
** Options:
**   --absolute           Return an absolute path instead of a relative one.
**   --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,
  Blob *pScheme,
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
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







-
+
+
+
+
+
+
+
+
+
+
+
+

-
+







-


















+
+
+







    blob_set(pPath, &zUri[i]);
  }else{
    blob_set(pPath, "/");
  }
}

/*
** Construct a random temporary filename into pBuf starting with zPrefix.
** 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 *zPrefix){
void file_tempname(Blob *pBuf, const char *zBasis, const char *zTag){
#if defined(_WIN32)
  const char *azDirs[] = {
     0, /* GetTempPath */
     0, /* TEMP */
     0, /* TMP */
     ".",
  };
  char *z;
#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 */
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
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


















+
+

















-
+


-
+
+
+
+
+
+






+

+
+
+
+
+






-
+







  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_panic("cannot generate a temporary filename");
    sqlite3_randomness(15, zRand);
    for(i=0; i<15; i++){
      zRand[i] = (char)zChars[ ((unsigned char)zRand[i])%(sizeof(zChars)-1) ];
    }
    zRand[15] = 0;
    blob_appendf(pBuf, "%s/%s-%s.txt", zDir, zPrefix ? zPrefix : "", zRand);
    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] BASENAME ...
** 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.
** 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]);
      file_tempname(&x, g.argv[i], zTag);
      fossil_print("%s\n", blob_str(&x));
      blob_reset(&x);
    }
  }
}


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
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





-
+







#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 *azRes[] = { "CON", "PRN", "AUX", "NUL", "COM", "LPT" };
  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))
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
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

















-
-
+
+







*/
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 subdirectores) in a given
** directory.  Return the count.  Return -1 of the object is not a
** 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);
1693
1694
1695
1696
1697
1698
1699









































































































































































































































































































































































































































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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
  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 &amp; 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 &amp; 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.

28
29
30
31
32
33
34






35
36
37
38
39
40

41
42
43
44

45
46
47



48
49
50
51
52
53
54
55









56
57

58
59

60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75

76
77
78
79
80
81
82
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

46
47
48
49

50
51


52
53
54
55







56
57
58
59
60
61
62
63
64


65
66

67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82

83
84
85
86
87
88
89
90







+
+
+
+
+
+





-
+



-
+

-
-
+
+
+

-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-
+

-
+















-
+







** 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
** 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
**   -b|--brief           Display a brief (one line / revision) summary
**   --case-sensitive B   Enable or disable case-sensitive filenames.  B is a
**                        boolean: "yes", "no", "true", "false", etc.
**   -l|--log             select log mode (the default)
**                          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<=0 means no limit.
**   --offset P           skip P changes
**   -p|--print           select print mode
**   -r|--revision R      print the given revision (or ckout, if none is given)
**                        to stdout (only in print mode)
**   -s|--status          select status mode (print a status indicator for FILE)
**   -W|--width <num>     Width of lines (default is to auto-detect). Must be
**                          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
**                        >22 or 0 (= no limit, resulting in a single line per
**                        entry).
**                          more than 22 or else 0 to indicate no limit.
**
** See also: artifact, cat, descendants, info, leaves
** See also: [[artifact]], [[cat]], [[descendants]], [[info]], [[leaves]]
*/
void finfo_cmd(void){
  db_must_be_within_tree();
  if( find_option("status","s",0) ){
    Stmt q;
    Blob line;
    Blob fname;
    int vid;

    /* We should be done with options.. */
    verify_all_options();

    if( g.argc!=3 ) usage("-s|--status FILENAME");
    vid = db_lget_int("checkout", 0);
    if( vid==0 ){
      fossil_fatal("no checkout to finfo files in");
      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());
134
135
136
137
138
139
140






















141
142
143
144
145
146
147
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







        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;
197
198
199
200
201
202
203
204

205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220

221
222
223
224
225
226
227
228
229

230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246

247
248


249
250

251
252
253
254
255

256
257

258
259
260




261
262
263
264
265



266


267
268
269
270
271
272
273
274
275
276

277


278
279








280
281
282
283
284
285
286
287
288
289
290
291
292

















293

294

295
296
297
298
299
300
301

302
303
304
305
306
307




308
309
310
311
312
313
314
315
316
317
318
319
320
321
322

323
324
325



326






327
328
329
330
331
332
333
334


335
336
337
338
339
340
341

342
343
344
345
346
347
348

349
350
351
352
353
354
355
356
357




358
359

360
361
362

363














































364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382






















383
384

385
386
387


388
389
390
391


392
393
394

395
396
397


398
399
400
401
402
403
404
405
406
407
408
409
410



411
412
413

414
415
416
417

418
419
420

421
422
423
424
425
426
427
428
429


430
431

432
433
434
435
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
227
228
229
230
231
232
233

234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249

250
251
252
253
254
255
256
257
258

259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277


278
279
280

281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305

306
307
308
309
310
311
312
313
314
315
316
317
318

319
320
321

322
323
324
325
326
327
328
329
330
331
332










333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351

352



353
354
355

356
357
358
359
360


361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386

387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408

409
410



411
412

413
414
415
416
417
418




419
420
421
422
423

424
425
426
427
428
429
430
431
432
433
434
435
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







-
+















-
+








-
+

















+
-
-
+
+

-
+





+


+



+
+
+
+





+
+
+
-
+
+










+
-
+
+

-
+
+
+
+
+
+
+
+



-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
-
+
-
-
-



-
+




-
-
+
+
+
+















+



+
+
+
-
+
+
+
+
+
+








+
+






-
+

-
-
-


-
+





-
-
-
-
+
+
+
+

-
+



+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+


-
+
+



-
+
+


-
+

-
-
+
+











-
-
+
+
+


-
+




+


-
+







-
-
+
+

-
+






-
-
+
+
+

-
+

-
+

-
-
+
+




+
+
+
+
+
+

-
-
+
+


+
+
+




+
-
+

-
+
+


-
+



-
+
+

-
+
















+
+



-
+





-
+





-
+



-
-
-
+
+
+
+














-
+











+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+
+
+
+
+
+
+
+
+

-
+






-














-
+
+



+
-
+

-
+




-
+





-
+





-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

-
+





-
+


+
+
+
+






+
+
-
+
+

-
+

-
+


-
+














-
+




-
+
+




-
+







        "   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 of %s\n", blob_str(&fname));
      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, g.comFmtFlags);
        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, g.comFmtFlags);
        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 FILE       Extract artifacts from repository FILE
**    -r VERSION                 The specific check-in containing the file
**    -R|--repository REPO     Extract artifacts from repository REPO
**    -r VERSION               The specific check-in containing the file
**
** See also: finfo
** 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_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:
** URL: /finfo?name=FILENAME
**   *  /finfo?name=FILENAME
**   *  /finfo?name=FILENAME&ci=HASH
**
** Show the change history for a single file.
** 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
**    m=HASH     Mark this particular file version
**    n=NUM      Show the first NUM changes only
**    brbg       Background color by branch name
**    ubg        Background color by user name
**    ci=UUID    Ancestors of a particular check-in
**    orig=UUID  If both ci and orig are supplied, only show those
**                 changes on a direct path from orig to ci.
**    showid     Show RID values for debugging
**    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",
** DATETIME may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in
** "YYYY-MM-DDTHH:MM:SS.SSS", "YYYYMMDDHHMM", and others.
** year-month-day form, it may be truncated, 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 finfo_page(void){
  Stmt q;
  const char *zFilename;
  const char *zFilename = PD("name","");
  char zPrevDate[20];
  const char *zA;
  const char *zB;
  int n;
  int baseCheckin;
  int origCheckin = 0;
  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("File History");
    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);
  baseCheckin = name_to_rid_www("ci");
  ridFrom = name_to_rid_www("from");
  zPrevDate[0] = 0;
  zFilename = PD("name","");
  cookie_render();
  fnid = db_int(0, "SELECT fnid FROM filename WHERE name=%Q", zFilename);
  if( fnid==0 ){
    @ No such file: %h(zFilename)
    style_footer();
    style_finish_page();
    return;
  }
  if( g.perm.Admin ){
    style_submenu_element("MLink Table", "%R/mlink?name=%t", zFilename);
  }
  if( baseCheckin ){
    if( P("orig")!=0 ){
      origCheckin = name_to_typed_rid(P("orig"),"ci");
      path_shortest_stored_in_ancestor_table(origCheckin, baseCheckin);
  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(baseCheckin);
      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"
    " datetime(min(event.mtime),toLocal()),"         /* Date of change */
    " coalesce(event.ecomment, event.comment),"      /* Check-in comment */
    " coalesce(event.euser, event.user),"            /* User who made chng */
    " mlink.pid,"                                    /* Parent file rid */
    " mlink.fid,"                                    /* File rid */
    " (SELECT uuid FROM blob WHERE rid=mlink.pid),"  /* Parent file uuid */
    " blob.uuid,"                                    /* Current file uuid */
    " (SELECT uuid FROM blob WHERE rid=mlink.mid),"  /* Check-in uuid */
    " event.bgcolor,"                                /* Background color */
    " (SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0"
                                " AND tagxref.rid=mlink.mid)," /* Branchname */
    " mlink.mid,"                                    /* check-in ID */
    " mlink.pfnid,"                                  /* Previous filename */
    " blob.size"                                     /* File size */
    "  FROM mlink, event, blob"
    " WHERE mlink.fnid=%d"
    "   AND event.objid=mlink.mid"
    "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",
    "   AND mlink.fid=blob.rid",
    TAG_BRANCH, fnid
    TAG_BRANCH
  );
  if( (zA = P("a"))!=0 ){
    blob_append_sql(&sql, " AND event.mtime>=julianday('%q')", zA);
    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<=julianday('%q')", zB);
    blob_append_sql(&sql, "  AND event.mtime<=%.16g\n",
         symbolic_name_to_mtime(zB,0));
    url_add_parameter(&url, "b", zB);
  }
  if( baseCheckin ){
  if( ridFrom ){
    blob_append_sql(&sql,
      " AND mlink.mid IN (SELECT rid FROM ancestor)"
      " GROUP BY mlink.fid"
      "  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"
      "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 /*sort*/");
  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: %h(blob_str(&sql))</p>
    @ <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( baseCheckin ){
    char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", baseCheckin);
  if( ridFrom ){
    char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", ridFrom);
    char *zLink = href("%R/info/%!S", zUuid);
    if( origCheckin ){
    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,"<a href='%R/finfo?name=%T'>%h</a>",
                 zFilename, zFilename);
    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, origCheckin ? " between " : " from ", -1);
    blob_append(&title, ridTo ? " between " : " from ", -1);
    blob_appendf(&title, "check-in %z%S</a>", zLink, zUuid);
    if( fShowId ) blob_appendf(&title, " (%d)", baseCheckin);
    if( fShowId ) blob_appendf(&title, " (%d)", ridFrom);
    fossil_free(zUuid);
    if( origCheckin ){
      zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", origCheckin);
    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 of ");
    hyperlinked_path(zFilename, &title, 0, "tree", "");
    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( baseCheckin ){
  if( ridFrom ){
    db_prepare(&qparent,
      "SELECT DISTINCT pid FROM mlink"
      "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*/"
      " ORDER BY isaux /*sort*/", mxfnid+1
    );
  }else{
    db_prepare(&qparent,
      "SELECT DISTINCT pid FROM mlink"
      "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*/"
      " 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;
    int aParent[GR_MAX_RAIL];
    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_int(&qparent, 0);
      aParent[nParent] = db_column_int64(&qparent, 0);
      nParent++;
    }
    db_reset(&qparent);
    if( zBr==0 ) zBr = "trunk";
    if( uBg ){
      zBgClr = hash_color(zUser);
      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 ? frid : fpid+1000000000,
                         nParent, aParent, zBr, zBgClr,
                         zUuid, 0);
    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/artifact/%!S",zUuid))%s(zTime)</a></td>
    @ %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) &rarr; %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_uuid(zUuid);
        hyperlink_to_version(zUuid);
        if( fShowId ){
          int srcId = delta_source_rid(frid);
          if( srcId ){
            @ (%z(href("%R/deltachain/%d",frid))%d(frid)&larr;%d(srcId)</a>)
          }else{
            @ (%z(href("%R/deltachain/%d",frid))%d(frid)</a>)
          }
        }
        @ part of check-in \
        hyperlink_to_uuid(zCkin);
        hyperlink_to_version(zCkin);
      }
    }
    @ %W(zCom)</span>
    if( (tmFlags & TIMELINE_COMPACT)!=0 ){
      @ <span class='timelineEllipsis' data-id='%d(frid)' \
      @ id='ellipsis-%d(frid)'>...</span>
      @ <span class='clutter timelineCompactDetail'
    }
    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:&nbsp;%z(href("%R/artifact/%!S",zUuid))[%S(zUuid)]</a>
      @ file:&nbsp;%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:&nbsp;%z(href("%R/deltachain/%d",frid))\
          @ id:&nbsp;%d(frid)&larr;%d(srcId)
          @ %d(frid)&larr;%d(srcId)</a>
        }else{
          @ id:&nbsp;%d(frid)
          @ id:&nbsp;%z(href("%R/deltachain/%d",frid))%d(frid)</a>
        }
      }
    }
    @ check-in:&nbsp;\
    hyperlink_to_uuid(zCkin);
    hyperlink_to_version(zCkin);
    if( fShowId ){
      @ (%d(fmid))
    }
    @ user:&nbsp;\
    hyperlink_to_user(zUser, zDate, ",");
    @ branch:&nbsp;%z(href("%R/timeline?t=%T&n=200",zBr))%h(zBr)</a>,
    @ branch:&nbsp;%z(href("%R/timeline?t=%T",zBr))%h(zBr)</a>,
    if( tmFlags & (TIMELINE_COMPACT|TIMELINE_VERBOSE) ){
      @ size:&nbsp;%d(szFile))
    }else{
      @ size:&nbsp;%d(szFile)
    }
    if( zUuid && origCheckin==0 ){
      if( nParent==0 ){
        @ <b>Added</b>
      }else if( pfnid ){
        char *zPrevName = db_text(0,"SELECT name FROM filename WHERE fnid=%d",
                                  pfnid);
        @ <b>Renamed</b> from
        @ %z(href("%R/finfo?name=%t", zPrevName))%h(zPrevName)</a>
      }
    }
    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, zFilename);
      if( zNewName ){
        @ <b>Renamed</b> to
        @ %z(href("%R/finfo?name=%t",zNewName))%h(zNewName)</a>
        fossil_free(zNewName);
      }else{
        @ <b>Deleted</b>
      }
    }
    if( g.perm.Hyperlink && zUuid ){
      const char *z = zFilename;
      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?n=all&uf=%!S",zUuid))[check-ins&nbsp;using]</a>
      @ %z(href("%R/timeline?uf=%!S",zUuid))[check-ins&nbsp;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) \
      @ <br />fid=%d(frid) pid=%d(fpid) mid=%d(fmid)
      @ pid=%d(fpid) mid=%d(fmid) fnid=%d(fnid) \
      @ pfnid=%d(pfnid) mxfnid=%d(mxfnid)
      if( nParent>0 ){
        @ parents=%d(aParent[0])
        @ parents=%lld(aParent[0])
        for(ii=1; ii<nParent; ii++){
          @ %d(aParent[ii])
          @ %lld(aParent[ii])
        }
      }
      zAncLink = href("%R/finfo?name=%T&ci=%!S&debug=1",zFilename,zCkin);
      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, 1);
    graph_finish(pGraph, 0, TIMELINE_DISJOINT);
    if( pGraph->nErr ){
      graph_free(pGraph);
      pGraph = 0;
    }else{
      @ <tr class="timelineBottom"><td></td><td></td><td></td></tr>
      @ <tr class="timelineBottom" id="btm-%d(iTableId)">\
      @ <td></td><td></td><td></td></tr>
    }
  }
  @ </table>
  timeline_output_graph_javascript(pGraph, TIMELINE_FILEDIFF, iTableId);
  style_footer();
  style_finish_page();
}

/*
** WEBPAGE: mlink
** URL: /mlink?name=FILENAME
** URL: /mlink?ci=NAME
**
676
677
678
679
680
681
682

683
684
685
686
687
688
689
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841







+







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);
775
776
777
778
779
780
781
782

783
784

785
786
787
788
789
790
791
927
928
929
930
931
932
933

934
935

936
937
938
939
940
941
942
943







-
+

-
+







       /* 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, 1);
    render_checkin_context(mid, 0, 1, 0);
    style_table_sorter();
    @ <hr />
    @ <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>
831
832
833
834
835
836
837
838

839
983
984
985
986
987
988
989

990
991







-
+

      @ </tr>
    }
    db_finalize(&q);
    @ </tbody>
    @ </table>
    @ </div>
  }
  style_footer();
  style_finish_page();
}

Changes to src/foci.c.

119
120
121
122
123
124
125
126

127

128
129
130
131
132
133
134
119
120
121
122
123
124
125

126
127
128
129
130
131
132
133
134
135







-
+

+







**
**   (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 = 10000.0;
  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{
260
261
262
263
264
265
266
267

268
269
270
271



272
273
274
275
261
262
263
264
265
266
267

268
269
270
271

272
273
274
275
276
277
278







-
+



-
+
+
+




    fociColumn,                   /* xColumn - read data */
    fociRowid,                    /* xRowid - read data */
    0,                            /* xUpdate */
    0,                            /* xBegin */
    0,                            /* xSync */
    0,                            /* xCommit */
    0,                            /* xRollback */
    0,                            /* xFindMethod */
    0,                            /* xFindFunction */
    0,                            /* xRename */
    0,                            /* xSavepoint */
    0,                            /* xRelease */
    0                             /* xRollbackTo */
    0,                            /* xRollbackTo */
    0,                            /* xShadowName */
    0                             /* xIntegrity  */
  };
  sqlite3_create_module(db, "files_of_checkin", &foci_module, 0);
  return SQLITE_OK;
}

Changes to src/forum.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
52
53
54
55
56





57
58













































































































































































































































59
60
61
62
63
64
65
66
67
68






69
70
71
72
73
74
75

76
77

78
79
80
81
82
83
84

85
86

87
88
89
90
91
92

93
94

95
96
97
98
99
100
101
102
103
104
105
106


107
108
109
110


111
112
113






114
115
116
117
118
119
120















121
122
123
124
125
126
127
128

129

130


131
132
133
134

135
136
137
138
139
140
141
142
143
144
145
146
147
148










149
150

151
152

153
154
155
156
157
158














159
160
161
162
163


164
165
166
167
168
169
170






171
172
173




174
175



176

177
178
179
180
181
182
183





184
185
186
187
188


























189
190
191
192
193

194
195




196
197
198
199
200
201
202

203
204
205




206
207
208
209



210

211
212
213
214
215
216
217
218
219


220
221


222
223
224






225
226
227
228
229
230


231





232
233




















































234
235


236
237
238
239
240
241
242
243
244
245


246
247
248
249
250
251
252
253
254
255
256
257
258







259
260









261



262

263
264
265
266
267
268
269
270
271
272

273
274

275
276









277

278

279
280
281

282
283


284
285






286
287
288













289
290











291
292
293
294
295
296
297
298


299
300
301
302
303
304
305
306
307


















































308
309

310


311
312


313
314
315
316
317
318
319
320
321










322
323
324
325



326

327

328
329

330
331
332



333
334
335
336




337
338
339

340
341
342
343
344
345
346






























347
348
349
350
351
352













353
354
355




356
357
358
359
360
361
362










363
364

365
366
367
368



369

370
371

372
373
374
375
376



















377
378
379
380
381
382
383
384















385
386



387
388
389














390
391

392
393
394
395
396
397
398
399
400



















401
402
403
404
405
406
407
408
409
410






















411
412


413
414


415
416
417
418
419
420
421










422

423
424
425
426













427
428
429
430






431
432
433
434



435
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
24
25
26
27
28
29
30

31
32
33
34


35
36



37
38
39
40
41
42
43






44
45
46
47
48
49
50
51
52
53
54
55
56
57
58




59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307





308
309
310
311
312
313
314
315
316
317

318

319
320

321
322
323
324

325
326

327
328

329
330
331
332
333
334

335
336

337
338
339
340
341
342
343
344
345
346
347


348
349
350
351


352
353


354
355
356
357
358
359
360







361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384

385
386
387
388
389
390
391

392
393
394
395
396
397









398
399
400
401
402
403
404
405
406
407
408

409
410

411
412





413
414
415
416
417
418
419
420
421
422
423
424
425
426





427
428







429
430
431
432
433
434



435
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







-
+



-
-
+
+
-
-
-
+
+

+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+






-
-
-
-
+
+
+
+
+


+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





-
-
-
-
-
+
+
+
+
+
+




-

-
+

-
+



-


-
+

-
+





-
+

-
+










-
-
+
+


-
-
+
+
-
-

+
+
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+








+
-
+

+
+



-
+





-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+

-
+

-
+

-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
-
-
-
+
+
+
+


+
+
+

+



-
-
-
-
+
+
+
+
+





+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+




-
+

-
+
+
+
+






-
+



+
+
+
+




+
+
+
-
+









+
+
-
-
+
+

-
-
+
+
+
+
+
+




-
-
+
+

+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


+
+









-
+
+













+
+
+
+
+
+
+


+
+
+
+
+
+
+
+
+

+
+
+

+









-
+
-

+
-
-
+
+
+
+
+
+
+
+
+

+
-
+
-
-
-
+
-
-
+
+
-
-
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+
+
+
+
+
+
+
+
+
+
+

-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+

+
+
-
-
+
+

-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+

+
-
+
-
-
+
-
-
-
+
+
+
-
-
-
-
+
+
+
+
-
-
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
+
-
-
-
-
+
+
+
-
+

-
+
-

-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
-
-
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+

+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
-
-
-
-
+
+
+
-
-
+
+
+
+
-
-
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+

+
-
+
+
+
+
+
+
+
+
+






-
+





-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+















+
-
-
+
+
+
+
+
+




+


+
+
+
+
+








+


+
-
-
+
+
+
+
-
+
+


-
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
-
-
-
+
+
-
-
-
-
+
+
-
-
-
-
-
-
+
+
+
+











+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+












-
+
+







+


+
+
+
+
+
+
+
+











-
+







/*
** Default to using Markdown markup
*/
#define DEFAULT_FORUM_MIMETYPE  "text/x-markdown"

#if INTERFACE
/*
** Each instance of the following object represents a single message - 
** 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 ForumEntry {
  int fpid;              /* rid for this entry */
struct ForumPost {
  int fpid;              /* rid for this post */
  int fprev;             /* zero if initial entry.  non-zero if an edit */
  int firt;              /* This entry replies to firt */
  int mfirt;             /* Root in-reply-to */
  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 */
  ForumEntry *pLeaf;     /* Most recent edit for this entry */
  ForumEntry *pEdit;     /* This entry is an edit of pEditee */
  ForumEntry *pNext;     /* Next in chronological order */
  ForumEntry *pPrev;     /* Previous in chronological order */
  ForumEntry *pDisplay;  /* Next in display order */
  int nIndent;           /* Number of levels of indentation for this entry */
  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 {
  ForumEntry *pFirst;    /* First entry in chronological order */
  ForumEntry *pLast;     /* Last entry in chronological order */
  ForumEntry *pDisplay;  /* Entries in display order */
  ForumEntry *pTail;     /* Last on the display list */
  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){
  ForumEntry *pEntry, *pNext;
  for(pEntry=pThread->pFirst; pEntry; pEntry = pNext){
    pNext = pEntry->pNext;
    fossil_free(pEntry->zUuid);
    fossil_free(pEntry);
  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);
}

#if 0 /* not used */
/*
** Search a ForumEntry list forwards looking for the entry with fpid
** Search a ForumPost list forwards looking for the post with fpid
*/
static ForumEntry *forumentry_forward(ForumEntry *p, int fpid){
static ForumPost *forumpost_forward(ForumPost *p, int fpid){
  while( p && p->fpid!=fpid ) p = p->pNext;
  return p;
}
#endif

/*
** Search backwards for a ForumEntry
** Search backwards for a ForumPost
*/
static ForumEntry *forumentry_backward(ForumEntry *p, int fpid){
static ForumPost *forumpost_backward(ForumPost *p, int fpid){
  while( p && p->fpid!=fpid ) p = p->pPrev;
  return p;
}

/*
** Add an entry to the display list
** Add a post to the display list
*/
static void forumentry_add_to_display(ForumThread *pThread, ForumEntry *p){
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 entry will be no earlier then
** entry "p".
** reference fpid.  The first such post will be no earlier then
** post "p".
*/
static void forumthread_display_order(
  ForumThread *pThread,
  ForumEntry *p,
  ForumThread *pThread,    /* The complete thread */
  ForumPost *pBase         /* Add replies to this post */
  int fpid,
  int nIndent
){
  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;
  while( p ){
    if( p->fprev==0 && p->mfirt==fpid ){
      p->nIndent = nIndent;
      forumentry_add_to_display(pThread, p);
      forumthread_display_order(pThread, p->pNext, p->fpid, nIndent+1);
    }
    p = p->pNext;
      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;
  ForumEntry *pEntry;
  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)"
     "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 ){
    pEntry = fossil_malloc( sizeof(*pEntry) );
    memset(pEntry, 0, sizeof(*pEntry));
    pEntry->fpid = db_column_int(&q, 0);
    pEntry->firt = db_column_int(&q, 1);
    pEntry->fprev = db_column_int(&q, 2);
    pEntry->zUuid = fossil_strdup(db_column_text(&q,3));
    pEntry->mfirt = pEntry->firt;
    pEntry->pPrev = pThread->pLast;
    pEntry->pNext = 0;
    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 = pEntry;
      pThread->pFirst = pPost;
    }else{
      pThread->pLast->pNext = pEntry;
      pThread->pLast->pNext = pPost;
    }
    pThread->pLast = pEntry;
  }
  db_finalize(&q);

  /* Establish which entries are the latest edit.  After this loop
    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;
        }
      }
    }

  ** completes, entries that have non-NULL pLeaf should not be
  ** displayed.
  */
  for(pEntry=pThread->pFirst; pEntry; pEntry=pEntry->pNext){
    if( pEntry->fprev ){
    /* Maintain the linked list of post edits. */
    if( fprev ){
      ForumEntry *pBase = 0, *p;
      p = forumentry_backward(pEntry->pPrev, pEntry->fprev);
      pEntry->pEdit = p;
      while( p ){
        pBase = p;
        p->pLeaf = pEntry;
        p = pBase->pEdit;
      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;
      }
      for(p=pEntry->pNext; p; p=p->pNext){
        if( p->mfirt==pEntry->fpid ) p->mfirt = pBase->fpid;
      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 */
    pEntry = pThread->pFirst;
    pEntry->nIndent = 1;
    forumentry_add_to_display(pThread, pEntry);
    forumthread_display_order(pThread, pEntry, pEntry->fpid, 2);
    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
** Usage: %fossil test-forumthread [THREADID]
**
** Display a summary of all messages on a thread.
** 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;
  ForumEntry *p;
  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);
    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  */
  fossil_print("     fpid      firt     fprev     mfirt     pLeaf\n");
/*  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("%9d %9d %9d %9d %9d\n",
       p->fpid, p->firt, p->fprev, p->mfirt, p->pLeaf ? p->pLeaf->fpid : 0);
    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->pLeaf ){
      fossil_print("%d->%d\n", p->fpid, p->pLeaf->fpid);
    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("%d\n", p->fpid);
    }
    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 */
  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>
  }
}

/*
** Generate the buttons in the display that allow a forum supervisor to
** Compute a display name from a login name.
** mark a user as trusted.  Only do this if:
**
** If the input login is found in the USER table, then check the USER.INFO
**   (1)  The poster is an individual, not a special user like "anonymous"
**   (2)  The current user has Forum Supervisor privilege
** 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 void generateTrustControls(Manifest *pPost){
  static Stmt q;
  if( !g.perm.AdminForum ) return;
  if( login_is_special(pPost->zUser) ) return;
  @ <br>
  char *zResult;
  @ <label><input type="checkbox" name="trust">
  @ Trust user "%h(pPost->zUser)"
  db_static_prepare(&q,
     "SELECT display_name(info) FROM user WHERE login=$login"
  @ so that future posts by "%h(pPost->zUser)" do not require moderation.
  @ </label>
  );
  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>",
  @ <input type="hidden" name="trustuser" value="%h(pPost->zUser)">
}

                  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;
}

/*
** Display all posts in a forum thread in chronological order
** 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 void forum_display_chronological(int froot, int target){
  ForumThread *pThread = forumthread_create(froot, 0);
  ForumEntry *p;
  int notAnon = login_is_individual();
  for(p=pThread->pFirst; p; p=p->pNext){
    char *zDate;
    Manifest *pPost;
static char *forum_post_display_name(ForumPost *p, Manifest *pManifest){
  Manifest *pToFree = 0;
    int isPrivate;        /* True for posts awaiting moderation */
    int sameUser;         /* True if author is also the reader */

    pPost = manifest_get(p->fpid, CFTYPE_FORUM, 0);
    if( pPost==0 ) continue;
    if( p->fpid==target ){
      @ <div id="forum%d(p->fpid)" class="forumTime forumSel">
    }else if( p->pLeaf!=0 ){
      @ <div id="forum%d(p->fpid)" class="forumTime forumObs">
  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{
      @ <div id="forum%d(p->fpid)" class="forumTime">
      @ >
    }

    /* If this is the first post (or an edit thereof), emit the thread title. */
    if( pPost->zThreadTitle ){
      @ <h1>%h(pPost->zThreadTitle)</h1>
    if( pManifest->zThreadTitle ){
      @ <h1>%h(pManifest->zThreadTitle)</h1>
    }
    zDate = db_text(0, "SELECT datetime(%.17g)", pPost->rDate);
    @ <p>By %h(pPost->zUser) on %h(zDate) (%d(p->fpid))
    fossil_free(zDate);
    if( p->pEdit ){
      @ edit of %z(href("%R/forumpost/%S?t=c",p->pEdit->zUuid))%d(p->fprev)</a>
    }
    if( p->firt ){
      ForumEntry *pIrt = p->pPrev;

    /* 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);
      while( pIrt && pIrt->fpid!=p->firt ) pIrt = pIrt->pPrev;
      if( pIrt ){
        @ reply to %z(href("%R/forumpost/%S?t=c",pIrt->zUuid))%d(p->firt)</a>
      }
    }else{
      zPosterName = forum_post_display_name(p, pManifest);
      zEditorName = zPosterName;
    }
    zDate = db_text(0, "SELECT datetime(%.17g,toLocal())", p->rDate);
    if( p->pLeaf ){
    if( p->pEditPrev ){
      @ updated by %z(href("%R/forumpost/%S?t=c",p->pLeaf->zUuid))\
      @ %d(p->pLeaf->fpid)</a>
      zPosterName = forum_post_display_name(p->pEditHead, 0);
    }
    if( g.perm.Debug ){
      @ <span class="debug">\
      zEditorName = forum_post_display_name(p, pManifest);
      zHist = bHist ? "" : zQuery[0]==0 ? "?hist" : "&hist";
      @ <h3 class='forumPostHdr'>(%d(p->sid)\
      @ <a href="%R/artifact/%h(p->zUuid)">artifact</a></span>
    }
    if( p->fpid!=target ){
      @ %z(href("%R/forumpost/%S?t=c",p->zUuid))[link]</a>
      @ .%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))\
    }
    isPrivate = content_is_private(p->fpid);
    sameUser = notAnon && fossil_strcmp(pPost->zUser, g.zLogin)==0;
        @ %d(p->sid).%0*d(fossil_num_digits(p->nEdit))(p->pEditPrev->rev)</a>
    if( isPrivate && !g.perm.ModForum && !sameUser ){
      @ <p><span class="modpending">Awaiting Moderator Approval</span></p>
    }else{
      forum_render(0, pPost->zMimetype, pPost->zWiki, 0);
    }
    if( g.perm.WrForum && p->pLeaf==0 ){
      int sameUser = login_is_individual()
      }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>
    }

                     && fossil_strcmp(pPost->zUser, g.zLogin)==0;
      @ <p><form action="%R/forumedit" method="POST">
      @ <input type="hidden" name="fpid" value="%s(p->zUuid)">
      if( !isPrivate ){
        /* Reply and Edit are only available if the post has already
        ** been approved */
    /* 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. */
        @ <input type="submit" name="reply" value="Reply">
        if( g.perm.Admin || sameUser ){
          @ <input type="submit" name="edit" value="Edit">
    if( !bUnf ){
      @ %z(href("%R/forumpost/%!S?raw",p->zUuid))[source]</a>
    }
    @ </h3>
          @ <input type="submit" name="nullout" value="Delete">
        }
      }else if( g.perm.ModForum ){
        /* Provide moderators with moderation buttons for posts that
        ** are pending moderation */
        @ <input type="submit" name="approve" value="Approve">
        @ <input type="submit" name="reject" value="Reject">
  }

  /* 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>
        generateTrustControls(pPost);
      }else if( sameUser ){
  }else{
        /* A post that is pending moderation can be deleted by the
        ** person who originally submitted the post */
        @ <input type="submit" name="reject" value="Delete">
      }
    if( bRaw || bUnf || p->pEditTail ){
      zMimetype = "text/plain";
    }else{
      @ </form></p>
      zMimetype = pManifest->zMimetype;
    }
    manifest_destroy(pPost);
    forum_render(0, zMimetype, pManifest->zWiki, 0, !bRaw);
    @ </div>
  }
  forumthread_delete(pThread);
}


  /* 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">
          }
/*
** Display all messages in a forumthread with indentation.
*/
static int forum_display_hierarchical(int froot, int target){
  ForumThread *pThread;
  ForumEntry *p;
  Manifest *pPost, *pOPost;
  int fpid;
          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>
  const char *zUuid;
  char *zDate;
          @ <input type="hidden" name="trustuser" value="%h(pManifest->zUser)">
        }
      }else if( bSameUser ){
  const char *zSel;
  int notAnon = login_is_individual();

        /* 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")' />
        }
  pThread = forumthread_create(froot, 1);
  for(p=pThread->pFirst; p; p=p->pNext){
        @ </form>
    if( p->fpid==target ){
      while( p->pEdit ) p = p->pEdit;
      target = p->fpid;
      break;
    }
  }
  for(p=pThread->pDisplay; p; p=p->pDisplay){
    int isPrivate;         /* True for posts awaiting moderation */
    int sameUser;          /* True if reader is also the poster */
      }
      @ </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 */
    pOPost = manifest_get(p->fpid, CFTYPE_FORUM, 0);
    if( p->pLeaf ){
      fpid = p->pLeaf->fpid;
      zUuid = p->pLeaf->zUuid;
      pPost = manifest_get(fpid, CFTYPE_FORUM, 0);
    }else{
      fpid = p->fpid;
      zUuid = p->zUuid;
      pPost = pOPost;
    }
};

/*
** 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;

    zSel = p->fpid==target ? " forumSel" : "";
    if( p->nIndent==1 ){
  /* In raw mode, force unformatted display and disable history. */
  if( mode == FD_RAW ){
      @ <div id='forum%d(fpid)' class='forumHierRoot%s(zSel)'>
    }else{
    bUnf = 1;
    bHist = 0;
      @ <div id='forum%d(fpid)' class='forumHier%s(zSel)' \
      @ style='margin-left: %d((p->nIndent-1)*3)ex;'>
    }
    pPost = manifest_get(fpid, CFTYPE_FORUM, 0);
    if( pPost==0 ) continue;
    if( pPost->zThreadTitle ){
      @ <h1>%h(pPost->zThreadTitle)</h1>
  }

  /* 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{
    zDate = db_text(0, "SELECT datetime(%.17g)", pOPost->rDate);
    @ <p>By %h(pOPost->zUser) on %h(zDate)
    fossil_free(zDate);
    if( g.perm.Debug ){
    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) ){
      @ <span class="debug">\
      @ <a href="%R/artifact/%h(p->zUuid)">(%d(p->fpid))</a></span>
    }
    if( p->pLeaf ){
    return;
  }

  /* Create the common query string to append to nearly all post links. */
  i = 0;
  if( !autoMode ){
      zDate = db_text(0, "SELECT datetime(%.17g)", pPost->rDate);
      if( fossil_strcmp(pOPost->zUser,pPost->zUser)==0 ){
        @ and edited on %h(zDate)
      }else{
    char m = 'a';
    switch( mode ){
      case FD_RAW:     m = 'r';  break;
        @ as edited by %h(pPost->zUser) on %h(zDate)
      }
      case FD_CHRONO:  m = 'c';  break;
      case FD_HIER:    m = 'h';  break;
      case FD_SINGLE:  m = 's';  break;
    }
      fossil_free(zDate);
      if( g.perm.Debug ){
    zQuery[i++] = '?';
    zQuery[i++] = 't';
    zQuery[i++] = '=';
    zQuery[i++] = m;
        @ <span class="debug">\
        @ <a href="%R/artifact/%h(p->pLeaf->zUuid)">(%d(fpid))</a></span>
      }
      manifest_destroy(pOPost);
    }
    if( fpid!=target ){
  }
  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';
      @ %z(href("%R/forumpost/%S",zUuid))[link]</a>
    }
    isPrivate = content_is_private(fpid);
    sameUser = notAnon && fossil_strcmp(pPost->zUser, g.zLogin)==0;
    if( isPrivate && !g.perm.ModForum && !sameUser ){
      @ <p><span class="modpending">Awaiting Moderator Approval</span></p>
    }else{
  }
  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{
      forum_render(0, pPost->zMimetype, pPost->zWiki, 0);
    }
    if( g.perm.WrForum ){
      @ <p><form action="%R/forumedit" method="POST">
      @ <input type="hidden" name="fpid" value="%s(zUuid)">
      if( !isPrivate ){
        /* Reply and Edit are only available if the post has already
        ** been approved */
    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;
        @ <input type="submit" name="reply" value="Reply">
        if( g.perm.Admin || sameUser ){
          @ <input type="submit" name="edit" value="Edit">
        if( p->pEditHead ) p = p->pEditHead;
        do p = p->pNext; while( p && p->sid<=sid );
        if( p && p->pEditTail ) p = p->pEditTail;
          @ <input type="submit" name="nullout" value="Delete">
        }
      }else if( g.perm.ModForum ){
        /* Provide moderators with moderation buttons for posts that
        ** are pending moderation */
        @ <input type="submit" name="approve" value="Approve">
        @ <input type="submit" name="reject" value="Reject">
        generateTrustControls(pPost);
      }else if( sameUser ){
        /* A post that is pending moderation can be deleted by the
        ** person who originally submitted the post */
        @ <input type="submit" name="reject" value="Delete">
      }
      @ </form></p>
    }
      }
    }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>
  }
    manifest_destroy(pPost);
    @ </div>
  }

  /* Clean up. */
  forumthread_delete(pThread);
}
  return target;

/*
** 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
** it's entire thread.  The selected posting is enclosed within
** 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=MODE        Display mode. MODE is 'c' for chronological or
**                   'h' for hierarchical, or 'a' for automatic.
**   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
**   t=MODE        Display mode. MODE is 'c' for chronological or
**                   'h' for hierarchical, or 'a' for automatic.
**                 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_error("Unknown or ambiguous forum id: \"%s\"", zName);
  }
      webpage_notfound_error("Unknown forum id: \"%s\"", zName);
    }else{
      ambiguous_page();
    }
  style_header("Forum");
    return;
  }
  froot = db_int(0, "SELECT froot FROM forumpost WHERE fpid=%d", fpid);
  if( froot==0 ){
    webpage_error("Not a forum post: \"%s\"", zName);
    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;
  if( zMode[0]=='a' ){
    if( cgi_from_mobile() ){
      zMode = "c";  /* Default to chronological on mobile */
    }else{
      zMode = "h";
    }
  forum_display_thread(froot, fpid, mode, autoMode, bUnf, bHist);

  }
  if( zMode[0]=='c' ){
    style_submenu_element("Hierarchical", "%R/%s/%s?t=h", g.zPath, zName);
    forum_display_chronological(froot, fpid);
  /* Emit Forum Javascript. */
  builtin_request_js("forum.js");
  }else{
    style_submenu_element("Chronological", "%R/%s/%s?t=c", g.zPath, zName);
    forum_display_hierarchical(froot, fpid);
  }
  style_load_js("forum.js");
  style_footer();
  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 */
  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, 
  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);
  }
625
626
627
628
629
630
631
632

633
634
635
636
637
638
639
1344
1345
1346
1347
1348
1349
1350

1351
1352
1353
1354
1355
1356
1357
1358







-
+







    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", strlen(zContent), zContent);
  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);
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
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







+
+
-
+
+
+
+
+
+








-
+





-
+
+

-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+












+






-
+


-
+










+







    @ <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();
    int nrid = wiki_put(&x, 0, forum_need_moderation());
    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_entry_widget(
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"><br>
    @ Title: <input type="input" name="title" value="%h(zTitle)" size="50"
    @ maxlength="125"><br>
  }
  @ Markup style:
  mimetype_option_menu(zMimetype);
  @ <br><textarea name="content" class="wikiedit" cols="80" \
  @ rows="25" wrap="virtual">%h(zContent)</textarea><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("%R/forume2?fpid=%S",PD("fpid",""));
    zGoto = mprintf("forume2?fpid=%S",PD("fpid",""));
    isEdit = 1;
  }else{
    zGoto = mprintf("%R/forume1");
    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>
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
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







+
-
+













+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+










+





-
-
+
+
+

-
+

-
+

+




-
+

-
+




-
-
-
+
-
-
-
-
-
-
-
-
+

+
-
+








-
+



+

+



+
+


+
+
+
+
+






-
+



+
+
+
+

-
+


+
+
+
-
-
+
+
+
+
+
+
-

-
+




+



+





-
+














+

-
+
+
+
+
+


-
-
-
+
+
+

-
+
+












-
+

-
+

+





-
+












-
+

-
-
-
-
+
+
+
+

-
+

+



-
+


+



-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+




-
+





-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
-
-

+
+

+
-
+



+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+










+



-
+

+
+
+






+
+
-
+
+


+
+
+
+
+
+







-
+



+
+
+
-
+
+
+
+
+
+
+
+
+
+
+







  @ <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_footer();
  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") ){
    if( forum_post(zTitle, 0, 0, 0, zMimetype, zContent) ) return;
  if( P("submit") && cgi_csrf_safe(2) ){
    if( forum_post(zTitle, 0, 0, 0, zMimetype, zContent,
                   forum_post_flags()) ) return;
  }
  if( P("preview") ){
  if( P("preview") && !whitespace_only(zContent) ){
    @ <h1>Preview:</h1>
    forum_render(zTitle, zMimetype, zContent, "forumEdit");
    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_entry_widget(zTitle, zMimetype, zContent);
  forum_post_widget(zTitle, zMimetype, zContent);
  @ <input type="submit" name="preview" value="Preview">
  if( P("preview") ){
  if( P("preview") && !whitespace_only(zContent) ){
    @ <input type="submit" name="submit" value="Submit">
  }else{
    @ <input type="submit" name="submit" value="Submit" disabled>
  }
  if( g.perm.Debug ){
    /* For the test-forumnew page add these extra debugging controls */
    @ <div class="debug">
  forum_render_debug_options();
    @ <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>
    @ </div>
  }
  login_insert_csrf_secret();
  @ </form>
  forum_emit_js();
  style_footer();
  style_finish_page();
}

/*
** WEBPAGE: forume2
**
** Edit an existing forum message.
** Query parameters:
**
**   fpid=X        Hash of the post to be editted.  REQUIRED
**   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(PD("fpid",""), "f");
  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",P("fpid"));
    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(1);
  if( g.perm.ModForum && isCsrfSafe ){
  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") ){
    if( P("approve") ){
      const char *zUserToTrust;
      moderation_approve(fpid);
      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 = 
      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 ){
  if( P("submit")
   && isCsrfSafe
   && (zContent = PDT("content",""))!=0
   && (!whitespace_only(zContent) || isDelete)
  ){
    int done = 1;
    const char *zMimetype = PD("mimetype",DEFAULT_FORUM_MIMETYPE);
    const char *zContent = PDT("content","");
    if( P("reply") ){
      done = forum_post(0, fpid, 0, 0, zMimetype, zContent);
    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);
      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");
                 "forumEdit", 1);
    @ <h1>Change Into:</h1>
    forum_render(zTitle, zMimetype, zContent,"forumEdit");
    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 type="hidden" name="title" value="%h(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");
    @ <h1>Original Post:</h1>
    @ <h2>Original Post:</h2>
    forum_render(pPost->zThreadTitle, pPost->zMimetype, pPost->zWiki,
                 "forumEdit");
    if( P("preview") ){
      @ <h1>Preview of Edited Post:</h1>
      forum_render(zTitle, zMimetype, zContent,"forumEdit");
                 "forumEdit", 1);
    if( bPreview ){
      @ <h2>Preview of Edited Post:</h2>
      forum_render(zTitle, zMimetype, zContent,"forumEdit", 1);
    }
    @ <h1>Revised Message:</h1>
    @ <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_entry_widget(zTitle, zMimetype, zContent);
    forum_post_widget(zTitle, zMimetype, zContent);
  }else{
    /* Reply */
    char *zDisplayName;
    zMimetype = PD("mimetype",DEFAULT_FORUM_MIMETYPE);
    zContent = PDT("content","");
    style_header("Reply");
    @ <h1>Replying To:</h1>
    forum_render(0, pPost->zMimetype, pPost->zWiki, "forumEdit");
    if( P("preview") ){
      @ <h1>Preview:</h1>
      forum_render(0, zMimetype,zContent, "forumEdit");
    @ <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);
    }
    @ <h1>Enter Reply:</h1>
    @ <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_entry_widget(0, zMimetype, zContent);
    forum_post_widget(0, zMimetype, zContent);
  }
  if( !isDelete ){
    @ <input type="submit" name="preview" value="Preview">
  }
  @ <input type="submit" name="cancel" value="Cancel">
  if( P("preview") || isDelete ){
    @ <input type="submit" name="submit" value="Submit">
  }
  if( (bPreview && !whitespace_only(zContent)) || isDelete ){
    if( !iClosed || g.perm.Admin ) {
      @ <input type="submit" name="submit" value="Submit">
    }
  if( g.perm.Debug ){
    /* For the test-forumnew page add these extra debugging controls */
    @ <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>
    @ </div>
  }
  forum_render_debug_options();
  login_insert_csrf_secret();
  @ </form>
  forum_emit_js();
  style_footer();
  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, iOfst, iCnt;
  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("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_footer();
      style_finish_page();
      return;
    }
  }
  cookie_read_parameter("n","forum-n");
  zLimit = P("n");
  if( zLimit!=0 ){
  iLimit = atoi(PD("n","25"));
    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),"
1071
1072
1073
1074
1075
1076
1077
1078

1079
2030
2031
2032
2033
2034
2035
2036

2037
2038







-
+

    db_finalize(&q);
  }
  if( iCnt>0 ){
    @ </table></div>
  }else{
    @ <h1>No forum posts found</h1>
  }
  style_footer();
  style_finish_page();
}

Changes to src/forum.js.

12
13
14
15
16
17
18
19

12
13
14
15
16
17
18

19







-
+
  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, '&amp;'], [/</g, '&lt;']];
        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> &rarr; 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,'&lt;'),
            /* ^^^ 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
                         &nbsp; 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 &nbsp;, so...*/.split('&nbsp;').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 = "&emsp;&#9851; " + wh_hidden;
  }
  else {

    wh_collapser.title="Hide intermediate edits";
    wh_collapser.innerHTML = "&emsp;&#9842;"
  }
}

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 = "&ensp;&#9842;" + edits;
      wh_radios[k].iterspan = span;
      edits = 0;
      //   also:  &#8746; (union)   &#931; (sigma)   &#215; (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> &rarr; 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);

Changes to src/fshell.c.

55
56
57
58
59
60
61

62
63





64
65
66
67

68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88

89
90
91
92
93
94
95
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72

73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93

94
95
96
97
98
99
100
101







+


+
+
+
+
+



-
+




















-
+







  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("fossil> ")) ){
  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 && !isspace(zLine[i]) ){ 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++){
107
108
109
110
111
112
113
114

115
116
117
118
119
120
121

122
123
113
114
115
116
117
118
119

120
121
122
123
124
125
126
127
128
129
130







-
+







+


      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**);
      main(nArg, azArg);
      fossil_main(nArg, azArg);
      exit(0);
    }else{
      /* The parent process */
      int status;
      waitpid(childPid, &status, 0);
    }
  }
  free(zPrompt);
#endif
}

Changes to src/fusefs.c.

18
19
20
21
22
23
24
25
26
27
28

29
30
31
32
33
34
35
18
19
20
21
22
23
24

25
26
27
28
29
30
31
32
33
34
35







-



+







** 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.
*/
#ifdef FOSSIL_HAVE_FUSEFS
#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"

281
282
283
284
285
286
287

288
289
290

291
292
293
294
295
296
297
281
282
283
284
285
286
287
288
289
290

291
292
293
294
295
296
297
298







+


-
+







}

static struct fuse_operations fusefs_methods = {
  .getattr = fusefs_getattr,
  .readdir = fusefs_readdir,
  .read    = fusefs_read,
};
#endif /* FOSSIL_HAVE_FUSEFS */

/*
** COMMAND: 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
312
313
314
315
316
317
318

319
320
321
322
323
324
325
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327







+







** 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);
333
334
335
336
337
338
339
340



341

342
343
344
345
346
347
348
335
336
337
338
339
340
341

342
343
344
345
346
347
348
349
350
351
352
353







-
+
+
+

+







  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

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);
  }
}

Changes to src/glob.c.

87
88
89
90
91
92
93
94
95


96
97
98



99
100

101
102
103
104
105
106
107
87
88
89
90
91
92
93


94
95
96


97
98
99
100

101
102
103
104
105
106
107
108







-
-
+
+

-
-
+
+
+

-
+







struct Glob {
  int nPattern;        /* Number of patterns */
  char **azPattern;    /* Array of pointers to patterns */
};
#endif /* INTERFACE */

/*
** zPatternList is a comma-separated list of glob patterns.  Parse up
** that list and use it to create a new Glob object.
** 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 our
** double-quotes.  This allows a comma to be part of a glob pattern.
** 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 unquoted glob patterns are ignored.
** 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 */
124
125
126
127
128
129
130
131
132


133
134


135
136
137
138
139
140
141
125
126
127
128
129
130
131


132
133


134
135
136
137
138
139
140
141
142







-
-
+
+
-
-
+
+







      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 delimter (or the end of the string). */
    for(i=0; z[i] && z[i]!=delimiter; i++){
    /* Find the next delimiter (or the end of the string). */
    for(i=0; z[i] && z[i]!=delimiter &&
      if( delimiter!=',' ) continue; /* If quoted, keep going. */
      if( fossil_isspace(z[i]) ) break; /* If space, stop. */
        !(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;
}
161
162
163
164
165
166
167

































168
169
170
171
172
173
174
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







*/
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

Changes to src/graph.c.

16
17
18
19
20
21
22
23


























24
25












26
27
28
29
30
31
32
33
34
35
36
37
38
39






40
41
42
43
44
45
46
47

48
49
50
51



52
53

54
55
56
57
58

59
60

61
62
63
64
65
66
67
68
69
70


71
72
73
74



75

76
77
78
79
80
81







82
83
84
85
86
87
88
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74



75
76
77
78
79
80
81
82
83
84
85
86
87

88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105

106
107
108
109
110
111
112
113
114


115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145








+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


+
+
+
+
+
+
+
+
+
+
+
+











-
-
-
+
+
+
+
+
+







-
+




+
+
+


+





+

-
+








-
-
+
+




+
+
+

+






+
+
+
+
+
+
+







*******************************************************************************
**
** 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 {
  int rid;                    /* The rid for the check-in */
  i8 nParent;                 /* Number of parents.  -1 for technote lines */
  int *aParent;               /* Array of parents.  0 element is primary .*/
  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.  First is 1.  0 used for "none" */
  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 */
  GraphRow *pLast;           /* Last row in the list */
  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);
139
140
141
142
143
144
145
146

147
148
149
150
151
152
153
196
197
198
199
200
201
202

203
204
205
206
207
208
209
210







-
+







    p->apHash[h] = pRow;
  }
}

/*
** Look up the row with rid.
*/
static GraphRow *hashFind(GraphContext *p, int 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];
}
175
176
177
178
179
180
181
182

183

184

185
186
187
188
189
190
191
192
193
194
195
196
197
198

199



200


201
202
203
204
205
206
207
232
233
234
235
236
237
238

239
240
241

242
243
244
245
246
247
248
249
250
251
252
253
254
255

256
257
258
259
260
261
262
263
264
265
266
267
268
269
270







-
+

+
-
+













-
+

+
+
+

+
+







}

/*
** 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 */
  int rid,             /* RID for the check-in */
  GraphRowId rid,      /* RID for the check-in */
  int nParent,         /* Number of parents */
  int nCherrypick,     /* How many of aParent[] are actually cherrypicks */
  int *aParent,        /* Array of parents */
  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 ? (int*)&pRow[1] : 0;
  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);
220
221
222
223
224
225
226
227


228
229
230
231
232
233
234
235
236
237
238


239
240





















241

242
243



244
245
246
247
248
249
250
251
252
253
254

255
256
257
258
259
260
261

262
263
264
265
266
267
268
269
270
271

272
273
274
275
276
277
278
279
280
























281
282
283
284
285
286
287
288
289



290
291
292
293
294
295
296








297

298
299
300
301

302

303
304
305
306

307
308


309
310
311
312
313
314
315










316

317
318
319
320
321
322
323
324
325
326
327
328



329
330
331
332
333
334
335


336
337
338

339
340

341
342
343
344
345
346
347
348
349
350
351
352
353







354
355

356
357
358
359
360




361
362
363
364
365
366

367
368
369
370
371
372
373
283
284
285
286
287
288
289

290
291
292
293
294
295
296
297
298
299
300
301
302
303
304


305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328

329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349

350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401


402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419

420
421
422
423
424
425

426
427
428
429
430
431


432
433
434
435
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







-
+
+











+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+

-
+
+
+











+






-
+










+









+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







-
-
+
+
+







+
+
+
+
+
+
+
+
-
+




+
-
+




+
-
-
+
+







+
+
+
+
+
+
+
+
+
+
-
+











-
+
+
+






-
+
+



+

-
+




-








+
+
+
+
+
+
+

-
+





+
+
+
+





-
+







/*
** 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 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<32; i++){
    if( (inUseMask & BIT(i))==0 ){
  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 ){
        return i;
        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){
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,
  GraphRow *pChild
  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;
      }
    if( u>=0 && u<pChild->idx ){
    }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->mergeUpto = pChild->idx;
      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);
      pParent->mergeUpto = pChild->idx;
      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] = 1;
  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>(BIT(p->mxRail+1)-1) ){
    while( p->mxRail<GR_MAX_RAIL
        && (pRow->mergeDown|pRow->cherrypickDown)>(BIT(p->mxRail+1)-1)
    ){
      p->mxRail++;
    }
  }
}

/*
** Draw a riser from pRow to the top of the graph
** 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 ){
  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, int omitDescenders){
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.
  */
  int mergeRiserFrom[GR_MAX_RAIL];
  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 );
389
390
391
392
393
394
395
396

397
398
399
400










401
402
403
404
405




















































406
407
408
409
410
411
412
413

414
415
416
417

418
419
420

421
422
423
424
425
426
427
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







-
+


-
-
+
+
+
+
+
+
+
+
+
+





+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







-
+



-
+


-
+







  **
  ** Each node has one primary parent and zero or more "merge" parents.
  ** A merge parent is a prior check-in from which changes were merged into
  ** the current check-in.  If a merge parent is not in the visible section
  ** of this graph, then no arrows will be drawn for it, so remove it from
  ** the aParent[] array.
  */
  if( omitDescenders ){
  if( (tmFlags & (TIMELINE_DISJOINT|TIMELINE_XMERGE))!=0 ){
    for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
      for(i=1; i<pRow->nParent; i++){
        if( hashFind(p, pRow->aParent[i])==0 ){
          pRow->aParent[i] = pRow->aParent[--pRow->nParent];
        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->nParent<2 ) continue;                    /* Not a fork */
    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->nParent; i++){
    for(i=1; i<pRow->nNonCherrypick; i++){
      pParent = hashFind(p, pRow->aParent[i]);
      if( pParent && pParent->zBranch==pRow->zBranch ){
        int t = pRow->aParent[0];
        GraphRowId t = pRow->aParent[0];
        pRow->aParent[0] = pRow->aParent[i];
        pRow->aParent[i] = t;
        break;
      }
    }
  }

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
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







-
-
+
+
-
-
+





+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+






+
+


-
-
-
-
-

-
-
+
-
-
-



+

-
+



-
+







-
+







  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;
       continue;                                       /* Time-warp */
      pParent->timeWarp = 1;
      nTimewarp++;
    }
    if( pRow->idxTop < pParent->idxTop ){
    }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 to the bottom of the screen.
  ** 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( i==0 ){
        if( pRow->zBranch!=zTrunk ) continue;
      }else {
        if( pRow->iRail>=0 ) continue;
      }
      if( pRow->nParent==0 || hashFind(p,pRow->aParent[0])==0 ){
        if( omitDescenders ){
          pRow->iRail = findFreeRail(p, pRow->idxTop, pRow->idx, 0);
        pRow->iRail = findFreeRail(p, pRow->idxTop, pRow->idx+riserMargin,0,0);
        }else{
          pRow->iRail = ++p->mxRail;
        }
        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; pLoop=pLoop->pNext){
          for(pLoop=pRow; pLoop && (n--)>0; pLoop=pLoop->pNext){
            pLoop->railInUse |= mask;
          }
        }
        assignChildrenToRail(pRow);
        assignChildrenToRail(pRow, tmFlags);
      }
    }
  }

  /* Assign rails to all rows that are still unassigned.
  */
  for(pRow=p->pLast; pRow; pRow=pRow->pPrev){
    int parentRid;
    GraphRowId parentRid;

    if( pRow->iRail>=0 ){
      if( pRow->pChild==0 && !pRow->timeWarp ){
        if( !omitDescenders && count_nonbranch_children(pRow->rid)!=0 ){
          riser_to_top(pRow);
        }
      }
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
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







-
+
+




















-
+




+
+
-
-
+
+
+
+
+
+
+
+








+
+
+

-
+
+
+
+
+
+
+
+
+


+

-
+
+
+







-
+



+
+

+
+
+
+
-
-
+
+
+




+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+



















-
+















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


        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, 0, pParent->idx, pParent->iRail);
        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);
      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;
        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++){
      int parentRid = pRow->aParent[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 */
        int iMrail = -1;
        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->nRow, 0);
          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;
          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{
        /* Merge from an on-screen node */
        createMergeRiser(p, pDesc, pRow);
        if( p->mxRail>=GR_MAX_RAIL ) return;
          /* 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);
      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 into
    **              the preferred branch.  Only applies if a preferred branch
    **              is defined.  This improves the display of r=BRANCH
    **              options to /timeline.
    **
    **    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;
        }
      }
      for(i=0; i<=p->mxRail; i++){
        if( p->mergeRail & BIT(i) ){
          aPriority[i] |= 2;
        }
      }
    }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;
        }
      }
    }

#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;
}

Changes to 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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

32
33
34
35






36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

54
55
56
57



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226

227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258

+
+
+

















+
+
+







-
+



-
-
-
-
-
-
+
+
+
+
+
+
+

+
+
+







-
+
+


-
-
-
+
+
+
+
+

+
+
+
+



















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+




+











-
+
+



















+


+







/* 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 tx.
**        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:  True if there is a "descender" - an arrow coming from the bottom
**        of the page straight up to this node.
**   mo:  "merge-out".  If non-negative, this is the rail position
**        for the upward portion of a merge arrow.  The merge arrow goes up
**        to the row identified by mu:.  If this value is negative then
**        node has no merge children and no merge-out line is drawn.
**    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.
**        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 the absolute value of mi[]
**        and a thin merge-arrow descender is drawn to the bottom of
**        the screen.
**        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"
      "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;
150
151
152
153
154
155
156
157

158
159
160
161
162
163











164
165
166
167
168
169
170
171
172
173
174
175
176
177

178
179

180
181
182
183
184
185
186


187
188

189

190












191
192
193





194

195
196
197
198
199
200
201
202
203
204
205
206






207


208
209
210
211
212
213
214
215
216
217
218


219
220
221



222
223
224


225












226








227
228
229
230













231
232
233




234
235
236







237


















238
239








240
241

242

243







244
245
246
247

248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280


























































281
282
283

284
285
286
287
288
289
290
291
292
293

294
295
296
297

298
299
300
301
302
303


304
305
306
307
308
309
310
311
312
313
314
315

316
317
318





























































































































319
320
321
322
323
324
325
292
293
294
295
296
297
298

299






300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323

324
325

326
327
328
329
330
331
332

333
334
335

336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359

360
361
362
363
364
365
366
367
368
369
370

371
372
373
374
375
376
377

378
379
380
381
382
383
384
385
386
387
388
389

390
391
392


393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422




423
424
425
426
427
428
429
430
431
432
433
434
435
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







-
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+













-
+

-
+






-
+
+

-
+

+

+
+
+
+
+
+
+
+
+
+
+
+



+
+
+
+
+
-
+










-

+
+
+
+
+
+
-
+
+










-
+
+

-
-
+
+
+



+
+

+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+



+
+
+
+

-
-
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+

-
+

+
-
+
+
+
+
+
+
+




+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



+









-
+




+





-
+
+











-
+

-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







    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 top = 0;
    var y = 0;
    if( obj.offsetParent ){
      do{
        top += obj.offsetTop;
      }while( obj = obj.offsetParent );
    }
    return top;
    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";
    }
    drawBox(cls,color,x0,y0,x1,y1);
    return drawBox(cls,color,x0,y0,x1,y1);
  }
  function drawUpArrow(from,to,color){
  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);
    drawLine(line,color,x,y0,null,y1);
    var n = drawLine(line,color,x,y0,null,y1);
    addToolTip(n,id)
    x = to.x + (node.w-arw.w)/2;
    var n = drawBox(arw.cls,null,x,y);
    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){
  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);
    drawMergeLine(x0,y,x1,null);
    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);
    var cls = "arrow merge " + (p.r<rail ? "l" : "r");
      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);
    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.mi.length ) cls += " merge";
    if( p.f&1 ) cls += " leaf";
    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{
      if( p.u==0 ) drawUpArrow(p,{x: p.x, y: -node.h},p.fg);
      if( p.d ) drawUpArrow({x: p.x, y: btm-node.h/2},p,p.fg);
    }
    if( p.mo>=0 ){
          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 ){
        x1 += mergeLines[p.mo] = -mLine.w/2;
      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{
        if( p.r!=p.mo ) drawMergeLine(x0,y0,x1+(x0<x1 ? mLine.w : 0),null);
        drawMergeLine(x1,y0+mLine.w,null,y1);
          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] = u.r<p.r ? -mergeOffset-mLine.w : 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);
          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 ){
        drawLine(line,u.fg,x0,y0,x1,null);
        drawUpArrow(p,u,u.fg);
      }else{
        var y1 = u.y + (node.h-line.w)/2;
        drawLine(wLine,u.fg,x0,y0,x1,null);
        drawLine(wLine,u.fg,x1-line.w,y0,null,y1+line.w);
        drawLine(wLine,u.fg,x1,y1,u.x-wArrow.w/2,null);
        var x = u.x-wArrow.w;
        var y = u.y+(node.h-wArrow.h)/2;
        var n = drawBox(wArrow.cls,null,x,y);
        if( u.fg ) n.style.borderLeftColor = u.fg;
      }
    }
    for( var i=0; i<p.mi.length; i++ ){
      var rail = p.mi[i];
      if( rail<0 ){
        rail = -rail;
        mergeLines[rail] = -mLine.w/2;
        var x = rail*railPitch + (node.w-mLine.w)/2;
        drawMergeLine(x,miLineY(p),null,btm);
      }
      drawMergeArrow(p,rail);
      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.querySelector(".timelineBottom");
    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(){
  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
        location.href=tx.baseUrl + "/fdiff?v1="+selRow.h+"&v2="+p.h;
      }else{
        location.href=tx.baseUrl + "/vdiff?from="+selRow.h+"&to="+p.h
      }
        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, "&amp;")
         .replace(/</g, "&lt;")
         .replace(/>/g, "&gt;")
         .replace(/"/g, "&quot;")
         .replace(/'/g, "&#039;");
      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;}
  }
389
390
391
392
393
394
395







396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411

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







+
+
+
+
+
+
+















-
+
    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);
  }
}())
}());

Changes to src/gzip.c.

19
20
21
22
23
24
25
26
27
28
29
30

31
32
33
34
35
36
37
38
39

40
41
42
43
44
45
46
19
20
21
22
23
24
25





26

27
28
29
30
31
32
33

34
35
36
37
38
39
40
41







-
-
-
-
-
+
-







-
+







** file.  The GZIP format is described in RFC-1952.
**
** State information is stored in static variables, so this implementation
** can only be building up a single GZIP file at a time.
*/
#include "config.h"
#include <assert.h>
#if defined(FOSSIL_ENABLE_MINIZ)
#  define MINIZ_HEADER_FILE_ONLY
#  include "miniz.c"
#else
#  include <zlib.h>
#include <zlib.h>
#endif
#include "gzip.h"

/*
** State information for the GZIP file under construction.
*/
struct gzip_state {
  int eState;           /* 0: idle   1: header  2: compressing */
  int iCRC;             /* The checksum */
  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.
*/

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
      }
    }
  }
})();

Changes to src/hname.c.

214
215
216
217
218
219
220
221

222
223
224
225
226
227
228
214
215
216
217
218
219
220

221
222
223
224
225
226
227
228







-
+







    return HPOLICY_AUTO;
  }
}

/*
** Names of the hash policies.
*/
static const char *azPolicy[] = {
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];

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;
}

Changes to src/href.js.

10
11
12
13
14
15
16
17
18


19

20



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39







40



41
42






43



44

45
46

10
11
12
13
14
15
16


17
18
19
20

21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38




39
40
41
42
43
44
45

46
47
48
49
50
51
52
53
54
55
56

57
58
59
60
61
62

63







-
-
+
+

+
-
+
+
+















-
-
-
-
+
+
+
+
+
+
+
-
+
+
+


+
+
+
+
+
+
-
+
+
+

+

-
+
** (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 timer does not start until a
** mouse motion event occurs over top of the document.
** 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 setAllHrefs(){
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);
  var isOperaMini =
       Object.prototype.toString.call(window.operamini)==="[object OperaMini]";
  if(g.mouseover && !isOperaMini){
    document.getElementByTagName("body")[0].onmousemove=function(){
  if( g.mouseover ){
    document.body.onmousedown=function(){
      antiRobot |= 2;
      antiRobotGo();
      document.body.onmousedown=null;
    }
    document.body.onmousemove=function(){
      setTimeout(setAllHrefs, g.delay);
      antiRobot |= 2;
      antiRobotGo();
      document.body.onmousemove=null;
    }
  }else{
    antiRobot |= 2;
  }
  if( g.delay>0 ){
    setTimeout(function(){
      antiRobot |= 1;
      antiRobotGo();
    setTimeout(setAllHrefs, g.delay);
    }, g.delay)
  }else{
    antiRobot |= 1;
  }
  antiRobotGo();
}
antiRobotDefense()
antiRobotDefense();

Changes to src/http.c.

27
28
29
30
31
32
33












34
35
36
37
38





39
40
41
42
43
44
45
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62







+
+
+
+
+
+
+
+
+
+
+
+





+
+
+
+
+







#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
76
77
78
79
80
81
82
83
84









85
86
87
















88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104






105
106

107
108


109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125



126
127
128
129
130
131








132
133
134
135
136
137
138
93
94
95
96
97
98
99


100
101
102
103
104
105
106
107
108
109


110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139



140
141
142
143
144
145
146
147
148


149
150





151
152
153
154
155
156
157
158
159
160
161
162
163
164
165






166
167
168
169
170
171
172
173
174
175
176
177
178
179
180







-
-
+
+
+
+
+
+
+
+
+

-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+














-
-
-
+
+
+
+
+
+


+
-
-
+
+
-
-
-
-
-












+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+







    zPw = 0;
  }else{
    /* Password failure while doing a sync from the command-line interface */
    url_prompt_for_password();
    zPw = g.url.passwd;
  }

  /* The login card wants the SHA1 hash of the password, so convert the
  ** password to its SHA1 hash if it isn't already a SHA1 hash.
  /* 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.
  */
  /* fossil_print("\nzPw=[%s]\n", zPw); // TESTING ONLY */
  if( zPw && zPw[0] ) zPw = sha1_shared_secret(zPw, zLogin, 0);
  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, Blob *pHdr){
  int i;
  const char *zSep;
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",
  i = strlen(g.url.path);
  if( i>0 && g.url.path[i-1]=='/' ){
               nPayload>0 ? "POST" : "GET", g.url.path,
               g.url.path[0]==0 ? "/" : "");
    zSep = "";
  }else{
    zSep = "/";
  }
  blob_appendf(pHdr, "POST %s%sxfer/xfer HTTP/1.0\r\n", g.url.path, zSep);
  if( g.url.proxyAuth ){
    blob_appendf(pHdr, "Proxy-Authorization: %s\r\n", g.url.proxyAuth);
  }
  if( g.zHttpAuth && g.zHttpAuth[0] ){
    const char *zCredentials = g.zHttpAuth;
    char *zEncoded = encode64(zCredentials, -1);
    blob_appendf(pHdr, "Authorization: Basic %s\r\n", zEncoded);
    fossil_free(zEncoded);
  }
  blob_appendf(pHdr, "Host: %s\r\n", g.url.hostname);
  blob_appendf(pHdr, "User-Agent: %s\r\n", get_user_agent());
  if( g.url.isSsh ) blob_appendf(pHdr, "X-Fossil-Transport: SSH\r\n");
  if( nPayload ){
    if( zAltMimetype ){
      blob_appendf(pHdr, "Content-Type: %s\r\n", zAltMimetype);
  if( g.fHttpTrace ){
    blob_appendf(pHdr, "Content-Type: application/x-fossil-debug\r\n");
  }else{
    blob_appendf(pHdr, "Content-Type: application/x-fossil\r\n");
  }
  blob_appendf(pHdr, "Content-Length: %d\r\n\r\n", blob_size(pPayload));
    }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;
187
188
189
190
191
192
193






















































































































































































194
195
196
197
198
199
200
201
202
203
204
205







206
207
208
209
210
211
212
213
214
215
216
217















218
219
220
221
222
223
224



225
226
227
228
229
230
231
232









233
234
235
236

237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265





266
267
268
269
270
271
272
273
274
275
276

277

278


279
280
281
282
283
284
285
286
287
288


289
290
291

292
293
294
295
296
297
298
299
300
301
302
303
304
305

306
307
308
309
310
311

312
313
314
315
316
317
318
319

320
321
322

323
324
325

326
327

328
329
330
331
332
333
334
335
336
337
338
339
340
341
342

343








344








345
346
347
348
349

350


351
352
353
354
355
356



357
358
359
360
361
362
363
364










































365
366
367
368
369
370
371
372
373
374





375
376
377
378
379
380
381





























382
383
384
385
386
387
388
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428

429
430
431
432
433
434
435
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+











-
+
+
+
+
+
+
+





-






+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+



-
+








-




















+
+
+
+
+











+

+
-
+
+









-
+
+


-
+













-
+






+





-
-
-
+
-

-
+


-
+


+















+
-
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+





+
-
+
+






+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+










+
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







    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, Blob *pReply, int useLogin, int maxRedirect){
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 iRecvLen;         /* Received length of the reply payload */
  int rc = 0;           /* Result code */
  int iHttpVersion;     /* Which version of HTTP protocol server uses */
  char *zLine;          /* A single line of the reply header */
  int i;                /* Loop counter */
  int isError = 0;      /* True if the reply is an error message */
  int isCompressed = 1; /* True if the reply is compressed */

  if( 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( useLogin ) http_build_login_card(pSend, &login);
  if( g.fHttpTrace ){
    payload = login;
    blob_append(&payload, blob_buffer(pSend), blob_size(pSend));
  }else{
    blob_compress2(&login, pSend, &payload);
    blob_reset(&login);
    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);
  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 ){
    static int traceCnt = 0;
    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 ){
    /* printf("[%s]\n", zLine); fflush(stdout); */
      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, useLogin, maxRedirect);
          return http_exchange(pSend, pReply, mHttpFlags,
                               maxRedirect, zAltMimetype);
        }
      }
      if( rc!=200 && rc!=301 && rc!=302 ){
      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 ){
      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 ){
      char c;
      for(i=11; fossil_isspace(zLine[i]); i++){}
      c = zLine[i];
      if( sqlite3_strlike("%close%", &zLine[11], 0)==0 ){
      if( c=='c' || c=='C' ){
        closeConnection = 1;
      }else if( c=='k' || c=='K' ){
      }else if( sqlite3_strlike("%keep-alive%", &zLine[11], 0)==0 ){
        closeConnection = 0;
      }
    }else if( ( rc==301 || rc==302 ) &&
    }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]);
        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, useLogin, maxRedirect);
      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( iLength<0 ){
    fossil_warning("server did not reply");
    goto write_err;
        }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( iRecvLen != iLength ){
    fossil_warning("response truncated: got %d bytes of %d", iRecvLen, iLength);
    goto write_err;
  }
  blob_resize(pReply, iLength);
    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++;
416
417
418
419
420
421
422












































































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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
  /*
  ** 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.

135
136
137
138
139
140
141
142

143
144
145
146
147
148
149
135
136
137
138
139
140
141

142
143
144
145
146
147
148
149







-
+







  }
}

/*
** Open a socket connection.  The identify of the server is determined
** by pUrlData
**
**    pUrlData->name       Name of the server.  Ex: www.fossil-scm.org
**    pUrlData->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;
194
195
196
197
198
199
200
201

202
203
204
205
206
207


208
209
210
211
212
213
214
194
195
196
197
198
199
200

201
202
203
204
205


206
207
208
209
210
211
212
213
214







-
+




-
-
+
+







  return rc;
}

/*
** Send content out over the open socket connection.
*/
size_t socket_send(void *NotUsed, const void *pContent, size_t N){
  size_t sent;
  ssize_t sent;
  size_t total = 0;
  while( N>0 ){
    sent = send(iSocket, pContent, N, 0);
    if( sent<=0 ) break;
    total += sent;
    N -= sent;
    total += (size_t)sent;
    N -= (size_t)sent;
    pContent = (void*)&((char*)pContent)[sent];
  }
  return total;
}

/*
** Receive content back from the open socket connection.
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
230
231
232
233
234
235
236
































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
    if( got<=0 ) break;
    total += (size_t)got;
    N -= (size_t)got;
    pContent = (void*)&((char*)pContent)[got];
  }
  return total;
}

/*
** Attempt to resolve pUrlData->name to an IP address and setup g.zIpAddr
** so rcvfrom gets populated. For hostnames with more than one IP (or
** if overridden in ~/.ssh/config) the rcvfrom may not match the host
** to which we connect.
*/
void socket_ssh_resolve_addr(UrlData *pUrlData){
  struct addrinfo *ai = 0;
  struct addrinfo hints;
  char zRemote[NI_MAXHOST];
  hints.ai_family = AF_UNSPEC;
  hints.ai_socktype = SOCK_STREAM;
  hints.ai_protocol = IPPROTO_TCP;
  if( getaddrinfo(pUrlData->name, NULL, &hints, &ai)==0
   && ai!=0
   && getnameinfo(ai->ai_addr, ai->ai_addrlen, zRemote,
                  sizeof(zRemote), 0, 0, NI_NUMERICHOST)==0 ){
    g.zIpAddr = mprintf("%s (%s)", zRemote, pUrlData->name);
  }
  if( ai ) freeaddrinfo(ai);
  if( g.zIpAddr==0 ){
    g.zIpAddr = mprintf("%s", pUrlData->name);
  }
}

Changes to src/http_ssl.c.

14
15
16
17
18
19
20
21
22





23
24
25
26
27
28

29
30
31
32
33
34

35
36
37
38
39
40
41
42
43
44
45

46
47
48
49





50













































































































51
52
53
54
55
56
57
14
15
16
17
18
19
20


21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

41
42
43
44
45
46
47
48

49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175







-
-
+
+
+
+
+






+






+

-








-
+




+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







**   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 identity
** of the server is held in global variables that are set by url_parse().
** 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 "http_ssl.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;    /* True after global initialization */
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;
82
83
84
85
86
87
88
89
90








































91
92
93
94

95
96
97
98





99
100
101
102
103
104
105
106

107
108












109
110
111
112





113
114





115
116
117

118
119
120

121

122
123
124
125


126
127
128



129
130


131
132
133
134
135
136
137
138
139
140
141
142
143

144
145
146

147
148
149
150
151
152
153


154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169

170
171
172

173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189


190
191
192
193
194
195
196
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251

252

253
254
255
256
257
258
259
260
261
262

263
264
265
266
267
268


269
270
271
272
273
274
275
276
277
278
279
280




281
282
283
284
285


286
287
288
289
290
291


292
293
294
295
296

297
298
299
300

301
302
303
304
305
306
307
308


309
310


311
312
313
314
315
316
317
318
319
320

321
322
323

324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348

349
350
351

352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368

369
370
371
372
373
374
375
376
377









+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



-
+
-



+
+
+
+
+


-





+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
-
-
+
+
+
+
+

-
-
+



+
-
+



-
+
+



+
+
+
-
-
+
+
-
-










-
+


-
+







+
+















-
+


-
+
















-
+
+







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.
*/
void ssl_global_init(void){
static void ssl_global_init_client(void){
  const char *zCaSetting = 0, *zCaFile = 0, *zCaDirectory = 0;
  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();
    ERR_load_BIO_strings();
    OpenSSL_add_all_algorithms();
    sslCtx = SSL_CTX_new(SSLv23_client_method());
    /* Disable SSLv2 and SSLv3 */
    SSL_CTX_set_options(sslCtx, SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3);

    /* Find the trust store */
    /* Set up acceptable CA root certificates */
    zCaSetting = db_get("ssl-ca-location", 0);
    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);
    if( zCaSetting==0 || zCaSetting[0]=='\0' ){
      /* CA location not specified, use platform's default certificate store */
      X509_STORE_set_default_paths(SSL_CTX_get_cert_store(sslCtx));
    }else{
          break;
        case 3:
          zFile = X509_get_default_cert_file();
          break;
        case 4:
      /* User has specified a CA location, make sure it exists and use it */
      switch( file_isdir(zCaSetting, ExtFILE) ){
          zFile = X509_get_default_cert_dir();
          break;
      }
      if( zFile==0 ) continue;
      switch( file_isdir(zFile, ExtFILE) ){
        case 0: { /* doesn't exist */
          fossil_panic("ssl-ca-location is set to '%s', "
              "but is not a file or directory", zCaSetting);
          zFile = 0;
          break;
        }
        case 1: { /* directory */
          zCaFile = 0;
          zCaDirectory = zCaSetting;
          zCaDirectory = zFile;
          break;
        }
        case 2: { /* file */
          zCaFile = zCaSetting;
          zCaFile = zFile;
          zCaDirectory = 0;
          break;
        }
      }
    }
    if( zFile==0 ){
      /* fossil_fatal("Cannot find a trust store"); */
      if( SSL_CTX_load_verify_locations(sslCtx, zCaFile, zCaDirectory)==0 ){
        fossil_panic("Failed to use CA root certificates from "
    }else if( SSL_CTX_load_verify_locations(sslCtx, zCaFile, zCaDirectory)==0 ){
      fossil_fatal("Cannot load CA root certificates from %s", zFile);
          "ssl-ca-location '%s'", zCaSetting);
      }
    }

    /* Load client SSL identity, preferring the filename specified on the
    ** command line */
    if( g.zSSLIdentity!=0 ){
      identityFile = g.zSSLIdentity;
    }else{
      identityFile = db_get("ssl-identity", 0);
    }
    if( identityFile!=0 && identityFile[0]!='\0' ){
      if( SSL_CTX_use_certificate_file(sslCtx,identityFile,SSL_FILETYPE_PEM)!=1
      if( SSL_CTX_use_certificate_chain_file(sslCtx,identityFile)!=1
       || SSL_CTX_use_PrivateKey_file(sslCtx,identityFile,SSL_FILETYPE_PEM)!=1
      ){
        fossil_panic("Could not load SSL identity from %s", identityFile);
        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 SSL connection.  If no connection is open,
** Close the currently open client SSL connection.  If no connection is open,
** this routine is a no-op.
*/
void ssl_close(void){
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);
  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));
222
223
224
225
226
227
228














229

230
231

232

233
234
235
236
237

238
239
240

241
242
243

244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263



264
265
266
267
268
269
270
271
272
273
274
275

276
277

278
279
280
281
282
283
284
285
286
287

288
289
290
291
292
293










294
295
296
297
298
299
300
301
302



303
304
305
306
307
308
309
310
311
312

313
314
315
316
317
318
319
320

321
322
323







324


325
326
327
328
329
330
331



332

333

334
335
336
337
338
339






340
341
342
343
344
345
346
347







348
349
350
351
352
353










354
355
356
357









358
359
360

361
362
363


364
365
366
367



368
369
370
371
372
373
374









375
376
377
378
379
380
381







382
383

384
385
386
387
388
389
390
391










392
393


394

395
396
397
398
399
400
401
402


403

404

405
406
407


408
409
410
411
412

413
414
415
416
417
418
419
420
421
422
423


424

425
426
427














428
429

430
431




432
433
434
435
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

























































































































































































































































































































403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423

424

425
426

427
428
429
430
431

432
433


434

435

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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-

+
-
+




-
+

-
-
+
-

-
+
-
-
-
-
-
-
-
-
-
-
-







-
-
+
+
+












+


+









-
+






+
+
+
+
+
+
+
+
+
+







-
-
+
+
+









-
+







-
+



+
+
+
+
+
+
+
-
+
+

-



-
-
+
+
+

+
-
+
-
-
-
-
-
-
+
+
+
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-

-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-
-
+
-
-
-
+
+

-
-
-
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+

-
+








+
+
+
+
+
+
+
+
+
+

-
+
+

+







-
+
+

+
-
+
-
-
-
+
+
-
-
-
-
-
+
-
-
-
-
-
-



-
-
+
+

+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-

+
-
-
+
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
-
-
+
-
-
+
-
-
-
-
-
-



-
+
+



















-
+
+


















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
  }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.
**
** Open an SSL connection.  The identify of the server is determined
*  The identify of the server is determined as follows:
** as follows:
**
**    pUrlData->name  Name of the server.  Ex: fossil-scm.org
**    g.url.name      Name of the server.  Ex: www.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(UrlData *pUrlData){
int ssl_open_client(UrlData *pUrlData){
  X509 *cert;
  int hasSavedCertificate = 0;
  int trusted = 0;
  const char *zRemoteHost;
  unsigned long e;

  ssl_global_init();
  ssl_global_init_client();

  /* Get certificate for current server from global config and
   * (if we have it in config) add it to certificate store.
   */
  cert = ssl_get_certificate(pUrlData, &trusted);
  if ( cert!=NULL ){
    X509_STORE_add_cert(SSL_CTX_get_cert_store(sslCtx), cert);
    X509_free(cert);
    hasSavedCertificate = 1;
  }

  if( pUrlData->useProxy ){
    int rc;
    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();
            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, (pUrlData->useProxy?pUrlData->hostname:pUrlData->name)) ){
  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();
         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();
    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();
    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( trusted<=0 && (e = SSL_get_verify_result(ssl)) != X509_V_OK ){
  if( !sslNoCertVerify && SSL_get_verify_result(ssl)!=X509_V_OK ){
    int x, desclen;
    char *desc, *prompt;
    const char *warning = "";
    Blob ans;
    char cReply;
    BIO *mem;
    unsigned char md[32];
    unsigned int mdLength = 31;
    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));
    mem = BIO_new(BIO_s_mem());
    zHash[0] = 0;
    X509_NAME_print_ex(mem, X509_get_subject_name(cert), 2, XN_FLAG_MULTILINE);
    BIO_puts(mem, "\n\nIssued By:\n\n");
    X509_NAME_print_ex(mem, X509_get_issuer_name(cert), 2, XN_FLAG_MULTILINE);
    BIO_puts(mem, "\n\nSHA1 Fingerprint:\n\n ");
    if(X509_digest(cert, EVP_sha1(), md, &mdLength)){
      int j;
                            /*  MMNNFFPPS */
#if OPENSSL_VERSION_NUMBER >= 0x010000000
    x = X509_digest(cert, EVP_sha256(), md, &mdLength);
#else
    x = X509_digest(cert, EVP_sha1(), md, &mdLength);
#endif
      for( j = 0; j < mdLength; ++j ) {
        BIO_printf(mem, " %02x", md[j]);
      }
    }
    BIO_write(mem, "", 1); /* nul-terminate mem buffer */
    BIO_get_mem_data(mem, &desc);

    if( hasSavedCertificate ){
    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;
      warning = "WARNING: Certificate doesn't match the "
                "saved certificate for this host!";
    }
    prompt = mprintf("\nSSL verification failed: %s\n"
        "Certificate received: \n\n%s\n\n%s\n"
        "Either:\n"

    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:    ");
        " * verify the certificate is correct using the "
        "SHA1 fingerprint above\n"
        " * use the global ssl-ca-location setting to specify your CA root\n"
        "   certificates list\n\n"
      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"
        "If you are not expecting this message, answer no and "
        "contact your server\nadministrator.\n\n"
        "Accept certificate for host %s (a=always/y/N)? ",
          "accept this cert and continue (y/N/fingerprint)? ",
        X509_verify_cert_error_string(e), desc, warning,
        pUrlData->useProxy?pUrlData->hostname:pUrlData->name);
    BIO_free(mem);
          pUrlData->name, desclen, desc);
      BIO_free(mem);

    prompt_user(prompt, &ans);
    free(prompt);
    cReply = blob_str(&ans)[0];
      prompt_user(prompt, &ans);
      free(prompt);
      cReply = blob_str(&ans)[0];
    blob_reset(&ans);
    if( cReply!='y' && cReply!='Y' && cReply!='a' && cReply!='A') {
      X509_free(cert);
      ssl_set_errmsg("SSL certificate declined");
      ssl_close();
      return 1;
    }
      if( cReply!='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;
      }
    if( cReply=='a' || cReply=='A') {
      if ( trusted==0 ){
        prompt_user("\nSave this certificate as fully trusted (a=always/N)? ",
                    &ans);
        cReply = blob_str(&ans)[0];
        trusted = ( cReply=='a' || cReply=='A' );
        blob_reset(&ans);
      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);
      }
      ssl_save_certificate(pUrlData, cert, trusted);
      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 = (const unsigned char *) BIO_ptr_ctrl(iBio,BIO_C_GET_CONNECT,2);
    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;
}

/*
** Save certificate to global config.
** Remember that the cert with the given hash is acceptable for
** use with pUrlData->name.
*/
LOCAL void ssl_remember_certificate_exception(
void ssl_save_certificate(UrlData *pUrlData, X509 *cert, int trusted){
  UrlData *pUrlData,
  BIO *mem;
  char *zCert, *zHost;

  const char *zHash
){
  mem = BIO_new(BIO_s_mem());
  PEM_write_bio_X509(mem, cert);
  BIO_write(mem, "", 1); /* nul-terminate mem buffer */
  BIO_get_mem_data(mem, &zCert);
  zHost = mprintf("cert:%s", pUrlData->useProxy?pUrlData->hostname:pUrlData->name);
  db_set_mprintf(zHash, 1, "cert:%s", pUrlData->name);
  db_set(zHost, zCert, 1);
  free(zHost);
  zHost = mprintf("trusted:%s", pUrlData->useProxy?pUrlData->hostname:pUrlData->name);
  db_set_int(zHost, trusted, 1);
  free(zHost);
  BIO_free(mem);
}

/*
** Get certificate for pUrlData->urlName from global config.
** Return NULL if no certificate found.
** Return true if the there exists a certificate exception for
** pUrlData->name that matches the hash.
*/
LOCAL int ssl_certificate_exception_exists(
X509 *ssl_get_certificate(UrlData *pUrlData, int *pTrusted){
  char *zHost, *zCert;
  BIO *mem;
  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;
}
  X509 *cert;

/*
  zHost = mprintf("cert:%s",
      pUrlData->useProxy ? pUrlData->hostname : pUrlData->name);
** Remember zHash as an acceptable certificate for this session only.
*/
LOCAL void ssl_one_time_exception(
  UrlData *pUrlData,
  zCert = db_get(zHost, NULL);
  free(zHost);
  if ( zCert==NULL )
    return NULL;

  if ( pTrusted!=0 ){
    zHost = mprintf("trusted:%s",
             pUrlData->useProxy ? pUrlData->hostname : pUrlData->name);
  const char *zHash
){
  fossil_free(sException.zHost);
  sException.zHost = fossil_strdup(pUrlData->name);
    *pTrusted = db_get_int(zHost, 0);
    free(zHost);
  fossil_free(sException.zHash);
  }

  sException.zHash = fossil_strdup(zHash);
  mem = BIO_new(BIO_s_mem());
  BIO_puts(mem, zCert);
  cert = PEM_read_bio_X509(mem, NULL, 0, NULL);
  free(zCert);
  BIO_free(mem);
  return cert;
}

/*
** Send content out over the SSL connection.
** 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 SSL connection.
** 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.

78
79
80
81
82
83
84
85

86
87

88
89
90
91
92
93
94
95
96
97
98


99
100

101









102
103
104
105
106
107
108
109
110
111
112
113

114

115
116

117
118
119
120
121
122
123

124
125
126

127
128
129

130

131
132



133
134





135

136
137
138

139
140

141
142
143


144
145

146
147
148
149
150
151
152
153
154
155
156
157

158
159
160
161
162
163
164
165
166
167
168
169
170
171


172
173

174
175
176

177
178
179
180




181
182

183
184
185
186

187
188
189
190
191
192
193
78
79
80
81
82
83
84

85
86

87
88
89
90
91
92
93
94
95
96


97
98
99

100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118

119
120
121
122

123


124
125



126

127
128
129
130

131
132
133

134
135
136


137
138
139
140
141
142
143
144
145
146

147
148
149

150
151

152
153


154
155
156

157
158
159
160
161
162
163
164
165
166
167
168

169
170
171
172
173
174
175
176
177
178
179
180
181


182
183
184

185
186
187

188
189



190
191
192
193


194

195
196

197
198
199
200
201
202
203
204







-
+

-
+









-
-
+
+

-
+

+
+
+
+
+
+
+
+
+








-



+
-
+
-
-
+

-
-
-

-

+


-
+


-
+

+
-
-
+
+
+


+
+
+
+
+
-
+


-
+

-
+

-
-
+
+

-
+











-
+












-
-
+
+

-
+


-
+

-
-
-
+
+
+
+
-
-
+
-


-
+








/*
** 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 *azSafe[] = { "*/fossil", "*/echo" };
  static const char *const azSafe[] = { "*/fossil", "*/fossil.exe", "*/echo" };
  int i;
  for(i=0; i<sizeof(azSafe)/sizeof(azSafe[0]); 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
*/
#ifdef _WIN32
static const char zDefaultSshCmd[] = "plink -ssh -T";
#if 0 /* was: defined(_WIN32).  Windows generally has ssh now. */
static const char zDefaultSshCmd[] = "plink -ssh";
#else
static const char zDefaultSshCmd[] = "ssh -e none -T";
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.
  */
  char *zSsh;        /* The base SSH command */
  Blob zCmd;         /* The SSH command */
  char *zHost;       /* The host name to contact */

  fossil_free(g.zIpAddr);
  socket_ssh_resolve_addr(pUrlData);
  g.zIpAddr = mprintf("%s", pUrlData->name);
  zSsh = db_get("ssh-command", zDefaultSshCmd);
  blob_init(&zCmd, zSsh, -1);
  transport_ssh_command(&zCmd);
  if( pUrlData->port!=pUrlData->dfltPort && pUrlData->port ){
#ifdef _WIN32
    blob_appendf(&zCmd, " -P %d", pUrlData->port);
#else
    blob_appendf(&zCmd, " -p %d", pUrlData->port);
#endif
  }
  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);
    blob_append_escaped_arg(&zCmd, zHost, 0);
    fossil_free(zHost);
  }else{
    blob_append_escaped_arg(&zCmd, pUrlData->name);
    blob_append_escaped_arg(&zCmd, pUrlData->name, 0);
  }
  if( (pUrlData->flags & URL_SSH_EXE)!=0
  if( !is_safe_fossil_command(pUrlData->fossil) ){
    fossil_panic("the ssh:// URL is asking to run an unsafe command [%s] on "
   && !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);
  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);
    blob_append_escaped_arg(&zCmd, pUrlData->path, 1);
  }else{
    fossil_panic("ssh:// URI does not specify a path to the repository");
    fossil_fatal("ssh:// URI does not specify a path to the repository");
  }
  if( g.fSshTrace ){
    fossil_print("%s\n", blob_str(&zCmd));  /* Show the whole SSH command */
  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);
  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: www.fossil-scm.org
**   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(pUrlData);
#ifdef FOSSIL_ENABLE_SSL
      rc = ssl_open_client(pUrlData);
      if( rc==0 ) transport.isOpen = 1;
      #else
#else
      socket_set_errmsg("HTTPS: Fossil has been compiled without SSL support");
      rc = 1;
      #endif
#endif
    }else if( pUrlData->isFile ){
      sqlite3_uint64 iRandId;
      sqlite3_randomness(sizeof(iRandId), &iRandId);
      transport.zOutFile = mprintf("%s-%llu-out.http",
      if( !db_looks_like_a_repository(pUrlData->name) ){
        fossil_fatal("not a fossil repository: \"%s\"", pUrlData->name);
      }
      transport.zOutFile = fossil_temp_filename();
                                       g.zRepositoryName, iRandId);
      transport.zInFile = mprintf("%s-%llu-in.http",
      transport.zInFile = fossil_temp_filename();
                                       g.zRepositoryName, iRandId);
      transport.pFile = fossil_fopen(transport.zOutFile, "wb");
      if( transport.pFile==0 ){
        fossil_panic("cannot output temporary file: %s", transport.zOutFile);
        fossil_fatal("cannot output temporary file: %s", transport.zOutFile);
      }
      transport.isOpen = 1;
    }else{
      rc = socket_open(pUrlData);
      if( rc==0 ) transport.isOpen = 1;
    }
  }
208
209
210
211
212
213
214
215

216
217
218
219
220
221
222
223
224
225


226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244

245
246
247
248
249
250
251
252

253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268

269
270
271
272
273
274
275


276
277

278
279
280
281
282
283
284
219
220
221
222
223
224
225

226
227
228
229
230
231
232
233
234


235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254

255
256
257
258
259
260
261
262

263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278

279
280
281
282
283
284


285
286
287
288
289
290
291
292
293
294
295
296







-
+








-
-
+
+


















-
+







-
+















-
+





-
-
+
+


+







      fclose(transport.pLog);
      transport.pLog = 0;
    }
    if( pUrlData->isSsh ){
      transport_ssh_close();
    }else if( pUrlData->isHttps ){
      #ifdef FOSSIL_ENABLE_SSL
      ssl_close();
      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);
      free(transport.zInFile);
      free(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
#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
#endif
  }else if( pUrlData->isFile ){
    fwrite(z, 1, n, transport.pFile);
  }else{
    int sent;
    while( n>0 ){
      sent = socket_send(0, z, n);
      /* printf("Sent %d of %d bytes\n", sent, n); fflush(stdout); */
      if( sent<=0 ) break;
      n -= sent;
    }
  }
}

/*
** This routine is called when the outbound message is complete and
** it is time to being receiving a reply.
** it is time to begin receiving a reply.
*/
void transport_flip(UrlData *pUrlData){
  if( pUrlData->isFile ){
    char *zCmd;
    fclose(transport.pFile);
    zCmd = mprintf("\"%s\" http --in \"%s\" --out \"%s\" --ipaddr 127.0.0.1"
                   " \"%s\" --localauth",
    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");
  }
}

/*
305
306
307
308
309
310
311
312

313
314
315
316
317
318
319
317
318
319
320
321
322
323

324
325
326
327
328
329
330
331







-
+








/*
** Read N bytes of content directly from the wire and write into
** the buffer.
*/
static int transport_fetch(UrlData *pUrlData, char *zBuf, int N){
  int got;
  if( sshIn ){
  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;

Changes to src/import.c.

24
25
26
27
28
29
30
31

32
33
34
35
36
37
38
24
25
26
27
28
29
30

31
32
33
34
35
36
37
38







-
+








#if INTERFACE
/*
** A single file change record.
*/
struct ImportFile {
  char *zName;           /* Name of a file */
  char *zUuid;           /* UUID of the 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

57
58
59
60
61
62
63
64

65
66
67
68
69
70
71
72

73
74
75
76
77
78
79
57
58
59
60
61
62
63

64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80







-
+








+







  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 UUID */
  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.
*/
141
142
143
144
145
146
147
148
149


150
151
152
153

154

155
156
157
158
159
160
161
162
163
164

165
166


167
168
169
170
171
172
173
142
143
144
145
146
147
148


149
150
151
152
153
154
155

156
157
158
159
160
161
162
163
164
165
166
167
168

169
170
171
172
173
174
175
176
177







-
-
+
+




+
-
+










+

-
+
+







}

/*
** 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 saveUuid is true, then pContent is a commit record.  Record its
** UUID in gg.zPrevCheckin.
** 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 saveUuid,            /* Save artifact hash in gg.zPrevCheckin */
  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, content) VALUES(:uuid, :size, :content)"
        "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);
185
186
187
188
189
190
191
192

193
194
195




196
197
198














199
200
201
202
203
204
205
206
207

208
209
210
211
212
213
214
215
216
217
218

219
220
221
222
223
224
225
226
227
228
229

230

231
232
233
234
235
236
237
189
190
191
192
193
194
195

196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226



227

228
229
230
231
232
233
234
235

236
237
238
239
240
241
242
243
244
245
246
247

248
249
250
251
252
253
254
255
256
257







-
+



+
+
+
+



+
+
+
+
+
+
+
+
+
+
+
+
+
+






-
-
-
+
-








-

+










-
+

+







    );
    db_multi_exec(
        "INSERT OR IGNORE INTO xmark(tname, trid, tuuid)"
        "VALUES(%B,%d,%B)",
        &hash, rid, &hash
    );
  }
  if( saveUuid ){
  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){
  Blob content;
  blob_init(&content, gg.aData, gg.nData);
  fast_insert_content(&content, gg.zMark, 0, 0);
  check_and_add_file(gg.zMark, 0);
  blob_reset(&content);
  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){
  Blob record, cksum;
  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, 1);
    fast_insert_content(&record, 0, 0, 0, 1);
    blob_reset(&cksum);
    blob_reset(&record);
  }
  import_reset(0);
}

/*
** Compare two ImportFile objects for sorting
*/
265
266
267
268
269
270
271

272



273
274
275
276
277
278

279
280

281
282

283




284
285
286
287
288
289
290
285
286
287
288
289
290
291
292

293
294
295
296
297
298
299
300

301
302

303
304

305
306
307
308
309
310
311
312
313
314
315
316
317







+
-
+
+
+





-
+

-
+

-
+

+
+
+
+







  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 ){
  if( !g.fQuiet ) fossil_print("%.10s\r", gg.zDate);
    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\n", 3);
      blob_append(&record, " x", 2);
    }else if( gg.aFile[i].isLink ){
      blob_append(&record, " l\n", 3);
      blob_append(&record, " l", 2);
    }else{
      blob_append(&record, "\n", 1);
      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);
320
321
322
323
324
325
326
327

328
329
330
331
332
333
334
335
336
337
338
339
340


341
342
343
344
345
346
347
348
349
350
351
352
353
354

355
356
357
358
359
360
361
347
348
349
350
351
352
353

354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379

380
381
382
383
384
385
386
387
388
389
390







-
+













+
+










-



+








  free(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, 1, 1);
  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(&record);
    blob_reset(&cksum);
  }

  blob_reset(&record);
  fossil_free(gg.zPrevBranch);
  gg.zPrevBranch = gg.zBranch;
  gg.zBranch = 0;
  import_reset(0);
}

/*
406
407
408
409
410
411
412
413

414
415
416
417
418
419
420
435
436
437
438
439
440
441

442
443
444
445
446
447
448
449







-
+







  }else{
    *pzIn = &z[i];
  }
  return z;
}

/*
** Convert a "mark" or "committish" into the UUID.
** 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;
}
515
516
517
518
519
520
521
522






523
524
525
526
527
528
529
544
545
546
547
548
549
550

551
552
553
554
555
556
557
558
559
560
561
562
563







-
+
+
+
+
+
+







  }
  zName[i] = 0;
}


static struct{
  const char *zMasterName;    /* Name of master branch */
  int authorFlag;             /* Use author as checkin committer */
  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){
565
566
567
568
569
570
571
572
573


574
575
576
577
578
579
580
599
600
601
602
603
604
605


606
607
608
609
610
611
612
613
614







-
-
+
+







      ** tag to the new commit.  However, if there are multiple instances
      ** of pattern B with the same TAGNAME, then only put the tag on the
      ** last commit that holds that tag.
      **
      ** None of the above is explained in the git-fast-export
      ** documentation.  We had to figure it out via trial and error.
      */
      for(i=5; i<strlen(zRefName) && zRefName[i]!='/'; i++){}
      gg.tagCommit = strncmp(&zRefName[5], "tags", 4)==0;  /* True for pattern B */
      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();
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
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







-
+




-
-
-
+
+
+




+
+
+
+




















+
-
+
+
+
+




-
+




+
+
+
+






-
+







      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_panic("short read: got %d of %d bytes", 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';
          /* 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';
      /*
      /* Lookup user by contact info. */
      ** 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. */
         * 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 = 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);
684
685
686
687
688
689
690
691

692
693




694


695
696
697
698
699
700
701
730
731
732
733
734
735
736

737
738
739
740
741
742
743

744
745
746
747
748
749
750
751
752







-
+


+
+
+
+
-
+
+







      dequote_git_filename(zName);
      i = 0;
      pFile = import_find_file(zName, &i, gg.nFile);
      if( pFile==0 ){
        pFile = import_add_file();
        pFile->zName = fossil_strdup(zName);
      }
      pFile->isExe = (fossil_strcmp(zPerm, "100755")==0);
      pFile->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->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);
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
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







-
-
+
+

-
+



















-
-
+
+

-
+










-







-
+
+
+
+
+
+
+
+
+
+
+
+
+







      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( strlen(pFile->zName)>nFrom ){
          pNew->zName = mprintf("%s%s", zTo, pFile->zName[nFrom]);
        if( (int)strlen(pFile->zName)>nFrom ){
          pNew->zName = mprintf("%s%s", zTo, pFile->zName+nFrom);
        }else{
          pNew->zName = fossil_strdup(pFile->zName);
          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( strlen(pFile->zName)>nFrom ){
          pNew->zName = mprintf("%s%s", zTo, pFile->zName[nFrom]);
        if( (int)strlen(pFile->zName)>nFrom ){
          pNew->zName = mprintf("%s%s", zTo, pFile->zName+nFrom);
        }else{
          pNew->zName = fossil_strdup(pFile->zName);
          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));
      }
      fossil_fatal("cannot handle R records, use --full-tree");
    }else
    if( strncmp(zLine, "deleteall", 9)==0 ){
      gg.fromLoaded = 1;
    }else
    if( strncmp(zLine, "N ", 2)==0 ){
      /* No-op */
    }else

    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;
790
791
792
793
794
795
796
797

798
799
800
801
802
803
804
852
853
854
855
856
857
858

859
860
861
862
863
864
865
866







-
+







  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 checkin */
  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;
953
954
955
956
957
958
959
960

961
962
963
964
965
966
967
1015
1016
1017
1018
1019
1020
1021

1022
1023
1024
1025
1026
1027
1028
1029







-
+







  svn_read_props(pIn, rec);
  blob_zero(&rec->content);
  zLen = svn_find_header(*rec, "Text-content-length");
  if( zLen ){
    rec->contentFlag = 1;
    nLen = atoi(zLen);
    blob_read_from_channel(&rec->content, pIn, nLen);
    if( blob_size(&rec->content)!=nLen ){
    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;
  }
1218
1219
1220
1221
1222
1223
1224
1225

1226
1227
1228
1229
1230
1231
1232
1280
1281
1282
1283
1284
1285
1286

1287
1288
1289
1290
1291
1292
1293
1294







-
+







  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;
      int nIgn = strlen(zIgn);
      unsigned nIgn = strlen(zIgn);
      if( strncmp(zPath, zIgn, nIgn) == 0
       && ( nPath == nIgn || (nPath > nIgn && zPath[nIgn] == '/')) ){
        return 0;
      }
    }
  }
  *type = SVN_UNKNOWN;
1344
1345
1346
1347
1348
1349
1350
1351


1352
1353
1354
1355
1356
1357
1358
1406
1407
1408
1409
1410
1411
1412

1413
1414
1415
1416
1417
1418
1419
1420
1421







-
+
+







    "   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"
    " 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)"
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
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







-
+

















+
+



















-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+



-
+
+









+
+
+
+
+
+
+
+
+











+







  db_finalize(&cpyPath);
  db_finalize(&cpyRoot);
  db_finalize(&revSrc);
  fossil_print(" Done!\n");
}

/*
** COMMAND: import
** 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
**   -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.
** 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;
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
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







+






+
+
+
+
+
+
+
+
+
+
+
+
+
+



















+



-
+


+







    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, 0);
    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)"
       ");"
1820
1821
1822
1823
1824
1825
1826
1827


1828
1829

1830
1831
1832
1833
1834
1835
1836
1914
1915
1916
1917
1918
1919
1920

1921
1922
1923

1924
1925
1926
1927
1928
1929
1930
1931







-
+
+

-
+







    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 ids (UUIDs - the 40-byte hex SHA1 hash of artifacts).
    ** into artifact hashes.
    **
    ** Given any valid fast-import symbol, the corresponding fossil rid and
    ** uuid can found by searching against the xmark.tname field.
    ** 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
1854
1855
1856
1857
1858
1859
1860





















1861
1862
1863
1864
1865
1866

1867
1868
1869
1870
1871
1872
1873
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





-
+







      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, 1);
      fast_insert_content(&record, 0, 0, 0, 1);
      import_reset(0);
    }
    db_finalize(&q);
    if( markfile_out ){
      int rid;
      Stmt q_marks;
      FILE *f;
1900
1901
1902
1903
1904
1905
1906
1907

1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920

1921
1922
2016
2017
2018
2019
2020
2021
2022

2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039







-
+













+


  db_end_transaction(0);
  fossil_print("                               \r");
  if( omitRebuild ){
    omitVacuum = 1;
  }else{
    db_begin_transaction();
    fossil_print("Rebuilding repository meta-data...\n");
    rebuild_db(0, 1, !incrFlag);
    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.

42
43
44
45
46
47
48
49

50
51
52
53
54
55
56
57

58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73

74
75
76
77
78
79
80
42
43
44
45
46
47
48

49
50
51
52
53
54
55
56

57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72

73
74
75
76
77
78
79
80







-
+







-
+















-
+







  return zTags;
}


/*
** Print common information about a particular record.
**
**     *  The UUID
**     *  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 *zUuidName,     /* Name of the UUID */
  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", zUuidName, zUuid, zDate ? zDate : "");
    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
115
116
117
118
119
120
121
122

123
124
125
126
127
128
129
115
116
117
118
119
120
121

122
123
124
125
126
127
128
129







-
+







  zTags = info_tags_of_checkin(rid, 0);
  if( zTags && zTags[0] ){
    fossil_print("tags:         %s\n", zTags);
  }
  free(zTags);
  if( zComment ){
    fossil_print("comment:      ");
    comment_print(zComment, 0, 14, -1, g.comFmtFlags);
    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.
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185

186
187

188
189
190

191
192
193
194

195
196
197

198
199
200
201
202
203
204
205
206

207
208
209
210
211
212
213
214
215
216
217
218
219

220
221
222
223
224
225






226
227
228
229

230


231
232
233

234
235
236
237
238
239
240







241















242
243
244
245
246
247
248

249
250
251
252
253
254

255
256

257
258





259
260

261
262
263


264
265
266
267
268

269
270

271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294

295
296
297
298
299


300
301
302
303
304
305
306
307
308
309
310


311
312
313
314
315
316
317
318
319
320
321













322
323
324
325
326

327
328
329
330
331
332
333
334
335
336

337
338
339
340








341
342
343
344
345
346
347
348
349
350
351
352

353
354
355
356
357

358
359
360

361
362

363
364
365
366
367
368
369
370
371
372


373
374
375
376

377
378
379
380


381
382
383
384
385
386
387
388
389
390

391
392
393
394
395

396
397
398
399
400
401
402
403
165
166
167
168
169
170
171

172
173
174
175
176
177
178
179
180
181
182
183

184
185

186
187
188

189
190
191


192
193
194

195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217

218
219
220
221



222
223
224
225
226
227
228
229
230
231
232

233
234
235
236
237
238







239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267

268
269
270
271
272
273

274
275

276
277
278
279
280
281
282
283
284
285
286
287


288
289



290

291
292

293
























294





295
296











297
298











299
300
301
302
303
304
305
306
307
308
309
310
311





312










313
314
315


316
317
318
319
320
321
322
323
324

325
326
327
328
329
330
331
332
333

334

335
336
337

338
339
340
341
342
343
344
345
346
347
348
349
350
351
352



353
354

355


356



357
358
359
360
361

362
363
364
365
366
367
368
369
370
371
372
373

374

375
376
377
378
379
380
381







-












-
+

-
+


-
+


-
-
+


-
+









+












-
+



-
-
-
+
+
+
+
+
+




+
-
+
+



+
-
-
-
-
-
-
-
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+






-
+





-
+

-
+


+
+
+
+
+


+

-
-
+
+
-
-
-

-
+

-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
+


-
-
+
+
+
+
+
+
+
+

-









-
+
-



-
+



+


+







-
-
-
+
+
-

-
-
+
-
-
-

+
+


-







+




-
+
-







  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
** known the check-out locations for that repository and all URLs used
** 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 a object within the repository.
** the argument is the name of an object within the repository.
**
** Use the "finfo" command to get information about a specific
** file in a checkout.
** file in a check-out.
**
** Options:
**
**    -R|--repository FILE       Extract info from repository FILE
**    -R|--repository REPO       Extract info from repository REPO
**    -v|--verbose               Show extra information about repositories
**
** See also: annotate, artifact, finfo, timeline
** 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(0,0);
  db_find_and_open_repository(OPEN_OK_NOT_FOUND,0);
  verify_all_options();
  if( g.argc==2 ){
    int vid;
         /* 012345678901234 */
    db_record_repository_filename(0);
    fossil_print("project-name: %s\n", db_get("project-name", "<unnamed>"));
    if( g.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 ){
    if( verboseFlag ) extraRepoInfo();
      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",
      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, "uuid:", 1, 1);
    show_common_info(rid, "hash:", 1, 1);
  }
}

/*
** Show the context graph (immediate parents and children) for
** check-in rid.
** check-in rid and rid2
*/
void render_checkin_context(int rid, int parentsOnly){
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;"
    "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 pid FROM plink WHERE cid=%d;",
     rid, rid
  );
  if( !parentsOnly ){
  for(i=0; i<n; i++){
    db_multi_exec(
      "INSERT OR IGNORE INTO ok SELECT cid FROM plink WHERE pid=%d;", rid
      "INSERT OR IGNORE INTO ok VALUES(%d);"
    );
  }
  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, rid, 0);
  db_finalize(&q);
}

/*
** Show a graph 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;
  zGlob = mprintf("%.5s*", zUuid);
  db_multi_exec(
     "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY);"
     "DELETE FROM ok;"
     "INSERT OR IGNORE INTO ok"
      "INSERT OR IGNORE INTO ok SELECT pid FROM plink WHERE cid=%d;",
     " SELECT srcid FROM backlink"
     "  WHERE target GLOB %Q"
     "    AND %Q GLOB (target || '*');",
     zGlob, zUuid
  );
      rx[i], rx[i]
    );
  if( !db_exists("SELECT 1 FROM ok") ) return;
  if( zLabel ) cgi_printf("%s", zLabel);
  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);
  db_finalize(&q);
}

  }
  if( !parentsOnly ){
/*
** WEBPAGE: test-backlinks
**
** 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;

    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]
        );
      }
  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_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);
  www_print_timeline(&q,
         mFlags
         |TIMELINE_GRAPH
         |TIMELINE_FILLGAPS
         |TIMELINE_NOSCROLL
         |TIMELINE_XMERGE
         |TIMELINE_CHPICK,
       0, 0, 0, rid, rid2, 0);
  db_finalize(&q);
  style_footer();
}


/*
** 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 */
  u64 diffFlags,        /* Diff formatting flags */
  DiffConfig *pCfg      /* The diff configuration */
  ReCompiled *pRe       /* Only show change matching this regex */
){
  int fromid;
  int toid;
  Blob from, to, out;
  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);
  }
  blob_zero(&out);
  if( diffFlags & DIFF_SIDEBYSIDE ){
    text_diff(&from, &to, &out, pRe, diffFlags | DIFF_HTML | DIFF_NOTTOOBIG);
  if( pCfg->diffFlags & DIFF_SIDEBYSIDE ){
    pCfg->diffFlags |= DIFF_HTML | DIFF_NOTTOOBIG;
    @ %s(blob_str(&out))
  }else{
    text_diff(&from, &to, &out, pRe,
           diffFlags | DIFF_LINENO | DIFF_HTML | DIFF_NOTTOOBIG);
    pCfg->diffFlags |= DIFF_LINENO | DIFF_HTML | DIFF_NOTTOOBIG;
    @ <pre class="udiff">
    @ %s(blob_str(&out))
    @ </pre>
  }
  text_diff(&from, &to, cgi_output_blob(), pCfg);
  pCfg->zLeftHash = 0;
  blob_reset(&from);
  blob_reset(&to);
  blob_reset(&out);
}

/*
** 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. */
  u64 diffFlags,        /* Flags for text_diff().  Zero to omit diffs */
  DiffConfig *pCfg,     /* Flags for text_diff() or NULL to omit all */
  ReCompiled *pRe,      /* Only show diffs that match this regex, if not NULL */
  int mperm             /* executable or symlink permission for zNew */
){
  @ <p>
  if( !g.perm.Hyperlink ){
    if( zNew==0 ){
      @ Deleted %h(zName).
    }else if( zOld==0 ){
411
412
413
414
415
416
417
418
419


420
421
422
423









424
425
426





427
428
429
430




431
432


433
434
435
436
437
438
439
440
441
442
443
444


445
446
447


448
449
450


451
452
453
454
455
456
457
458
459
460
461
462
463
464



465
466
467
468
469
470
471

472
473
474
475
476
477



478
479
480
481

482
483

484
485
486

487
488
489
490

491
492
493
494




495
496
497
498
499
500
501
502
503
504
505
506
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
389
390
391
392
393
394
395


396
397
398
399
400
401
402
403
404
405
406
407
408
409
410



411
412
413
414
415
416
417


418
419
420
421
422

423
424
425
426
427
428
429
430
431
432
433
434


435
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







-
-
+
+




+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+


-
-
+
+
+
+

-
+
+










-
-
+
+

-
-
+
+

-
-
+
+











-
-
-
+
+
+






-
+



-
-
-
+
+
+
-
-
-
-
+
-
-
+
-
-
-
+


-
-
+


-
-
+
+
+
+

-

















+






-
-
+
+


+


+
+
-
+







        @ %h(zName) became a symlink.
      }else{
        @ %h(zName) became a regular file.
      }
    }else{
      @ Changes to %h(zName).
    }
    if( diffFlags ){
      append_diff(zOld, zNew, diffFlags, pRe);
    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",zName,zNew))%h(zName)</a>
        @ from %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a>
        @ to %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>.
          @ 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",zOldName,zOld))%h(zOldName)</a>
        @ to %z(href("%R/finfo?name=%T&m=%!S",zName,zNew))%h(zName)</a>.
        @ 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",zName,zNew))%h(zName)</a> became
        @ %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",zName,zOld))%h(zName)</a>
      @ version %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a>.
      @ 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",zName,zNew))%h(zName)</a>
      @ version %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>.
      @ 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( diffFlags ){
      append_diff(zOld, zNew, diffFlags, pRe);
    if( pCfg ){
      append_diff(zOld, zNew, pCfg);
    }else if( zOld && zNew && fossil_strcmp(zOld,zNew)!=0 ){
      @ &nbsp;&nbsp;
      @ %z(href("%R/fdiff?v1=%!S&v2=%!S",zOld,zNew))[diff]</a>
    }
  }
  @ </p>
}

/*
** Generate javascript to enhance HTML diffs.
*/
void append_diff_javascript(int sideBySide){
  if( !sideBySide ) return;
  style_load_one_js_file("sbsdiff.js");
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.
*/
u64 construct_diff_flags(int diffType){
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( diffType==2 ) diffFlags = DIFF_SIDEBYSIDE;
    if( P_NoBot("w") )  diffFlags |= DIFF_IGNORE_ALLWS;
    if( PD_NoBot("noopt",0)!=0 ) diffFlags |= DIFF_NOOPT;
      /* "dw" query parameter determines width of each column */
      x = atoi(PD("dw","80"))*(DIFF_CONTEXT_MASK+1);
      if( x<0 || x>DIFF_WIDTH_MASK ) x = DIFF_WIDTH_MASK;
      diffFlags += x;
    diffFlags |= DIFF_STRIP_EOLCR;
    }

    diff_config_init(pCfg, diffFlags);
    if( P("w") ){
      diffFlags |= DIFF_IGNORE_ALLWS;
    }

    /* "dc" query parameter determines lines of context */
    x = atoi(PD("dc","7"));
    if( x<0 || x>DIFF_CONTEXT_MASK ) x = DIFF_CONTEXT_MASK;
    diffFlags += x;
    if( x>0 ) pCfg->nContext = x;

    /* The "noopt" parameter disables diff optimization */
    if( PD("noopt",0)!=0 ) diffFlags |= DIFF_NOOPT;
    diffFlags |= DIFF_STRIP_EOLCR;
    return pCfg;
  }else{
    diff_config_init(pCfg, 0);
    return 0;
  }
  return diffFlags;
}

/*
** 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(g.argv[2])
    style_footer();
    @ 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 Check-In \
  @ <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"
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
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







-
+










-
+







      @ <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_uuid(zOrigUuid);
        hyperlink_to_version(zOrigUuid);
      }else{
        @ propagates to descendants
      }
    }
    if( zSrcUuid && zSrcUuid[0] ){
      if( tagtype==0 ){
        @ by
      }else{
        @ added by
      }
      hyperlink_to_uuid(zSrcUuid);
      hyperlink_to_version(zSrcUuid);
      @ on
      hyperlink_to_date(zDate,0);
    }
    @ </li>
  }
  db_finalize(&q);
  if( cnt ){
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
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







+









+





-
+

-
+





-
-
+
+

-
-
-
+
+
+

-
-
+
+
-






-

-
-
+
+


-
+


+
+



+




-
-
+
+



















+
-
+
-
-
+
+









+
+
+

+















-
+
+







     "  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, rid, 0);
                     0, 0, 0, rid, 0, 0);
  db_finalize(&q);
  style_footer();
  style_finish_page();
}

/*
** WEBPAGE: vinfo
** WEBPAGE: ci
** URL:  /ci?name=ARTIFACTID
** URL:  /vinfo?name=ARTIFACTID
** URL:  /ci/ARTIFACTID
**  OR:  /ci?name=ARTIFACTID
**
** Display information about a particular check-in.
**
** We also jump here from /info if the name is a check-in
** 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.
**
** If the /ci and /vinfo pages used to differ in their default
** diff settings, but now diff settings persist with a cookie and
** 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.
** so /ci and /vinfo behave the same.
*/
void ci_page(void){
  Stmt q1, q2, q3;
  int rid;
  int isLeaf;
  int diffType;        /* 0: no diff,  1: unified,  2: side-by-side */
  u64 diffFlags;       /* Flag parameter for text_diff() */
  const char *zName;   /* Name of the check-in to be displayed */
  const char *zUuid;   /* UUID of zName */
  const char *zParent; /* UUID of the parent check-in (if any) */
  const char *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 *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(g.argv[2])
    style_footer();
    @ 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);
  

  cookie_link_parameter("diff","diff","2");
  diffType = atoi(PD("diff","2"));
  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">Overview</div>
    @ <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);
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
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







+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+












-
+
+

-
+


















-
+
+








+


-
+
+



+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



+
+
+
-
+











+
+





+
+
+
+
+
-
-
-
-
+
+
+
+
+
+

-
-
+
+
+







    }
    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>",
      @  | %z(href("%R/timeline?r=%T&unhide",zTagName))%h(zTagName)</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>%s(zUuid)
    @ <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))
      @  (Record ID: %d(rid))
    }
    @ </td></tr>
    @ <tr><th>User&nbsp;&amp;&nbsp;Date:</th><td>
    hyperlink_to_user(zUser,zDate," on ");
    hyperlink_to_date(zDate, "</td></tr>");
    if( zEComment ){
      @ <tr><th>Original&nbsp;Comment:</th>
      @     <td class="infoComment">%!W(zComment)</td></tr>
    }
    if( fossil_strcmp(zDate, zOrigDate)!=0
     || fossil_strcmp(zOrigUser, zUser)!=0
    ){
      @ <tr><th>Original&nbsp;User&nbsp;&amp;&nbsp;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)"
         "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&nbsp;From:</th>
        @ <td>%h(zUser) @ %h(zIpAddr) on %s(zDate)</td></tr>
        @ <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&nbsp;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&nbsp;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&nbsp;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/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\">References</div>\n");
  @ <div class="section">Context</div>
  render_checkin_context(rid, 0);
  @ <div class="section">Changes</div>
  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">
  diffFlags = construct_diff_flags(diffType);
  zW = (diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
  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&nbsp;Diffs</a>
  }
  if( diffType!=1 ){
    @ %z(chref("button","%R/%s/%T?diff=1%s",zPage,zName,zW))\
    @ Unified&nbsp;Diffs</a>
843
844
845
846
847
848
849
850

851
852
853
854
855
856
857
921
922
923
924
925
926
927

928
929
930
931
932
933
934
935







-
+







  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>
  @ </div>
  if( pRe ){
    @ <p><b>Only differences that match regular expression "%h(zRe)"
    @ are shown.</b></p>
  }
  db_prepare(&q3,
    "SELECT name,"
    "       mperm,"
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
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







-
+
+


+
-
+
-
-
+




-
+











+
+



+




-
+



















-
+


+


-
-
-
-
+
+
+
+










-
+
+
+












-
+
+



+
+
+
+
+
+







-
+

-
+








+



+
-
+







  );
  while( db_step(&q3)==SQLITE_ROW ){
    const char *zName = db_column_text(&q3,0);
    int mperm = db_column_int(&q3, 1);
    const char *zOld = db_column_text(&q3,2);
    const char *zNew = db_column_text(&q3,3);
    const char *zOldName = db_column_text(&q3, 4);
    append_file_change_line(zName, zOld, zNew, zOldName, diffFlags,pRe,mperm);
    append_file_change_line(zUuid, zName, zOld, zNew, zOldName,
                            pCfg,mperm);
  }
  db_finalize(&q3);
  @ </div>
  append_diff_javascript(diffType==2);
  append_diff_javascript(diffType);
  cookie_render();
  style_footer();
  style_finish_page();
}

/*
** WEBPAGE: winfo
** URL:  /winfo?name=UUID
** 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_footer();
    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(rid);
      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)", pWiki->rDate);
  style_submenu_element("Raw", "artifact/%s", zUuid);
  style_submenu_element("History", "whistory?name=%t", pWiki->zWikiTitle);
  style_submenu_element("Page", "wiki?name=%t", pWiki->zWikiTitle);
  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&nbsp;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&nbsp;Name:</th><td>%h(pWiki->zWikiTitle)</td></tr>
  @ <tr><th>Page&nbsp;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&nbsp;User:</th><td>
  hyperlink_to_user(pWiki->zUser, zDate, "</td></tr>");
  if( pWiki->zMimetype ){
    @ <tr><th>Mimetype:</th><td>%h(pWiki->zMimetype)</td></tr>
  }
  if( pWiki->nParent>0 ){
    int i;
    @ <tr><th>Parent%s(pWiki->nParent==1?"":"s"):</th><td>
    for(i=0; i<pWiki->nParent; i++){
      char *zParent = pWiki->azParent[i];
      @ %z(href("info/%!S",zParent))%s(zParent)</a>
      @ %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 />
    @ Delete this change</label><br>
    @ <label><input type="radio" name="modaction" value="approve">
    @ Approve this change</label><br />
    @ 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_footer();
  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){
1000
1001
1002
1003
1004
1005
1006

1007
1008
1009
1010
1011
1012
1013
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108







+







  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),"
1026
1027
1028
1029
1030
1031
1032
1033

1034
1035
1036
1037
1038
1039
1040
1121
1122
1123
1124
1125
1126
1127

1128
1129
1130
1131
1132
1133
1134
1135







-
+







    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_uuid(zUuid);
    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 ){
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
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







+



















-
+
+






-






-
-
+
+

+
+
+
+
+



-
-
+
+
+
-



-
-
+
+
+
+
+

+
+
+
+
+
+






+
+
+
+
+
+
+


+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+
-
-


-
+
-
-
-


-
+
-
-
-

+
-
+
-
-
+

-
+
-

-
+
+




+
+
+
-
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
-







-
+

+





+











-
-
+
+




-
-
+
+









-
+

-
+








-
-
+
+







    }
    @ 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 */
  u64 diffFlags = 0;
  Manifest *pFrom, *pTo;
  ManifestFile *pFileFrom, *pFileTo;
  const char *zBranch;
  const char *zFrom;
  const char *zTo;
  const char *zRe;
  const char *zW;
  const char *zGlob;
  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();
  cookie_link_parameter("diff","diff","2");
  diffType = atoi(PD("diff","2"));
  fossil_nice_default();
  blob_init(&qp, 0, 0);
  diffType = preferred_diff_type();
  cookie_render();
  zRe = P("regex");
  if( zRe ) re_compile(&pRe, zRe, 0);
  zBranch = P("branch");
  if( zBranch && zBranch[0] ){
    cgi_replace_parameter("from", mprintf("root:%s", zBranch));
  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){
    zGlob = NULL;
  }
  diffFlags = construct_diff_flags(diffType);
  zW = (diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
  style_submenu_element("Path", "%R/timeline?me=%T&you=%T", zFrom, zTo);
    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?from=%T&to=%T&diff=0%s%T%s",
    style_submenu_element("Hide Diff", "%R/vdiff?diff=0&%b", &qp);
                          zFrom, zTo,
                          zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW);
  }
  if( diffType!=2 ){
    style_submenu_element("Side-by-Side Diff",
    style_submenu_element("Side-by-Side Diff", "%R/vdiff?diff=2&%b", &qp);
                          "%R/vdiff?from=%T&to=%T&diff=2%s%T%s",
                          zFrom, zTo,
                          zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW);
  }
  if( diffType!=1 ) {
    style_submenu_element("Unified Diff",
    style_submenu_element("Unified Diff", "%R/vdiff?diff=1&%b", &qp);
                          "%R/vdiff?from=%T&to=%T&diff=1%s%T%s",
                          zFrom, zTo,
                          zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW);
  }
  if( zBranch==0 ){
  style_submenu_element("Invert",
    style_submenu_element("Invert","%R/vdiff?diff=%d&inv&%b", diffType, &qp);
                        "%R/vdiff?from=%T&to=%T&%s%T%s", zTo, zFrom,
                        zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW);
  }
  if( zGlob ){
    style_submenu_element("Clear glob",
    style_submenu_element("Clear glob", "%R/vdiff?diff=%d&%b", diffType, &qp);
                          "%R/vdiff?from=%T&to=%T&%s", zFrom, zTo, zW);
  }else{
    style_submenu_element("Patch", "%R/vpatch?from=%T&to=%T%s", zFrom, zTo, zW);
    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");
    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:</h2><blockquote>
    checkin_description(ridFrom);
    @ </blockquote><h2>To:</h2><blockquote>
    checkin_description(ridTo);
      @ <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);
    @ </blockquote>
    if( pRe ){
      @ <p><b>Only differences that match regular expression "%h(zRe)"
      @ are shown.</b></p>
    }
    if( zGlob ){
      @ <p><b>Only files matching the glob "%h(zGlob)" are shown.</b></p>
    }
    @<hr /><p>
    @<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(pFileFrom->zName,
                                pFileFrom->zUuid, 0, 0, diffFlags, pRe, 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(pFileTo->zName,
                                0, pFileTo->zUuid, 0, diffFlags, pRe,
        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(pFileFrom->zName,
        append_file_change_line(zFrom, pFileFrom->zName,
                                pFileFrom->zUuid,
                                pFileTo->zUuid, 0, diffFlags, pRe,
                                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==2);
  style_footer();
  append_diff_javascript(diffType);
  style_finish_page();
}

#if INTERFACE
/*
** Possible return values from object_description()
*/
#define OBJTYPE_CHECKIN    0x0001
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
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







-
+
+




-
-
-
-
-
-
-
-
-
-
-
-


-
+

+
-
+








+







#define OBJTYPE_EXE        0x0100
#define OBJTYPE_FORUM      0x0200

/*
** Possible flags for the second parameter to
** object_description()
*/
#define OBJDESC_DETAIL      0x0001   /* more detail */
#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.
**
** If the object is a file then mention:
**
**     * It's artifact ID
**     * All its filenames
**     * The check-in it was part of, with times and users
**
** If the object is a manifest, then mention:
**
**     * It's artifact ID
**     * date of check-in
**     * Comment & user
*/
int object_description(
  int rid,                 /* The artifact ID */
  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 an appropriate download name */
  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"
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
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







+



















+
+
+
-
+
+

-
+
+










-
+


-
+














-
+
+
+
+







    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&m=%!S",zName,zUuid))%h(zName)</a>
      @ %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,"");
      @ &mdash; part of check-in
      hyperlink_to_uuid(zVers);
      hyperlink_to_version(zVers);
    }else{
      @ &mdash; part of check-in
      hyperlink_to_uuid(zVers);
      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>
    }
    @ &mdash; %!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?n=all&uf=%!S",zUuid))[check-ins&nbsp;using]</a>
      @ %z(href("%R/timeline?uf=%!S",zUuid))[check-ins&nbsp;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 ){
1426
1427
1428
1429
1430
1431
1432
1433

1434
1435
1436
1437
1438
1439
1440
1580
1581
1582
1583
1584
1585
1586

1587
1588
1589
1590
1591
1592
1593
1594







-
+







      }else if( zType[0]=='f' ){
        objType |= OBJTYPE_FORUM;
        @ Forum post
      }else{
        @ Tag referencing
      }
      if( zType[0]!='e' || eventTagId == 0){
        hyperlink_to_uuid(zUuid);
        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);
      }
1458
1459
1460
1461
1462
1463
1464
1465

1466
1467
1468
1469
1470
1471
1472
1612
1613
1614
1615
1616
1617
1618

1619
1620
1621
1622
1623
1624
1625
1626







-
+







    /* 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_uuid(zTarget) ){
    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)]
1514
1515
1516
1517
1518
1519
1520






































1521



1522
1523
1524

1525
1526
1527
1528
1529
1530
1531
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+


-
+







      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=UUID&v2=UUID
** 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
**
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
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







-


+



-
-
+
+
-

-
-
+
+







  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;
  u64 diffFlags;
  u32 objdescFlags = 0;
  int verbose = PB("verbose");
  DiffConfig DCfg;

  login_check_credentials();
  if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
  cookie_link_parameter("diff","diff","2");
  diffType = atoi(PD("diff","2"));
  diff_config_init(&DCfg, 0);
  diffType = preferred_diff_type();
  cookie_render();
  if( P("from") && P("to") ){
    v1 = artifact_from_ci_and_filename(0, "from");
    v2 = artifact_from_ci_and_filename(0, "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
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
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







+




+


-
+


+
-
+







-
+
+

+




















-
+

-
+




+

-
-
+
+

-
+




-
+


+
+
+
+
+






-
-

+
+
-
-
+
+






+


+




+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
-
-
-
-
+
+
+
+
+

-
-
-
+
+
+

+
-
-
-
+
+
+
+
+
+

+

+
+
+







        "%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");
    diffFlags = 4;
    DCfg.diffFlags = DIFF_VERBOSE;
    content_get(v1, &c1);
    content_get(v2, &c2);
    DCfg.pRe = pRe;
    text_diff(&c1, &c2, pOut, pRe, diffFlags);
    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);
  diffFlags = construct_diff_flags(diffType) | DIFF_HTML;
  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);
    object_description(v1, objdescFlags,0, 0);
    @ <h2>To Artifact %z(href("%R/artifact/%!S",zV2))[%S(zV2)]</a>:</h2>
    object_description(v2, objdescFlags, 0);
    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, diffFlags, pRe);
  @ <hr>
  append_diff(zV1, zV2, &DCfg);
  append_diff_javascript(diffType);
  style_footer();
  style_finish_page();
}

/*
** WEBPAGE: raw
** URL: /raw?name=ARTIFACTID&m=TYPE
** 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;
  const char *zMime;
  Blob content;

  (void)P("at")/*for cgi_check_for_malice()*/;
  (void)P("m");
  if( P("ci") && P("filename") ){
    rid = artifact_from_ci_and_filename(0, 0);
  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()*/;
  zMime = P("m");
  (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 ){
    char *zFName = db_text(0, "SELECT filename.name FROM mlink, filename"
                              " WHERE mlink.fid=%d"
                              "   AND filename.fnid=mlink.fnid", rid);
    if( !zFName ){
      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 */
      zFName = db_text(0, "SELECT attachment.filename FROM attachment, blob"
                          " WHERE blob.rid=%d"
                          "   AND attachment.src=blob.uuid", rid);
      zFN = db_text(0, "SELECT attachment.filename FROM attachment, blob"
                       " WHERE blob.rid=%d"
                       "   AND attachment.src=blob.uuid", rid);
    }
    if( zFN ){
    if( zFName ) zMime = mimetype_from_name(zFName);
    if( zMime==0 ) zMime = "application/x-fossil-artifact";
  }
      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){
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
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







+
-
+
-


-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+


-
+


-
-
+
+







      }else{
        zLine[k+1] = ' ';
        zLine[k+2] = ' ';
      }
    }
    zLine[53] = ' ';
    zLine[54] = ' ';
    cgi_append_content(zLine, 55);
    for(j=0; j<16; j++){
    for(j=k=0; j<16; j++){
      k = j+55;
      if( i+j<n ){
        unsigned char c = x[i+j];
        if( c>=0x20 && c<=0x7e ){
          zLine[k] = c;
        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] = '.';
          zLine[k++] = '.';
        }
      }else{
        zLine[k] = 0;
        break;
      }
    }
    zLine[71] = 0;
    @ %h(zLine)
    zLine[k++] = '\n';
    cgi_append_content(zLine, k);
  }
}

/*
** WEBPAGE: hexdump
** URL: /hexdump?name=ARTIFACTID
**
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
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







+



-
+
-

-
+




+
+
+

-
+

-
+



-
-
-
-
+
+
+
+

+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+






-
-
-
+
+
+

-
-
+
+
-
-
-
+
-
-

-
+





+








+
+
+



-
-
+
+






-
+
-
-
-
-
-
-
-
+



-
+



-
-
+
+
+
+
+
+
+






+
+
+
+
+
+
+



+
+
-
+
+





+
+
+
+



















+



+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
-
-
-
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+

-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
-
-
+
-
-
+
+
+

+
+
+
-
-
+
+
+
+
+
+
+




















-
-
-
-
+
+
+
+
+
+


-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+








+


-
-
+
+




+

+
+
+
+

+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+



-
-


-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+





+
+
+

+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+

-
-
+
+
+





-
+
-

-
+


+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
-
+
+
+















-
+
-

-
+

-
-









-
+
+

+
-
+




+
+
+
+
+
+
+
+
+
+
+
+








-
-
-
-
+


+

+

-
+

-
-
-
+
+
+
+
+
+
+
+
+
+
+

+
-
+
+
+
+

-
-
-
-
+
+
+
+
+

+
+
+
+
+
+

-
+
+
+
+
+
+






-
-
+
+

+
+
+
+
+






-
+







  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", "%s/shun?accept=%s&sub=1#delshun",
      style_submenu_element("Unshun", "%R/shun?accept=%s&sub=1#delshun", zUuid);
            g.zTop, zUuid);
    }else{
      style_submenu_element("Shun", "%s/shun?shun=%s#addshun", g.zTop, zUuid);
      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 ){
    @ <h2>Artifact %s(zUuid) (%d(rid)):</h2>
    @  (%d(rid)):</h2>
  }else{
    @ <h2>Artifact %s(zUuid):</h2>
    @ :</h2>
  }
  blob_zero(&downloadName);
  if( P("verbose")!=0 ) objdescFlags |= OBJDESC_DETAIL;
  object_description(rid, objdescFlags, &downloadName);
  style_submenu_element("Download", "%s/raw/%T?name=%s",
        g.zTop, blob_str(&downloadName), zUuid);
  @ <hr />
  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_footer();
    @ <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" as an alias for "filename".  If either "filename"
** or "fn" is present but "ci" is missing, use "tip" as a default value
** for "ci".
** 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, this use that parameter as the filename
** rather than "fn" or "filename".
** If zNameParam is not NULL, then use that parameter as the filename
** rather than "fn" or "filename" or "name".  the zNameParam is used
**
** If pUrl is not NULL, then record the "ci" and "filename" values in
** pUrl.
** for the from= and to= query parameters of /fdiff.
**
** At least one of pUrl or zNameParam must be NULL.
*/
int artifact_from_ci_and_filename(HQuery *pUrl, const char *zNameParam){
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 = P("ci");
  cirid = name_to_typed_rid(zCI ? zCI : "tip", "ci");
  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 ){
      int rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", pFile->zUuid);
      rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", pFile->zUuid);
      manifest_destroy(pManifest);
      if( pUrl ){
        assert( zNameParam==0 );
        url_add_parameter(pUrl, "fn", zFilename);
        if( zCI ) url_add_parameter(pUrl, "ci", zCI);
      }
      return rid;
      break;
    }
  }
  manifest_destroy(pManifest);
  return 0;
  return rid;
}

/*
** The "z" argument is a string that contains the text of a source code
** file.  This routine appends that text to the HTTP reply with line numbering.
** 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
  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 min(iStart), max(iEnd) FROM lnos");
  if( db_step(&q)==SQLITE_ROW ){
    iStart = db_column_int(&q, 0);
    iEnd = db_column_int(&q, 1);
    iTop = iStart - 15 + (iEnd-iStart)/4;
    if( iTop>iStart - 2 ) iTop = iStart-2;
  }
  db_finalize(&q);
  @ <pre>
      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;
  while( z[0] ){
    n++;
      /*cgi_printf("<!-- iStart=%d, iEnd=%d -->", iStart, iEnd);*/
    }
    if(n==iTop) {
    db_prepare(&q,
      "SELECT min(iStart), max(iEnd) FROM lnos"
      " WHERE iStart <= %d AND iEnd >= %d", n, n);
      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;
    if( db_step(&q)==SQLITE_ROW ){
      iStart = db_column_int(&q, 0);
      iEnd = db_column_int(&q, 1);
      }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'";
    }
    db_finalize(&q);
    for(i=0; z[i] && z[i]!='\n'; i++){}
    if( n==iTop ) cgi_append_content("<span id=\"scrollToMe\">", -1);
    if( n==iStart ){
      cgi_append_content("<div class=\"selectedText\">",-1);
    }
    cgi_printf("%6d  ", n);
    if( i>0 ){
      char *zHtml = htmlize(z, i);
    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;
      cgi_append_content(zHtml, -1);
      fossil_free(zHtml);
    }
    if( n==iTop ) cgi_append_content("</span>", -1);
    if( n==iEnd ) cgi_append_content("</div>", -1);
    else cgi_append_content("\n", 1);
    z += i;
    if( 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( z[0]=='\n' ) z++;
  }

  if( n<iEnd ) cgi_printf("</div>");
  @ </pre>
  if(g.argc < 3){
  if( db_int(0, "SELECT EXISTS(SELECT 1 FROM lnos)") ){
    style_load_one_js_file("scroll.js");
    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=SHA1HASH   - Provide the SHA1HASH as a query parameter
**   filename=NAME   - Show information for content file NAME
**   fn=NAME         - "fn" is shorthand for "filename"
**   ci=VERSION      - The specific check-in to use for "filename=".
**   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 as preformatted text.  The
** /whatis page shows only a description of the file.  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.
** 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;
  u32 objdescFlags = 0;
  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");
  url_initialize(&url, g.zPath);
  rid = artifact_from_ci_and_filename(&url, 0);
  if( rid==0 ){
    url_add_parameter(&url, "name", zName);
    if( isFile ){
      /* Do a top-level directory listing in /file mode if no argument
      ** specified */
      if( zName==0 || zName[0]==0 ){
        if( P("ci")==0 ) cgi_set_query_parameter("ci","tip");
        page_tree();
        return;
      }
      /* Look for a single file with the given name */
      rid = db_int(0,
         "SELECT fid FROM filename, mlink, event"
         " WHERE name=%Q"
         "   AND mlink.fnid=filename.fnid"

  /* 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 */

         "   AND event.objid=mlink.mid"
         " ORDER BY event.mtime DESC LIMIT 1",
         zName
      );
      /* If no file called NAME exists, instead look for a directory
  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);
  }

      ** with that name, and do a directory listing */
      if( rid==0 ){
        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;
        }
  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);
      }
      /* If no file or directory called NAME: issue an error */
      db_finalize(&q);
      if( rid==0 ){
        style_header("No such file");
        @ File '%h(zName)' does not exist in this repository.
        style_footer();
        return;
      }
    }else{
      rid = name_to_rid_www("name");
    }
  }

  login_check_credentials();
  if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
  if( rid==0 ){
    style_header("No such artifact");
    @ Artifact '%h(zName)' does not exist in this repository.
    style_footer();
    return;
  }
      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>Latest version of file '%h(zName)':</h2>
      @ <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);
  }else if( g.perm.Setup ){
    @ <h2>Artifact %s(zUuid) (%d(rid)):</h2>
  }else{
    @ <h2>Artifact %s(zUuid):</h2>
  }
  blob_zero(&downloadName);
  objType = object_description(rid, objdescFlags, &downloadName);
    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/%T?name=%s", blob_str(&downloadName),
          db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid));
    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", "%s/shun?accept=%s&sub=1#accshun",
      style_submenu_element("Unshun", "%R/shun?accept=%s&sub=1#accshun", zUuid);
            g.zTop, zUuid);
    }else{
      style_submenu_element("Shun", "%s/shun?shun=%s#addshun", g.zTop, zUuid);
      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", isFile ? "File Content" :
  style_header("%s", zHeader);
                     descOnly ? "Artifact Description" : "Artifact Content");
  if( g.perm.Admin ){
  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/%T?name=%s",
  style_submenu_element("Download", "%R/raw/%s?at=%T", zUuid, file_tail(zName));
                         blob_str(&downloadName), zUuid);
  if( db_exists("SELECT 1 FROM mlink WHERE fid=%d", rid) ){
    style_submenu_element("Check-ins Using", "%R/timeline?n=200&uf=%s", zUuid);
    style_submenu_element("Check-ins Using", "%R/timeline?uf=%s", zUuid);
  }
  asText = P("txt")!=0;
  zMime = mimetype_from_name(blob_str(&downloadName));
  if( zMime ){
    if( fossil_strcmp(zMime, "text/html")==0 ){
      if( asText ){
        style_submenu_element("Html", "%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-markdown")==0
           || fossil_strcmp(zMime, "text/x-pikchr")==0 ){
      if( asText ){
        style_submenu_element(zMime[7]=='p' ? "Pikchr" : "Wiki",
        style_submenu_element("Wiki", "%s", url_render(&url, "txt", 0, 0, 0));
                              "%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{
    if( zLn==0 || atoi(zLn)==0 ){
      style_submenu_checkbox("ln", "Line Numbers", 0, 0);
    }
    @ <hr />
    @ <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/%T(blob_str(&downloadName))?name=%s(zUuid)"
      @ <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>
      @ 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", "%s/hexdump?name=%s", g.zTop, zUuid);
      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);
      zMime = mimetype_from_content(&content);
      @ <blockquote>
      if( zMime==0 ){
        const char *z;
      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, 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 ){
        @ <i>(file is %d(blob_size(&content)) bytes of image data)</i><br />
        @ <img src="%R/raw/%s(zUuid)?m=%s(zMime)" />
        @ <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_footer();
  style_finish_page();
}

/*
** WEBPAGE: tinfo
** URL: /tinfo?name=ARTIFACTID
**
** Show the details of a ticket change control artifact.
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
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







+



-
+
-

-
+




-
+


















-
+



-
+


+


-
+



















-
+














-
+

-
+







-
+

-
+





-
+

-
-
+
+

+
+
-
+










+











+















-
+











-
+












+
+
+
+
+










-
-
-
-
-







  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", "%s/shun?accept=%s&sub=1#accshun",
      style_submenu_element("Unshun", "%R/shun?accept=%s&sub=1#accshun", zUuid);
            g.zTop, zUuid);
    }else{
      style_submenu_element("Shun", "%s/shun?shun=%s#addshun", g.zTop, zUuid);
      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)", pTktChng->rDate);
  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(rid);
      moderation_approve('t', rid);
    }
  }
  zTktTitle = db_table_has_column("repository", "ticket", "title" )
      ? db_text("(No 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", zTktName);
  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&nbsp;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)
        @<br>%h(zTktTitle)
  }
  @</td></tr>
  @ <tr><th>User&nbsp;&amp;&nbsp;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 />
    @ Delete this change</label><br>
    @ <label><input type="radio" name="modaction" value="approve">
    @ Approve this change</label><br />
    @ Approve this change</label><br>
    @ <input type="submit" value="Submit">
    @ </form>
    @ </blockquote>
  }

  @ <div class="section">Changes</div>
  @ <p>
  ticket_output_change_artifact(pTktChng, 0);
  ticket_output_change_artifact(pTktChng, 0, 1, 0);
  manifest_destroy(pTktChng);
  style_footer();
  style_finish_page();
}


/*
** WEBPAGE: info
** URL: info/ARTIFACTID
** URL: info/NAME
**
** The argument is a artifact ID which might be a check-in or a file or
** a ticket changes or a wiki edit or something else.
** 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
** Figure out what the artifact ID is and display it appropriately.
** 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_footer();
    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_footer();
    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
  if( db_table_exists("repository","forumpost")
   && db_exists("SELECT 1 FROM forumpost WHERE fpid=%d", rid)
  ){
    forumthread_page();
  }else
  {
    artifact_page();
  }
}

/*
** Do a comment comparison.
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
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







-
+
+
+
+
+
+
+




















+
+
+
-
+
+


+
+
+
+
+
-
-
-
-
+
+
+
+
-
-
+
+
+


















-
+



-
+







  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){
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());
      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_begin_transaction();
      g.markPrivate = content_is_private(rid);
      nrid = content_put(ctrl);
      manifest_crosslink(nrid, ctrl, MC_PERMIT_HOOKS);
    assert( blob_is_reset(ctrl) );
    db_end_transaction(0);
      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 parmeters:
** Query parameters:
**
**     rid=INTEGER        Record ID of the check-in to edit (REQUIRED)
**
** POST parameters after pressing "Perview", "Cancel", or "Apply":
** 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)
2603
2604
2605
2606
2607
2608
2609
2610

2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
3218
3219
3220
3221
3222
3223
3224

3225
3226
3227
3228

3229
3230
3231
3232
3233
3234
3235







-
+



-







  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(1) ){
  if( P("apply") && cgi_csrf_safe(2) ){
    Blob ctrl;
    char *zNow;

    login_verify_csrf_secret();
    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
2639
2640
2641
2642
2643
2644
2645
2646

2647
2648
2649
2650
2651
2652
2653
3253
3254
3255
3256
3257
3258
3259

3260
3261
3262
3263
3264
3265
3266
3267







-
+







      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);
    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") ){
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
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







-
+





-
-
+




-
+









-
+





-
+





-
+





-
+

-
+


+
+
+
+
+




-
+

-
+
-







    @ %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 />
    @ <hr>
    blob_reset(&suffix);
  }
  @ <p>Make changes to attributes of check-in
  @ [%z(href("%R/ci/%!S",zUuid))%s(zUuid)</a>]:</p>
  form_begin(0, "%R/ci_edit");
  login_insert_csrf_secret();
  @ <div><input type="hidden" name="r" value="%s(zUuid)" />
  @ <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)" />
  @   <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)" />
  @   <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)" />
    @   <input type="text" name="chngtime" size="20" value="%h(zChngTime)">
    @ </td></tr>
  }

  @ <tr><th align="right" valign="top">Background&nbsp;Color:</th>
  @ <td valign="top">
  @ <div><label><input type='checkbox' name='newclr'%s(zNewColorFlag) />
  @ <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" />
    @ <input type="checkbox" name="pclr" checked="checked">
  }else{
    @ <input type="checkbox" name="pclr" />
    @ <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) />
  @ <label><input type="checkbox" id="newtag" name="newtag"%s(zNewTagFlag)>
  @ Add the following new tag name to this check-in:</label>
  @ <input type="text" size='15' name="tagname" value="%h(zNewTag)" \
  @ <input size="15" name="tagname" id="tagname" value="%h(zNewTag)">
  @ id='tagname' />
  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)"
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
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







-
+

-
+

-
+











-
+







-
-
+
+

-
+



-
+









-
+






-
+










-
-
+
+

-
+




-
-
+
+







    }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>
    @ <br><label>
    if( P(zLabel) ){
      @ <input type="checkbox" name="c%d(tagid)" checked="checked" />
      @ <input type="checkbox" name="c%d(tagid)" checked="checked">
    }else{
      @ <input type="checkbox" name="c%d(tagid)" />
      @ <input type="checkbox" name="c%d(tagid)">
    }
    if( isSpecialTag ){
      @ Cancel special tag <b>%h(zTagName)</b></label>
    }else{
      @ Cancel tag <b>%h(&zTagName[4])</b></label>
    }
  }
  db_finalize(&q);
  @ </td></tr>

  if( !zBranchName ){
    zBranchName = db_get("main-branch", "trunk");
    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) />
  @ Make this check-in the start of a new branch named:</label>
  @ 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>
  @ 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) />
    @ <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) />
      @ <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) />
      @ <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" />
  @ <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" />
    @ <input type="submit" name="apply" value="Apply Changes">
  }
  @ </td></tr>
  @ </table>
  @ </div></form>
  style_load_one_js_file("ci_edit.js");
  style_footer();
  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.
**
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
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







-
+



-
+

-
+


-









-
+


+
+
+







    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 "UUID OPTION ?OPTION ...?"
#define AMEND_USAGE_STMT "HASH OPTION ?OPTION ...?"
/*
** COMMAND: amend
**
** Usage: %fossil amend UUID OPTION ?OPTION ...?
** Usage: %fossil amend HASH OPTION ?OPTION ...?
**
** Amend the tags on check-in UUID to change how it displays in the timeline.
** 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           Make this check-in the start of branch NAME
**    --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.
*/
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
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







+

+








-
















+
-
+
+
+







-
+







  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;

  if( g.argc==3 ) usage(AMEND_USAGE_STMT);
  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("chngtime",0,1);
  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 UUID");
  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"
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053









































































































































































































































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







-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
        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);
  show_common_info(rid, "uuid:", 1, 0);
}
  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>&nbsp;&rarr;&nbsp;</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&nbsp;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();
}

Changes to 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
1
2

3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

21
22

23

24
25
26
27


28
29
30
31
32
33
34
35
36
37
38
39
40
41

42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87


88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105


-
+

















-
+

-
+
-




-
-
+
+
+











-
+
+














+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+


+
+
+







#ifdef FOSSIL_ENABLE_JSON
/*
** Copyright (c) 2011 D. Richard Hipp
** 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.
**
** For notes regarding the public JSON interface, please see:
** The JSON API's public interface is documented at:
**
** https://docs.google.com/document/d/1fXViveNhDbiXgCuE7QDXQOKeFzf2qNUkBEgiUvoqFN4/view
** 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().
** 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 */
#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)
116
117
118
119
120
121
122
123

124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156


157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174

175
176
177
178
179
180
181
154
155
156
157
158
159
160

161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178











179
180
181
182

183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200


201
202
203
204
205
206
207
208







-
+

















-
-
-
-
-
-
-
-
-
-
-




-
+
+
















-
-
+







    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 checkout.");
    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;
}

/*
** Implements the cson_data_source_f() interface and reads input from
** a fossil Blob object. pState must be-a (Blob*) populated with JSON
** data.
*/
int cson_data_src_Blob(void * pState, void * dest, unsigned int * n){
  Blob * b = (Blob*)pState;
  *n = blob_read( b, dest, *n );
  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 ){
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;
  blob_rewind( pSrc );
  cson_parse( &root, cson_data_src_Blob, pSrc, NULL, pInfo );
  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.
**/
262
263
264
265
266
267
268
269

270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288


289
290
291
292
293
294
295
289
290
291
292
293
294
295

296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313


314
315
316
317
318
319
320
321
322







-
+

















-
-
+
+







  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));
  free(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 difficult
** for me to do that. Since fossil only uses one cookie, 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;
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
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







-
+














-
-
+
+







}

/*
** 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 or application/javascript, and will fall back to
** 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
      ? "application/javascript; charset=utf-8"
      : "application/javascript";
      ? "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.
    */
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
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+












-
+

+
+
+
+
+


+

+



+







          : "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
** application/javascript and the output is wrapped in a jsonp
** 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*/
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
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







+
+
+
+
+
+


-
-
+
+
+
+
+








-
-
-
-
+
+
+
+

-
-
+
+

-
+
+







**
** 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_main_bootstrap() was not called!");
    if( !g.json.authToken ){
  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()=...
         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.
         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) );
      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.
      */
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
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







+

+
+
+
+
+
+
+
+
+
+
+

-
-
+
+
+
+
+





+
+

-
+

+
-
-
-
+
+
+
+

-


-
+
-










-
+
-







*/
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 only be called from cgi_init() or
** json_cmd_top() (early on in those functions).
** 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_main_bootstrap(){
void json_bootstrap_early(void){
  cson_value * v;

  assert( (NULL == g.json.gc.v) &&
          "json_main_bootstrap() was called twice!" );

  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.
     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.
       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
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
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







-
+


















-
+







** 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_main_bootstrap() was not called!");
  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)));
    free(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
829
830
831
832
833
834
835

836

837
838
839
840
841
842
843
906
907
908
909
910
911
912
913

914
915
916
917
918
919
920
921







+
-
+







        assert( head != p );
        zPart = (char*)fossil_malloc(len+1);
        memcpy(zPart, head, len);
        zPart[len] = 0;
        if(doDeHttp){
          dehttpize(zPart);
        }
        if( *zPart ){
        if( *zPart ){ /* should only fail if someone manages to url-encoded a NUL byte */
          /* 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{
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
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







-
+


-
-
+
+





-
+
+







** 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.
*/
static void json_mode_bootstrap(){
void json_bootstrap_late(){
  static char once = 0  /* guard against multiple runs */;
  char const * zPath = P("PATH_INFO");
  assert(g.json.gc.a && "json_main_bootstrap() was not called!");
  assert( (0==once) && "json_mode_bootstrap() called too many times!");
  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;
  }
  g.json.isJsonMode = 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.
    */
    ;
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
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







+








-
+



-
+
-
-
+
-
+
+







-
+







    /* 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_panic("Could not open JSON file [%s].",jfile)
      fossil_fatal("Could not open JSON file [%s].",jfile)
        /* Does not return. */
        ;
    }
    cgi_parse_POST_JSON(inFile, 0);
    blob_read_from_channel(&json, inFile, -1);
    if( stdin != inFile ){
      fclose(inFile);
    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 );
  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.
        */;
  }

1038
1039
1040
1041
1042
1043
1044
1045

1046
1047
1048
1049
1050
1051
1052
1118
1119
1120
1121
1122
1123
1124

1125
1126
1127
1128
1129
1130
1131
1132







-
+







  }


  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*/;
    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)
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
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







-
-
+
+









-
+
-
-
+
+













-
+
+

















-
+
+







**
** 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_mode_bootstrap() called?");
  assert((g.argc>1) && "Internal error - we never should have gotten this far.");
  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*/
      if( g.isHTTP/*workaround for "abbreviated name" in CLI mode*/
          ? (0==strcmp(g.argv[1],tok))
          : (0==strncmp("json",tok,4))
          ? (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 )));
    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 * 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;
    }
  }
1215
1216
1217
1218
1219
1220
1221
1222


1223
1224
1225


1226
1227
1228
1229
1230
1231
1232
1297
1298
1299
1300
1301
1302
1303

1304
1305
1306
1307

1308
1309
1310
1311
1312
1313
1314
1315
1316







-
+
+


-
+
+







*/
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 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)));
      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;
      }
1252
1253
1254
1255
1256
1257
1258
1259


1260
1261
1262
1263
1264
1265
1266
1336
1337
1338
1339
1340
1341
1342

1343
1344
1345
1346
1347
1348
1349
1350
1351







-
+
+







*/
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 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);
1372
1373
1374
1375
1376
1377
1378

1379

1380
1381
1382
1383




1384
1385
1386
1387
1388
1389
1390
1391
1392
1457
1458
1459
1460
1461
1462
1463
1464

1465




1466
1467
1468
1469
1470

1471
1472
1473
1474
1475
1476
1477







+
-
+
-
-
-
-
+
+
+
+

-







  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 ); \
  rc = cson_object_set( o, K, tmp );  \
  if(rc) do{\
    cson_value_free(tmp); \
    tmp = NULL; \
    goto cleanup; \
  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);

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
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







+
+
+
















-
-
+
+
+


















-

















+
-
+
-
+







  }

  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 );
      cson_object_set( o, "cmd.offset", tmp );
      cson_object_set( o, "isCGI", cson_value_new_bool( g.isHTTP ) );
      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)
  if(json_find_option_bool("debugFossilG","json-debug-g",NULL,0)
     && json_find_option_bool("debugFossilG","json-debug-g",NULL,0)
     &&(g.perm.Admin||g.perm.Setup)){
     ){
    tmp = json_g_to_json();
    SET("g");
  }

#undef SET
  goto ok;
  cleanup:
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
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







+










-
+
+







** 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() here because that calls this function.
       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){
1547
1548
1549
1550
1551
1552
1553
1554

1555
1556
1557
1558
1559
1560
1561
1638
1639
1640
1641
1642
1643
1644

1645
1646
1647
1648
1649
1650
1651
1652







-
+







** 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) );
  free(g.zErrMsg);
  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);
1695
1696
1697
1698
1699
1700
1701
1702

1703
1704
1705
1706
1707
1708
1709
1786
1787
1788
1789
1790
1791
1792

1793
1794
1795
1796
1797
1798
1799
1800







-
+







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);
    }
    free(tags);
    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
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
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







-
+












-
-
-
-
+
+
+
+
+







    : cson_value_false();
}

/*
** Impl of /json/resultCodes
**
*/
cson_value * json_page_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_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);
1784
1785
1786
1787
1788
1789
1790
1791

1792
1793
1794
1795
1796
1797
1798
1876
1877
1878
1879
1880
1881
1882

1883
1884
1885
1886
1887
1888
1889
1890







-
+









/*
** /json/version implementation.
**
** Returns the payload object (owned by the caller).
*/
cson_value * json_page_version(){
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");
1838
1839
1840
1841
1842
1843
1844
1845

1846
1847
1848
1849
1850
1851
1852
1930
1931
1932
1933
1934
1935
1936

1937
1938
1939
1940
1941
1942
1943
1944







-
+







**
** 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(){
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
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1958
1959
1960
1961
1962
1963
1964

1965
1966
1967
1968
1969
1970
1971







-







  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(Delete,"delete");
  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");
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
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







+








-
+

















-
+
+






-
+


-
+


-
+







  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(){
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)));
#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));
  free(zTmp);
  fossil_free(zTmp);
  zTmp = db_get("project-description",NULL);
  cson_object_set(jo, "projectDescription", json_new_string(zTmp));
  free(zTmp);
  fossil_free(zTmp);
  zTmp = NULL;
  fsize = file_size(g.zRepositoryName, ExtFILE);
  cson_object_set(jo, "repositorySize", 
  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));
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
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







+
-
+




-
-
+
+

-
-
-
-
+
+
+
+
+
+
+

-
-
+
+
+
+







  }/*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_object_set(jo, "compiler", cson_value_new_string(COMPILER_NAME, strlen(COMPILER_NAME)));
                  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());
  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"));
  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());
  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
}




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
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







-
+
















-
+








-
+









-
+

-
+

-
+

-
+

-
+

-
+

-
+

-
+

-
+

-
+

-
+

-
+

-
+

-
+






-
+
+
















+







  }
}


/*
** Impl of /json/rebuild. Requires admin privileges.
*/
static cson_value * json_page_rebuild(){
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, 0);
    rebuild_db(0, 0);
    db_end_transaction(0);
    return NULL;
  }
}

/*
** Impl of /json/g. Requires admin/setup rights.
*/
static cson_value * json_page_g(){
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();
cson_value * json_page_anon_password(void);
/* Impl in json_artifact.c. */
cson_value * json_page_artifact();
cson_value * json_page_artifact(void);
/* Impl in json_branch.c. */
cson_value * json_page_branch();
cson_value * json_page_branch(void);
/* Impl in json_diff.c. */
cson_value * json_page_diff();
cson_value * json_page_diff(void);
/* Impl in json_dir.c. */
cson_value * json_page_dir();
cson_value * json_page_dir(void);
/* Impl in json_login.c. */
cson_value * json_page_login();
cson_value * json_page_login(void);
/* Impl in json_login.c. */
cson_value * json_page_logout();
cson_value * json_page_logout(void);
/* Impl in json_query.c. */
cson_value * json_page_query();
cson_value * json_page_query(void);
/* Impl in json_report.c. */
cson_value * json_page_report();
cson_value * json_page_report(void);
/* Impl in json_tag.c. */
cson_value * json_page_tag();
cson_value * json_page_tag(void);
/* Impl in json_user.c. */
cson_value * json_page_user();
cson_value * json_page_user(void);
/* Impl in json_config.c. */
cson_value * json_page_config();
cson_value * json_page_config(void);
/* Impl in json_finfo.c. */
cson_value * json_page_finfo();
cson_value * json_page_finfo(void);
/* Impl in json_status.c. */
cson_value * json_page_status();
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. */
/* 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},
2230
2231
2232
2233
2234
2235
2236
2237
2238


2239
2240
2241
2242
2243
2244
2245
2331
2332
2333
2334
2335
2336
2337


2338
2339
2340
2341
2342
2343
2344
2345
2346







-
-
+
+







**
** 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_main_bootstrap() was not called!");
  json_mode_bootstrap();
  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;
  }
2302
2303
2304
2305
2306
2307
2308
2309
2310


2311
2312
2313
2314
2315
2316
2317
2403
2404
2405
2406
2407
2408
2409


2410
2411
2412
2413
2414
2415
2416
2417
2418







-
-
+
+







       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_main_bootstrap();
  json_mode_bootstrap();
  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

Changes to src/json_artifact.c.

209
210
211
212
213
214
215
216


217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235


236
237
238
239
240
241
242
243
244
245
246
247
248
249
250

251
252
253
254


255
256
257
258
259

260
261
262
263
264
265
266
209
210
211
212
213
214
215

216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235

236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251

252
253
254
255

256
257
258
259
260
261

262
263
264
265
266
267
268
269







-
+
+


















-
+
+














-
+



-
+
+




-
+







}

/*
** 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." );
    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},
/*{"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(){
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;
    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 */;
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{
377
378
379
380
381
382
383
384


385
386
387
388
389
390
391


392
393
394
395
396
397
398
399
400
401
402

403
404
405
406
407
408
409
380
381
382
383
384
385
386

387
388
389
390
391
392
393


394
395
396
397
398
399
400
401
402
403
404
405

406
407
408
409
410
411
412
413







-
+
+





-
-
+
+










-
+







    );
  /* 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));
    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_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(){
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;
474
475
476
477
478
479
480








481
482
483
484
485
486
487
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499







+
+
+
+
+
+
+
+







  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) );*/

Changes to src/json_branch.c.

20
21
22
23
24
25
26
27
28


29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

46
47
48
49
50
51
52
53
54
55
56


57
58
59
60
61
62
63
64
65

66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84

85
86
87
88
89
90
91
20
21
22
23
24
25
26


27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

45
46
47
48
49
50
51
52
53
54


55
56
57
58
59
60
61
62
63
64

65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83

84
85
86
87
88
89
90
91







-
-
+
+
















-
+









-
-
+
+








-
+


















-
+







#include "json_branch.h"

#if INTERFACE
#include "json_detail.h"
#endif


static cson_value * json_branch_list();
static cson_value * json_branch_create();
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(){
cson_value * json_page_branch(void){
  return json_page_dispatch_helper(&JsonPageDefs_Branch[0]);
}

/*
** Impl for /json/branch/list
**
**
** CLI mode options:
**
**  --range X | -r X, where X is one of (open,closed,all)
**    (only the first letter is significant, default=open).
**  -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(){
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_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";
126
127
128
129
130
131
132
133

134
135
136
137
138
139
140
126
127
128
129
130
131
132

133
134
135
136
137
138
139
140







-
+







      : 0;
    if(zCurrent){
      cson_object_set(pay,"current",json_new_string(zCurrent));
    }
  }


  branch_prepare_list_query(&q, branchListFlags);
  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",
152
153
154
155
156
157
158
159

160
161
162
163
164
165
166
152
153
154
155
156
157
158

159
160
161
162
163
164
165
166







-
+







/*
** Parameters for the create-branch operation.
*/
typedef struct BranchCreateOptions{
  char const * zName;
  char const * zBasis;
  char const * zColor;
  char isPrivate;
  int isPrivate;
  /**
     Might be set to an error string by
     json_branch_new().
   */
  char const * rcErrMsg;
} BranchCreateOptions;

195
196
197
198
199
200
201

202




203
204
205
206
207
208
209
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214







+

+
+
+
+







  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"
258
259
260
261
262
263
264
265

266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292

293
294
295
296

297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320

321
322
323
324
325
326
327
263
264
265
266
267
268
269

270
271
272
273
274
275



276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293

294
295
296
297

298
299
300
301
302
303
304
305
306
307
308
309
310




311
312
313
314
315
316
317

318
319
320
321
322
323
324
325







-
+





-
-
-


















-
+



-
+












-
-
-
-







-
+







    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 ) zColor = "#fec084";
  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);
  if( zOpt->isPrivate ){
    blob_appendf(&branch, "T +private *\n");
  }

  /* 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(&branch);
  brid = content_put_ex(&branch, 0, 0, 0, zOpt->isPrivate);
  if( brid==0 ){
    fossil_panic("Problem committing manifest: %s", g.zErrMsg);
  }
  db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", brid);
  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);

#if 0 /* Do an autosync push, if requested */
  /* arugable for JSON mode? */
  if( !g.isHTTP && !isPrivate ) autosync(SYNC_PUSH);
#endif
  return 0;
}


/*
** Impl of /json/branch/create.
*/
static cson_value * 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 ){
335
336
337
338
339
340
341
342


343
344
345
346
347
348
349
333
334
335
336
337
338
339

340
341
342
343
344
345
346
347
348







-
+
+







  }

  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." );
    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);

Changes to src/json_config.c.

19
20
21
22
23
24
25
26
27


28
29
30
31
32
33
34
35
36
37
38












39
40
41
42
43
44

45
46








47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65


66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
19
20
21
22
23
24
25


26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107

108
109
110
111
112
113
114







-
-
+
+











+
+
+
+
+
+
+
+
+
+
+
+





-
+


+
+
+
+
+
+
+
+



















+
+




















-







#include "config.h"
#include "json_config.h"

#if INTERFACE
#include "json_detail.h"
#endif

static cson_value * json_config_get();
static cson_value * json_config_save();
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(){
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 },
{ "allow-symlinks",         CONFIGSET_PROJ },
{ "dotfiles",               CONFIGSET_PROJ },

{ "ticket-table",           CONFIGSET_TKT  },
{ "ticket-common",          CONFIGSET_TKT  },
{ "ticket-change",          CONFIGSET_TKT  },
{ "ticket-newpage",         CONFIGSET_TKT  },
{ "ticket-viewpage",        CONFIGSET_TKT  },
102
103
104
105
106
107
108
109

110
111
112
113
114
115
116
123
124
125
126
127
128
129

130
131
132
133
134
135
136
137







-
+







};


/*
** Impl of /json/config/get. Requires setup rights.
**
*/
static cson_value * json_config_get(){
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;
178
179
180
181
182
183
184
185

186
187
188


































































































































































































189
199
200
201
202
203
204
205

206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404







-
+



+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

}

/*
** Impl of /json/config/save.
**
** TODOs:
*/
static cson_value * json_config_save(){
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 */

Changes to src/json_detail.h.

14
15
16
17
18
19
20

21

22
23
24
25
26
27
28
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30







+

+







**
** 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
51
52
53
54
55
56
57

58
59
60
61
62
63
64
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67







+







** 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*/,
144
145
146
147
148
149
150
151

152
153
154
155
156
157
158
147
148
149
150
151
152
153

154
155
156
157
158
159
160
161







-
+







** 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)();
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.
255
256
257
258
259
260
261
262

263
264
265
266
267

268
269
258
259
260
261
262
263
264

265
266
267
268
269
270
271
272
273







-
+





+


** 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();
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 */

Changes to src/json_diff.c.

36
37
38
39
40
41
42

43
44
45
46

47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62

63

64
65
66
67
68
69
70
36
37
38
39
40
41
42
43
44
45


46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

64
65
66
67
68
69
70
71







+


-
-
+
















+
-
+







*/
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 = (DIFF_CONTEXT_MASK & nContext)
    | (fSbs ? DIFF_SIDEBYSIDE : 0)
  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, 0, 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));
  }
80
81
82
83
84
85
86
87

88
89
90
91
92
93
94
81
82
83
84
85
86
87

88
89
90
91
92
93
94
95







-
+







** 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(){
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;

Changes to src/json_dir.c.

19
20
21
22
23
24
25
26

27
28
29
30
31
32
33
34
35
36
37
38

39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

55
56
57
58
59
60
61
19
20
21
22
23
24
25

26
27
28
29
30
31
32
33
34
35
36
37

38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

54
55
56
57
58
59
60
61







-
+











-
+















-
+







#include "config.h"
#include "json_dir.h"

#if INTERFACE
#include "json_detail.h"
#endif

static cson_value * json_page_dir_list();
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(){
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(){
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;
279
280
281
282
283
284
285
286

287
288
289
290
291
292
293
294
279
280
281
282
283
284
285

286
287
288
289
290
291
292
293
294







-
+








  return cson_object_value(zPayload);
}

/*
** Implements the /json/dir family of pages/commands.
**
*/
cson_value * json_page_dir(){
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 */

Changes to src/json_finfo.c.

23
24
25
26
27
28
29
30

31
32
33
34
35
36
37
38
39
40
41

42
43
44
45

46

47
48
49


50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70

71
72
73
74
75
76
77
23
24
25
26
27
28
29

30
31
32
33
34
35
36
37
38
39
40

41
42
43
44
45
46

47
48


49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70

71
72
73
74
75
76
77
78







-
+










-
+




+
-
+

-
-
+
+




















-
+







#include "json_detail.h"
#endif

/*
** Implements the /json/finfo page/command.
**
*/
cson_value * json_page_finfo(){
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;
  char sort = -1;
  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,
  json_warn( FSL_JSON_W_UNKNOWN, "Achtung: the output of the finfo command is up for change.");
             "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.
  /* 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 */
/*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,"
86
87
88
89
90
91
92
93
94



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109


110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128


129
130
131
132


133
134
135


136
137
138
139
140
141
142
143
144
145
146
147
87
88
89
90
91
92
93


94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110

111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130

131
132
133
134
135

136
137
138


139
140
141
142
143
144
145
146
147
148
149
150
151
152







-
-
+
+
+














-
+
+


















-
+
+



-
+
+

-
-
+
+












               );

  if( zCheckin && *zCheckin ){
    char * zU = NULL;
    int rc = name_to_uuid2( zCheckin, "ci", &zU );
    /*printf("zCheckin=[%s], zU=[%s]", zCheckin, zU);*/
    if(rc<=0){
      json_set_err((rc<0) ? FSL_JSON_E_AMBIGUOUS_UUID : FSL_JSON_E_RESOURCE_NOT_FOUND,
                   "Check-in UUID %s.", (rc<0) ? "is ambiguous" : "not found");
      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"));
  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, "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, "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)));
    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 */

Changes to src/json_login.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
52


53
54

55
56

57
58
59
60
61
62
63
24
25
26
27
28
29
30

31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47


48
49
50


51
52


53
54

55
56
57
58
59
60
61
62







-
+
















-
-
+
+

-
-
+
+
-
-
+

-
+







#endif


/*
** Implementation of the /json/login page.
**
*/
cson_value * json_page_login(){
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 a bug in cgi_parameter() is breaking that, causing PD() to
    return the last element of the PATH_INFO instead.
    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"
    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
    is not recognized). If we reverse the order of the
    checks then both forms work. Strangely enough, the
    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") ==
144
145
146
147
148
149
150
151

152
153

154
155
156
157
158
159
160
161


162
163


164
165
166
167
168
169
170
143
144
145
146
147
148
149

150
151

152
153
154
155
156
157
158
159

160
161
162

163
164
165
166
167
168
169
170
171







-
+

-
+







-
+
+

-
+
+







      : 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);
      login_set_anon_cookie(NULL, &cookie, 0);
    }else{
      login_set_user_cookie(name, uid, &cookie);
      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() );
    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() ) );
    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
180
181
182
183
184
185
186
187

188
189

190
191
192
193
194
195
196
181
182
183
184
185
186
187

188
189

190
191
192
193
194
195
196
197







-
+

-
+







  }
}

/*
** Impl of /json/logout.
**
*/
cson_value * json_page_logout(){
cson_value * json_page_logout(void){
  cson_value const *token = g.json.authToken;
    /* Remember that json_mode_bootstrap() replaces the login cookie
    /* 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
206
207
208
209
210
211
212
213

214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232

233
234
235
236

237
238
239
240
241
242
243
207
208
209
210
211
212
213

214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232

233
234
235
236

237
238
239
240
241
242
243
244







-
+


















-
+



-
+







  }
  return json_page_whoami();
}

/*
** Implementation of the /json/anonymousPassword page.
*/
cson_value * json_page_anon_password(){
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(){
cson_value * json_page_whoami(void){
  cson_value * payload = NULL;
  cson_object * obj = NULL;
  Stmt q;
  if(!g.json.authToken){
  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);
  }

Changes to src/json_query.c.

36
37
38
39
40
41
42
43

44
45
46
47
48
49
50
36
37
38
39
40
41
42

43
44
45
46
47
48
49
50







-
+







**
** 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(){
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,

Changes to src/json_report.c.

20
21
22
23
24
25
26
27
28
29
30
31





32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

51
52
53
54
55
56
57
20
21
22
23
24
25
26





27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

50
51
52
53
54
55
56
57







-
-
-
-
-
+
+
+
+
+


















-
+







#include "json_report.h"

#if INTERFACE
#include "json_detail.h"
#endif


static cson_value * json_report_create();
static cson_value * json_report_get();
static cson_value * json_report_list();
static cson_value * json_report_run();
static cson_value * json_report_save();
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(){
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);
}
75
76
77
78
79
80
81
82

83
84
85
86
87

88
89
90
91
92
93
94
75
76
77
78
79
80
81

82
83
84
85
86

87
88
89
90
91
92
93
94







-
+




-
+







    if(arg && fossil_isdigit(*arg)) {
      nReport = atoi(arg);
    }
  }
  return nReport;
}

static cson_value * json_report_create(){
static cson_value * json_report_create(void){
  json_set_err(FSL_JSON_E_NYI, NULL);
  return NULL;
}

static cson_value * json_report_get(){
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.");
120
121
122
123
124
125
126
127

128
129
130
131
132
133
134
120
121
122
123
124
125
126

127
128
129
130
131
132
133
134







-
+







  db_finalize(&q);
  return pay;
}

/*
** Impl of /json/report/list.
*/
static cson_value * 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;
  }
156
157
158
159
160
161
162
163

164
165
166
167
168
169
170
156
157
158
159
160
161
162

163
164
165
166
167
168
169
170







-
+







** 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(){
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;
253
254
255
256
257
258
259
260

261
262
263
253
254
255
256
257
258
259

260
261
262
263







-
+



  pay = NULL;
  end:

  return pay ? cson_object_value(pay) : NULL;

}

static cson_value * json_report_save(){
static cson_value * json_report_save(void){
  return NULL;
}
#endif /* FOSSIL_ENABLE_JSON */

Changes to src/json_status.c.

58
59
60
61
62
63
64

65
66
67
68
69
70
71
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72







+







  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);
91
92
93
94
95
96
97
98


99
100
101
102
103

104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
92
93
94
95
96
97
98

99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119


120
121
122
123
124
125
126







-
+
+





+













-
-







#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)"
    "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( isRenamed ){
      zStatus = "renamed";
    }else if( !file_isfile_or_link(zFullName) ){
      if( file_access(zFullName, F_OK)==0 ){
        zStatus = "notAFile";
        ++nErr;
      }else{
        zStatus = "missing";
        ++nErr;
135
136
137
138
139
140
141
142
143
144













145
146
147
148


149
150
151
152
153
154
155
156
157
158
159
160

161
162
163
164
165
166
167
168
169
170
171
172
173

174
175
176
177
178
179
136
137
138
139
140
141
142

143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160

161
162
163
164
165
166
167
168
169
170
171
172


173
174
175
176
177
178
179
180
181
182
183
184
185

186
187
188
189
190
191
192







-


+
+
+
+
+
+
+
+
+
+
+
+
+



-
+
+










-
-
+












-
+






    }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", json_new_string( zStatus ) );
    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 uuid, id FROM vmerge JOIN blob ON merge=rid"
                 " WHERE id<=0");
  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_panic("aborting due to prior errors");
    fossil_fatal("aborting due to prior errors");
  }
#endif
  return cson_object_value( oPay );
}

#endif /* FOSSIL_ENABLE_JSON */

Changes to src/json_tag.c.

20
21
22
23
24
25
26
27
28
29
30




31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

48
49
50
51
52
53
54
55

56
57
58
59
60
61
62
20
21
22
23
24
25
26




27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

47
48
49
50
51
52
53
54

55
56
57
58
59
60
61
62







-
-
-
-
+
+
+
+
















-
+







-
+







#include "json_tag.h"

#if INTERFACE
#include "json_detail.h"
#endif


static cson_value * json_tag_add();
static cson_value * json_tag_cancel();
static cson_value * json_tag_find();
static cson_value * json_tag_list();
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(){
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(){
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;
115
116
117
118
119
120
121
122


123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143

144
145
146
147
148
149
150
115
116
117
118
119
120
121

122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143

144
145
146
147
148
149
150
151







-
+
+




















-
+







  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 UUID!");
      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(){
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,
186
187
188
189
190
191
192
193

194
195
196
197
198
199
200
187
188
189
190
191
192
193

194
195
196
197
198
199
200
201







-
+







  return NULL;
}


/*
** Impl of /json/tag/find.
*/
static cson_value * 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;
321
322
323
324
325
326
327
328

329
330
331
332
333
334
335
322
323
324
325
326
327
328

329
330
331
332
333
334
335
336







-
+







/*
** Impl for /json/tag/list
**
** TODOs:
**
** Add -type TYPE (ci, w, e, t)
*/
static cson_value * json_tag_list(){
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;

Changes to src/json_timeline.c.

20
21
22
23
24
25
26
27
28
29



30
31
32
33
34
35
36
37
38
39

40

41
42
43
44
45
46
47
48
49
50
51
52
53

54
55
56
57
58
59
60
20
21
22
23
24
25
26



27
28
29
30
31
32
33
34
35
36
37
38
39
40

41
42
43
44
45
46
47
48
49
50
51
52
53

54
55
56
57
58
59
60
61







-
-
-
+
+
+










+
-
+












-
+







#include "config.h"
#include "json_timeline.h"

#if INTERFACE
#include "json_detail.h"
#endif

static cson_value * json_timeline_branch();
static cson_value * json_timeline_ci();
static cson_value * json_timeline_ticket();
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},
{"event", 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(){
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.");
140
141
142
143
144
145
146
147
148


149
150
151
152
153
154
155
141
142
143
144
145
146
147


148
149
150
151
152
153
154
155
156







-
-
+
+







**
** 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 char json_timeline_add_tag_branch_clause(Blob *pSql,
                                                cson_object * pPayload){
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;
166
167
168
169
170
171
172
173


174
175
176
177
178
179
180
181
182


183
184
185
186
187
188
189
167
168
169
170
171
172
173

174
175
176
177
178
179
180
181
182
183

184
185
186
187
188
189
190
191
192







-
+
+








-
+
+







  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) );
    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"
               " 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"
217
218
219
220
221
222
223
224

225
226
227
228
229
230
231
220
221
222
223
224
225
226

227
228
229
230
231
232
233
234







-
+







** 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 char json_timeline_add_time_clause(Blob *pSql){
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){
349
350
351
352
353
354
355
356

357
358


359
360
361
362
363
364
365
366
367
368

369
370
371
372
373
374
375
352
353
354
355
356
357
358

359
360

361
362
363
364
365
366
367
368
369
370
371

372
373
374
375
376
377
378
379







-
+

-
+
+









-
+







    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)));
                  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. */
                        /* 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(){
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.");
442
443
444
445
446
447
448
449

450
451
452
453
454
455
456
446
447
448
449
450
451
452

453
454
455
456
457
458
459
460







-
+








/*
** Implementation of /json/timeline/ci.
**
** Still a few TODOs (like figuring out how to structure
** inheritance info).
*/
static cson_value * json_timeline_ci(){
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;
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
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







-
+




















-
+







    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." );
                "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(){
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;
545
546
547
548
549
550
551
552


553
554
555
556
557
558
559
560



561
562
563
564
565
566
567
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







-
+
+







-
+
+
+







  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))));
  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,"
             " 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");
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
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







-
+








-
+
+












-
+
+







  return payV;
}

/*
** Implementation of /json/timeline/wiki.
**
*/
cson_value * 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.");
    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))));
  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
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
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







-
+










-
+
+







  return payV;
}

/*
** Implementation of /json/timeline/ticket.
**
*/
static cson_value * 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.");
    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.");
723
724
725
726
727
728
729
730

731
732
733
734
735
736
737
733
734
735
736
737
738
739

740
741
742
743
744
745
746
747







-
+







    }

    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." );
                "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);

Changes to src/json_user.c.

19
20
21
22
23
24
25
26
27
28



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

47
48
49
50
51
52
53
54

55
56
57
58
59
60
61
19
20
21
22
23
24
25



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

46
47
48
49
50
51
52
53

54
55
56
57
58
59
60
61







-
-
-
+
+
+

















-
+







-
+







#include "config.h"
#include "json_user.h"

#if INTERFACE
#include "json_detail.h"
#endif

static cson_value * json_user_get();
static cson_value * json_user_list();
static cson_value * json_user_save();
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(){
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(){
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;
  }
120
121
122
123
124
125
126
127

128
129
130
131
132
133
134
120
121
122
123
124
125
126

127
128
129
130
131
132
133
134







-
+







  return u;
}


/*
** Impl of /json/user/get. Requires admin or setup rights.
*/
static cson_value * json_user_get(){
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;
  }
168
169
170
171
172
173
174
175


176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
168
169
170
171
172
173
174

175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191







192
193
194
195
196
197
198







-
+
+















-
-
-
-
-
-
-







** 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 ) ))
#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 0
  if(!g.perm.Admin && !g.perm.Setup && !g.perm.Password){
    return json_set_err( FSL_JSON_E_DENIED,
                         "Password change requires 'a', 's', "
                         "or 'p' permissions.");
  }
#endif
  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,
217
218
219
220
221
222
223

224
225
226

227
228
229
230
231
232
233
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229







+



+







      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);
350
351
352
353
354
355
356

357
358
359

360
361
362
363
364
365
366
367
368
369

370

371
372
373
374
375
376
377
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377







+



+










+

+







#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;
392
393
394
395
396
397
398
399

400
401
402
403
404

405
406
407
408
409
410
411
412
413
414
415
416
417
418

419
420
421
422
423
424
425
392
393
394
395
396
397
398

399
400
401
402
403

404
405
406
407
408
409
410
411
412
413
414
415
416
417

418
419
420
421
422
423
424
425







-
+




-
+













-
+







  return g.json.resultCode;
}


/*
** Impl of /json/user/save.
*/
static cson_value * 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;
  char b = -1;
  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)); } (void)0
  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

Changes to src/json_wiki.c.

19
20
21
22
23
24
25
26
27
28
29
30
31






32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

53
54
55
56
57
58
59
19
20
21
22
23
24
25






26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

52
53
54
55
56
57
58
59







-
-
-
-
-
-
+
+
+
+
+
+




















-
+







#include "config.h"
#include "json_wiki.h"

#if INTERFACE
#include "json_detail.h"
#endif

static cson_value * json_wiki_create();
static cson_value * json_wiki_get();
static cson_value * json_wiki_list();
static cson_value * json_wiki_preview();
static cson_value * json_wiki_save();
static cson_value * json_wiki_diff();
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(){
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.
161
162
163
164
165
166
167
168


169
170
171
172
173
174
175
161
162
163
164
165
166
167

168
169
170
171
172
173
174
175
176







-
+
+







}

/*
** 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){
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
242
243
244
245
246
247
248
249

250
251
252
253
254
255
256
257
258
259
260
261
262
263
264

265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280

281


282
283
284
285
286
287
288
289







290
291



292
293



294
295

296
297











298
299


300
301
302
303
304
305
306
243
244
245
246
247
248
249

250
251
252
253
254
255
256
257
258
259
260
261
262
263
264

265
266
267
268
269
270
271
272
273
274
275
276
277
278

279

280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298


299
300
301
302

303
304
305
306
307
308


309
310
311
312
313
314
315
316
317
318
319
320

321
322
323
324
325
326
327
328
329







-
+














-
+













-

-
+

+
+








+
+
+
+
+
+
+
-
-
+
+
+

-
+
+
+


+
-
-
+
+
+
+
+
+
+
+
+
+
+

-
+
+







  }
}

/*
** Implementation of /json/wiki/get.
**
*/
static cson_value * 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.");
            "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(){
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{
  zContent = cson_string_cstr(cson_value_get_string(g.json.reqPayload.v));
  if(!zContent) {
    sContent = cson_value_get_string(g.json.reqPayload.v);
  }
  if(!sContent) {
    json_set_err(FSL_JSON_E_MISSING_ARGS,
                 "The 'payload' property must be a string containing the wiki code to preview.");
                "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(cson_value_get_string(g.json.reqPayload.v)) );
  wiki_convert( &contentOrig, &contentHtml, 0 );
  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));
  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
323
324
325
326
327
328
329
330


331
332
333
334
335
336
337
346
347
348
349
350
351
352

353
354
355
356
357
358
359
360
361







-
+
+







                                             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. */
  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.",
426
427
428
429
430
431
432
433

434
435
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
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







-
+






-
+







-
+














-
-
+
+
+
+
+







  return payV;

}

/*
** Implementation of /json/wiki/create.
*/
static cson_value * 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(){
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(){
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"
              " substr(tagname,6) as name"
              " FROM tag WHERE tagname GLOB 'wiki-*'",
              " 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");
505
506
507
508
509
510
511
512

513
514
515
516
517
518
519
520
521

522
523
524
525
526
527
528
532
533
534
535
536
537
538

539
540
541
542
543
544
545
546
547

548
549
550
551
552
553
554
555







-
+








-
+







  cson_value_free(listV);
  listV = NULL;
  end:
  db_finalize(&q);
  return listV;
}

static cson_value * json_wiki_diff(){
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;
  u64 diffFlags;
  DiffConfig DCfg;
  char * zUuid = NULL;
  if( !g.perm.Hyperlink ){
    json_set_err(FSL_JSON_E_DENIED,
                 "Requires 'h' permissions.");
    return NULL;
  }

562
563
564
565
566
567
568
569
570


571
572
573
574
575
576
577
589
590
591
592
593
594
595


596
597
598
599
600
601
602
603
604







-
-
+
+







    goto manifest;
  }

  blob_init(&w1, pW1->zWiki, -1);
  blob_zero(&w2);
  blob_init(&w2, pW2->zWiki, -1);
  blob_zero(&d);
  diffFlags = DIFF_IGNORE_EOLWS | DIFF_STRIP_EOLCR;
  text_diff(&w1, &w2, &d, 0, diffFlags);
  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) );

Changes to src/leaf.c.

150
151
152
153
154
155
156











157
158
159
160
161
162
163
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174







+
+
+
+
+
+
+
+
+
+
+







    "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;

Deleted src/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















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
/* 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 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 ====================== */

/* 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);
            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));
    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));
    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. */
                if (write(l->ofd,&c,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) {
        if (len == maxlen) {
            if (maxlen == 0) maxlen = 16;
            maxlen *= 2;
            char *oldval = line;
            line = realloc(line,maxlen);
            if (line == NULL) {
                if (oldval) free(oldval);
                return NULL;
            }
        }
        int 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;
}

Deleted src/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









































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
/* 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);

#ifdef __cplusplus
}
#endif

#endif /* __LINENOISE_H */

Changes to src/loadctrl.c.

43
44
45
46
47
48
49
50
51



52
53
54



55
56







57
58
59
60

61
62

63
64
65
66
43
44
45
46
47
48
49


50
51
52
53
54

55
56
57
58
59
60
61
62
63
64
65
66
67
68
69

70
71

72
73
74
75
76







-
-
+
+
+


-
+
+
+


+
+
+
+
+
+
+



-
+

-
+




** Print the load average on the host machine.
*/
void loadavg_test_cmd(void){
  fossil_print("load-average: %f\n", load_average());
}

/*
** Abort the current operation of the load average of the host computer
** is too high.
** 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"));
  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 />
  @ <p>Current load average: %f(load_average()).<br>
  @ Load average limit: %f(mxLoad)</p>
  style_footer();
  style_finish_page();
  cgi_set_status(503,"Server Overload");
  cgi_reply();
  exit(0);
}

Changes to src/login.c.

47
48
49
50
51
52
53















54
55
56
57
58
59
60
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







#  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;
103
104
105
106
107
108
109
110

111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
118
119
120
121
122
123
124

125
126
127
128
129
130






















131
132
133
134
135
136
137







-
+





-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-







/*
** 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_redirect(zGoto);
    cgi_redirectf("%R/%s",zGoto);
  }else{
    fossil_redirect_home();
  }
}

/*
** The IP address of the client is stored as part of login cookies.
** But some clients are behind firewalls that shift the IP address
** with each HTTP request.  To allow such (broken) clients to log in,
** extract just a prefix of the IP address.
*/
static char *ipPrefix(const char *zIP){
  int i, j;
  static int ip_prefix_terms = -1;
  if( ip_prefix_terms<0 ){
    ip_prefix_terms = db_get_int("ip-prefix-terms",2);
  }
  if( ip_prefix_terms==0 ) return mprintf("0");
  for(i=j=0; zIP[i]; i++){
    if( zIP[i]=='.' ){
      j++;
      if( j==ip_prefix_terms ) break;
    }
  }
  return mprintf("%.*s", i, zIP);
}

/*
** 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){
164
165
166
167
168
169
170
171

172
173
174
175
176
177
178


179
180
181
182
183
184
185
186










187
188
189
190
191
192
193
194
195
196

197
198
199
200
201
202
203












204
205
206
207
208
209
210
157
158
159
160
161
162
163

164
165
166
167
168
169
170
171
172
173








174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194







195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213







-
+







+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+










+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+







  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 length(pw)>0 AND length(cap)>0");
                  " 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_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) ) return;
  create_accesslog_table();
  db_multi_exec(
    "INSERT INTO accesslog(uname,ipaddr,success,mtime)"
    "VALUES(%Q,%Q,%d,julianday('now'));",
    zUsername, zIpAddr, bSuccess
  );
  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.
**
220
221
222
223
224
225
226
227

228
229
230
231
232
233
234
223
224
225
226
227
228
229

230
231
232
233
234
235
236
237







-
+







** 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 length(cap)>0 AND length(pw)>0"
    "   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
  );

249
250
251
252
253
254
255
256

257
258
259
260
261
262
263
252
253
254
255
256
257
258

259
260
261
262
263
264
265
266







-
+







      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.
**
281
282
283
284
285
286
287






288
289
290
291
292


293
294
295
296
297


298
299
300
301
302
303
304
305
306
307
308
309

310
311
312


313
314
315


316
317
318


319
320

321
322
323
324
325
326
327
328
329
330
331
332

333
334
335
336
337
338
339


340
341


342
343
344
345
346

347
348
349
350
351
352
353

354
355

356
357
358
359

360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385

386
387
388
389

390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420

421
422
423
424
425




426
427
428
429



430
431
432
433
434

435
436
437
438
439



440

441
442
443
444
445
446
447
448


















449
450
451
452
453
454
455
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300

301
302
303
304
305


306
307
308
309

310
311
312
313
314

315
316

317
318
319

320
321
322


323
324
325


326
327


328
329
330
331
332
333
334
335
336
337
338
339

340




341
342
343
344
345
346

347
348
349
350
351
352

353




354
355

356
357

358
359
360
361

362
363
364
365
366
367

368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398

















399
400
401
402
403
404

405

406





407
408
409
410

411
412
413
414
415
416
417
418
419
420

421
422
423
424
425

426
427
428
429
430
431
432
433
434
435
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







+
+
+
+
+
+




-
+
+



-
-
+
+


-





-


-
+


-
+
+

-
-
+
+

-
-
+
+
-
-
+











-
+
-
-
-
-



+
+

-
+
+




-
+
-
-
-
-


-
+

-
+



-
+





-




















+




+





-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-






-

-
+
-
-
-
-
-
+
+
+
+
-



+
+
+




-
+




-
+
+
+

+








+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







**
** 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. */
  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");
  int expires = atoi(zExpire)*3600;
  char *zHash;
  const int expires = atoi(zExpire)*3600;
  char *zHash = 0;
  char *zCookie;
  const char *zIpAddr = PD("REMOTE_ADDR","nil"); /* IP address of user */
  char *zRemoteAddr = ipPrefix(zIpAddr);         /* Abbreviated IP address */

  assert((zUsername && *zUsername) && (uid > 0) && "Invalid user data.");
  zHash = db_text(0,
      "SELECT cookie FROM user"
      " WHERE uid=%d"
      "   AND ipaddr=%Q"
      "   AND cexpire>julianday('now')"
      "   AND length(cookie)>30",
      uid, zRemoteAddr);
      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(), expires);
  cgi_set_cookie(zCookieName, zCookie, login_cookie_path(),
                 bSessionCookie ? 0 : expires);
  record_login_attempt(zUsername, zIpAddr, 1);
  db_multi_exec(
                "UPDATE user SET cookie=%Q, ipaddr=%Q, "
  db_unprotect(PROTECT_USER);
  db_multi_exec("UPDATE user SET cookie=%Q,"
                "  cexpire=julianday('now')+%d/86400.0 WHERE uid=%d",
                zHash, zRemoteAddr, expires, uid
                );
                zHash, expires, uid);
  db_protect_pop();
  free(zRemoteAddr);
  free(zHash);
  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/IPADDR/SECRET, in which IPADDR
** Where HASH is the sha1sum of TIME/SECRET, in which SECRET is captcha-secret.
** is the abbreviated IP address and SECRET is captcha-secret.
**
** If either zIpAddr or zRemoteAddr are NULL then REMOTE_ADDR
** is used.
**
** If zCookieDest is not NULL then the generated cookie is assigned to
** *zCookieDest and the caller must eventually free() it.
**
** If bSessionCookie is true, the cookie will be a session cookie.
*/
void login_set_anon_cookie(const char *zIpAddr, char **zCookieDest ){
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 */
  char *zRemoteAddr;           /* Abbreviated IP address */
  int expires = bSessionCookie ? 0 : 6*3600;
  if(!zIpAddr){
    zIpAddr = PD("REMOTE_ADDR","nil");
  }
  zRemoteAddr = ipPrefix(zIpAddr);
  zCookieName = login_cookie_name();
  zNow = db_text("0", "SELECT julianday('now')");
  assert( zCookieName && zRemoteAddr && zIpAddr && zNow );
  assert( zCookieName && zNow );
  blob_init(&b, zNow, -1);
  blob_appendf(&b, "/%s/%s", zRemoteAddr, db_get("captcha-secret",""));
  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(), 6*3600);
  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);
  }
}

/*
** Return true if the prefix of zStr matches zPattern.  Return false if
** they are different.
**
** A lowercase character in zPattern will match either upper or lower
** case in zStr.  But an uppercase in zPattern will only match an
** uppercase in zStr.
*/
static int prefix_match(const char *zPattern, const char *zStr){
  int i;
  char c;
  for(i=0; (c = zPattern[i])!=0; i++){
    if( zStr[i]!=c && fossil_tolower(zStr[i])!=c ) return 0;
  }
  return 1;
}

/*
** 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){
  int i;
  if( zAgent==0 ) return 0;  /* If no UserAgent, then probably a bot */
  for(i=0; zAgent[i]; i++){
  if( strstr(zAgent, "bot")!=0 ) return 0;
    if( prefix_match("bot", zAgent+i) ) return 0;
    if( prefix_match("spider", zAgent+i) ) return 0;
    if( prefix_match("crawl", zAgent+i) ) return 0;
    /* If a URI appears in the User-Agent, it is probably a bot */
    if( strncmp("http", zAgent+i,4)==0 ) return 0;
  if( 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( sqlite3_strglob("*Safari/537.36Mozilla/5.0*", zAgent)==0 ) return 0;
    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("*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.
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
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







+
+
+
+
+
+
+
+
+












-
+







  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",""));
  pCap = capability_add(0, db_get("default-perms", "u"));
  capability_expand(pCap);
  rc = capability_has_any(pCap, zNeeded);
  capability_free(pCap);
  return rc;
}

/*
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
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







-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
-
-
-
-


+
+





-

-
+














+







  const char *zAnonPw = 0;
  const char *zGoto = P("g");
  int anonFlag;                /* Login as "anonymous" would be useful */
  char *zErrMsg = "";
  int uid;                     /* User id logged in user */
  char *zSha1Pw;
  const char *zIpAddr;         /* IP address of requestor */
  const char *zReferer;
  int noAnon = P("noanon")!=0;

  login_check_credentials();
  const int noAnon = P("noanon")!=0;
  int rememberMe;              /* If true, use persistent cookie, else
                                  session cookie. Toggled per
                                  checkbox. */
  if( login_wants_https_redirect() ){
    const char *zQS = P("QUERY_STRING");
    if( P("redir")!=0 ){
      style_header("Insecure Connection");
      @ <h1>Unable To Establish An Encrypted Connection</h1>
      @ <p>This website requires that login credentials be sent over
      @ an encrypted connection.  The current connection is not encrypted
      @ across the entire route between your browser and the server.
      @ An attempt was made to redirect to %h(g.zHttpsURL) but
      @ the connection is still insecure even after the redirect.</p>
      @ <p>This is probably some kind of configuration problem.  Please
      @ contact your sysadmin.</p>
      @ <p>Sorry it did not work out.</p>
      style_footer();
      return;
    }
    if( zQS==0 ){
      zQS = "?redir=1";

  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();
    }else if( zQS[0]!=0 ){
      zQS = mprintf("?%s&redir=1", zQS);
    }
    cgi_redirectf("%s%T%s", g.zHttpsURL, P("PATH_INFO"), zQS);
    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") ){
  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"
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
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







+
+
+
+
+
+
+
+



-







+
-
+
+
+
+
+
+
+
+
+

















-

+
+
+
+
+
+

-
+















+








-
+



+

















+










-
+
-
-


-
+




+

-
-
+
-
-
-
+
+
+
+

-
+

-
-
+
-
-
-



-
-
-
+
+
+
-
-





+
+
+
-
-
-
+
+
+
+
+
+
+
+
-

+
+


+
+
+
+
+
+
-
+






+
+
+
+
+
+



-



-
-
+
+








-
-
+
+












+
+
+
+
+
+
+
+

+




-
-
-
-
-
-
+
+
+
+
+
+
+
+
+

-
+




-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+












-
+
-







           @ 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
        );
        fossil_free(zNewPw);
        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);
        if( login_group_sql(zChngPw, "<p>", "</p>\n", &zErr) ){
        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 */
  zReferer = P("HTTP_REFERER");
  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);
    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);
      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)" />
    @ <input type="hidden" name="g" value="%h(zGoto)">
  }else if( zReferer && strncmp(g.zBaseURL, zReferer, strlen(g.zBaseURL))==0 ){
    @ <input type="hidden" name="g" value="%h(zReferer)" />
  }
  if( anonFlag ){
    @ <input type="hidden" name="anon" value="1" />
    @ <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{
    @ <table class="login_out">
    @ <tr>
    unsigned int uSeed = captcha_seed();
    @   <td class="form_label">User ID:</td>
    if( anonFlag ){
      @ <td><input type="text" id="u" name="u" value="anonymous" size="30"></td>
    if( g.zLogin==0 && (anonFlag || zGoto==0) ){
      zAnonPw = db_text(0, "SELECT pw FROM user"
                           " WHERE login='anonymous'"
                           "   AND cap!=''");
    }else{
      @ <td><input type="text" id="u" name="u" value="" size="30" /></td>
      zAnonPw = 0;
    }
    @ </tr>
    @ <tr>
    @ <table class="login_out">
    @  <td class="form_label">Password:</td>
    @  <td><input type="password" id="p" name="p" value="" size="30" /></td>
    @ </tr>
    if( P("HTTPS")==0 ){
      @ <tr><td class="form_label">Warning:</td>
      @ <td><span class='securityWarning'>
      @ Your password will be sent in the clear over an
      @ unencrypted connection.
      if( g.sslNotAvailable ){
      @ Login information, including the password,
      @ will be sent in the clear over an unencrypted connection.
      if( !g.sslNotAvailable ){
        @ No encrypted connection is available on this server.
      }else{
        @ 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" \
    if( g.zLogin==0 && (anonFlag || zGoto==0) ){
      zAnonPw = db_text(0, "SELECT pw FROM user"
                           " WHERE login='anonymous'"
    @ 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\"");
                           "   AND cap!=''");
    }
    @ </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"></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 ){
      unsigned int uSeed = captcha_seed();
      const char *zDecoded = captcha_decode(uSeed);
      int bAutoCaptcha = db_get_boolean("auto-captcha", 0);
      char *zCaptcha = captcha_render(zDecoded);
  
      @ <p><input type="hidden" name="cs" value="%u(uSeed)" />

      @ <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)' />
         style_load_one_js_file("login.js");
         @ 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">Old Password:</td>
      @ <td><input type="password" name="p" size="30" /></td></tr>
      @ <tr><td class="form_label">New Password:</td>
      @ <td><input type="password" name="n1" size="30" /></td></tr>
      @ <tr><td class="form_label">Repeat New Password:</td>
      @ <td><input type="password" name="n2" size="30" /></td></tr>
      @ <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>
      @ <td><input type="submit" value="Change Password"></td></tr>
      @ </table>
      @ </form>
    }
  }
  style_footer();
  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 */
  const char *zHash            /* HASH from login cookie HASH/CODE/LOGIN */
  const char *zRemoteAddr      /* Request comes from here */
){
  sqlite3 *pOther = 0;         /* The other repository */
  sqlite3_stmt *pStmt;         /* Query against the other repository */
  char *zSQL;                  /* SQL of the query against other repo */
  char *zOtherRepo;            /* Filename of the other repository */
  int rc;                      /* Result code from SQLite library functions */
  int nXfer = 0;               /* Number of credentials transferred */
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
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







-
-
-
+
+


-
+




+

-
+

-
+


+







    sqlite3_create_function(pOther,"now",0,SQLITE_UTF8,0,db_now_function,0,0);
    sqlite3_create_function(pOther, "constant_time_cmp", 2, SQLITE_UTF8, 0,
                  constant_time_cmp_function, 0, 0);
    sqlite3_busy_timeout(pOther, 5000);
    zSQL = mprintf(
      "SELECT cexpire FROM user"
      " WHERE login=%Q"
      "   AND ipaddr=%Q"
      "   AND length(cap)>0"
      "   AND length(pw)>0"
      "   AND octet_length(cap)>0"
      "   AND octet_length(pw)>0"
      "   AND cexpire>julianday('now')"
      "   AND constant_time_cmp(cookie,%Q)=0",
      zLogin, zRemoteAddr, zHash
      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, ipaddr=%Q, cexpire=%.17g"
        "UPDATE user SET cookie=%Q, cexpire=%.17g"
        " WHERE login=%Q",
        zHash, zRemoteAddr,
        zHash,
        sqlite3_column_double(pStmt, 0), zLogin
      );
      db_protect_pop();
      nXfer++;
    }
    sqlite3_finalize(pStmt);
  }
  sqlite3_close(pOther);
  fossil_free(zOtherRepo);
  return nXfer;
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
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







-
-
+
+


-
+
-



-
+
-






-

-
-
+
+

-
+



-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-





-
+








-
+

-
+
+
+







  if( fossil_strcmp(zLogin, "nobody")==0 ) return 1;
  if( fossil_strcmp(zLogin, "developer")==0 ) return 1;
  if( fossil_strcmp(zLogin, "reader")==0 ) return 1;
  return 0;
}

/*
** Lookup the uid for a non-built-in user with zLogin and zCookie and
** zRemoteAddr.  Return 0 if not found.
** 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) and zRemoteAddr (db: user.ipaddr)
** zCookie (db: user.cookie) entries.
** entries.
*/
static int login_find_user(
  const char *zLogin,            /* User name */
  const char *zCookie,           /* Login cookie value */
  const char *zCookie            /* Login cookie value */
  const char *zRemoteAddr        /* Abbreviated IP address for valid login */
){
  int uid;
  if( login_is_special(zLogin) ) return 0;
  uid = db_int(0,
    "SELECT uid FROM user"
    " WHERE login=%Q"
    "   AND ipaddr=%Q"
    "   AND cexpire>julianday('now')"
    "   AND length(cap)>0"
    "   AND length(pw)>0"
    "   AND octet_length(cap)>0"
    "   AND octet_length(pw)>0"
    "   AND constant_time_cmp(cookie,%Q)=0",
    zLogin, zRemoteAddr, zCookie
    zLogin, zCookie
  );
  return uid;
}

/*
** Return true if it is appropriate to redirect login requests to HTTPS.
**
** Redirect to https is appropriate if all of the above are true:
**    (1) The redirect-to-https flag is set
**    (2) The current connection is http, not https or ssh
**    (3) The sslNotAvailable flag is clear
*/
int login_wants_https_redirect(void){
  if( g.sslNotAvailable ) return 0;
  if( db_get_boolean("redirect-to-https",0)==0 ) return 0;
  if( P("HTTPS")!=0 ) return 0;
  return 1;
}


/*
** 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 logic_basic_authentication(const char *zIpAddr){
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 */
  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 */
  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++){}
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
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







+






-

-















-
+






+







-
+


-
+
+
+
+







** 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 */
  char *zRemoteAddr;            /* Abbreviated IP address of the requestor */
  const char *zCap = 0;         /* Capability string */
  const char *zPublicPages = 0; /* GLOB patterns of public pages */
  const char *zLogin = 0;       /* Login user for credentials */

  /* Only run this check once.  */
  if( g.userUid!=0 ) return;

  sqlite3_create_function(g.db, "constant_time_cmp", 2, SQLITE_UTF8, 0,
                  constant_time_cmp_function, 0, 0);

  /* If the HTTP connection is coming over 127.0.0.1 and if
  ** local login is disabled and if we are using HTTP and not HTTPS,
  ** then there is no need to check user credentials.
  **
  ** This feature allows the "fossil ui" command to give the user
  ** full access rights without having to log in.
  */
  zRemoteAddr = ipPrefix(zIpAddr = PD("REMOTE_ADDR","nil"));
  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 = "sx";
    zCap = "sxy";
    g.noPswd = 1;
    g.isHuman = 1;
    sqlite3_snprintf(sizeof(g.zCsrfToken), g.zCsrfToken, "localhost");
    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);
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
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







-
+





-
+
-




-
-
+
+










-
-
-
+
+
+



-
+









+
-
+








-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+










+
+
-
-
+
+
+

+
+
+
+
+
+
+
+







        }
      }
    }
    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/IPADDR/SECRET must match HASH.
      ** 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/%s",
      blob_appendf(&b, "%s/%s", zArg, db_get("captcha-secret",""));
                   zArg, zRemoteAddr, db_get("captcha-secret",""));
      sha1sum_blob(&b, &b);
      if( fossil_strcmp(zHash, blob_str(&b))==0 ){
        uid = db_int(0,
            "SELECT uid FROM user WHERE login='anonymous'"
            " AND length(cap)>0"
            " AND length(pw)>0"
            " AND 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, zRemoteAddr);
      if( uid==0 && login_transfer_credentials(zUser,zArg,zHash,zRemoteAddr) ){
        uid = login_find_user(zUser, zHash, zRemoteAddr);
      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);
      }
    }
    sqlite3_snprintf(sizeof(g.zCsrfToken), g.zCsrfToken, "%.10s", zHash);
    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",
                      " AND length(cap)>0 AND length(pw)>0", zRemoteUser);
                      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 = logic_basic_authentication(zIpAddr);
    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");
  }
    sqlite3_snprintf(sizeof(g.zCsrfToken), g.zCsrfToken, "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);
1165
1166
1167
1168
1169
1170
1171
1172
1173

1174
1175
1176
1177
1178







1179
1180
1181
1182
1183
1184
1185
1467
1468
1469
1470
1471
1472
1473


1474





1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488







-
-
+
-
-
-
-
-
+
+
+
+
+
+
+







  ** 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
  if( zCap[0] && !g.perm.Hyperlink && g.isHuman ){
   && g.isHuman
   && db_get_boolean("auto-hyperlink",1)
  ){
    g.perm.Hyperlink = 1;
    g.javascriptHyperlink = 1;
    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.
  **
1194
1195
1196
1197
1198
1199
1200
1201

1202
1203
1204

1205
1206
1207
1208
1209
1210
1211
1497
1498
1499
1500
1501
1502
1503

1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515







-
+



+







  */
  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);
      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;

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
1562
1563
1564
1565
1566
1567
1568

1569
1570


1571
1572

1573
1574
1575
1576
1577

1578
1579
1580
1581
1582
1583
1584







-
+

-
-
+
+
-





-







    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->Delete =
                             p->ModWiki = p->ModTkt =
                             p->RdForum = p->WrForum = p->ModForum =
                             p->WrTForum = p->AdminForum =
                             p->EmailAlert = p->Announce = p->Debug =
                             p->WrTForum = p->AdminForum = p->Chat =
                             p->EmailAlert = p->Announce = p->Debug = 1;
                             p->WrUnver = p->Private = 1;
                             /* Fall thru into Read/Write */
      case 'i':   p->Read = p->Write = 1;                      break;
      case 'o':   p->Read = 1;                                 break;
      case 'z':   p->Zip = 1;                                  break;

      case 'd':   p->Delete = 1;                               break;
      case 'h':   p->Hyperlink = 1;                            break;
      case 'g':   p->Clone = 1;                                break;
      case 'p':   p->Password = 1;                             break;

      case 'j':   p->RdWiki = 1;                               break;
      case 'k':   p->WrWiki = p->RdWiki = p->ApndWiki =1;      break;
      case 'm':   p->ApndWiki = 1;                             break;
1299
1300
1301
1302
1303
1304
1305

1306
1307
1308
1309
1310
1311
1312
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615







+







      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;
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
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







+
+
+











-
+







  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;
      case 'd':  rc = p->Delete;    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;
1382
1383
1384
1385
1386
1387
1388

1389




1390
1391
1392
1393
1394
1395
1396
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707







+

+
+
+
+







      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;
}

/*
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
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







-

+


+
-
-
+
+

-
+

-

-
+

+














-
-
+
+










-
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
-
-
+
+
+
+
+
+
+
+

+
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+













-


+
+
+



+




-
+


+
+
+
+
+
+
-
+
+



-
+

-
+









-
+


-
+


-
+

-
+



+
+
+
+
+
+
+






-
+


+
+
+






-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+






+
+
+
+
+
+
+
+





-
+

+

+

-
+




-




+
+
+

-
-
-
-
+
+
+
+

+

-
+

-
-
+
+

-
+
+







-







-
-
-


















-
+
-





-
+






+
+
+
-
+
+





+


-
+

-
+


-
-
+
+
+





-
-
+
+
+






-
-
+
+
+


-
+
+
+
+
+
+
+




-
-
+
+





-
-
+
+
+
+
+
+
+
+
+





-
-
+
+
+





-
-
+
+
+
+
+





-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







-
+
-







    json_err( FSL_JSON_E_DENIED, NULL, 1 );
    fossil_exit(0);
    /* NOTREACHED */
    assert(0);
  }else
#endif /* FOSSIL_ENABLE_JSON */
  {
    const char *zUrl = PD("REQUEST_URI", "index");
    const char *zQS = P("QUERY_STRING");
    const char *zPathInfo = PD("PATH_INFO","");
    Blob redir;
    blob_init(&redir, 0, 0);
    if( zPathInfo[0]=='/' ) zPathInfo++; /* skip leading slash */
    if( login_wants_https_redirect() && !g.sslNotAvailable ){
      blob_appendf(&redir, "%s/login?g=%T", g.zHttpsURL, zUrl);
    if( fossil_wants_https(1) ){
      blob_appendf(&redir, "%s/login?g=%T", g.zHttpsURL, zPathInfo);
    }else{
      blob_appendf(&redir, "%R/login?g=%T", zUrl);
      blob_appendf(&redir, "%R/login?g=%T", zPathInfo);
    }
    if( anonOk ) blob_append(&redir, "&anon", 5);
    if( zQS && zQS[0] ){
      blob_appendf(&redir, "&%s", zQS);
      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("REQUEST_URI", "index");
    @ <p>Many <span class="disabled">hyperlinks are disabled.</span><br />
    const char *zUrl = PD("PATH_INFO", "");
    @ <p>Many <span class="disabled">hyperlinks are disabled.</span><br>
    @ Use <a href="%R/login?anon=1&amp;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)" />
  @ <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);
    }
  }
}


/*
** Before using the results of a form, first call this routine 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.
** 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;
void login_verify_csrf_secret(void){
  if( g.okCsrf ) return;
  if( fossil_strcmp(P("csrf"), g.zCsrfToken)==0 ){
    g.okCsrf = 1;
    return;
  }
  fossil_fatal("Cross-site request forgery attempt");

  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;
  char *zCaptcha;
  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_footer();
    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");
  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,
  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(1) ){
  if( P("new")==0 || !cgi_csrf_safe(2) ){
    /* This is not a valid form submission.  Fall through into
    ** the form display */
  }else if( !captcha_is_correct(1) ){
  }else if( (captchaIsCorrect = captcha_is_correct(1))==0 ){
    iErrLine = 6;
    zErr = "Incorrect CAPTCHA";
  }else if( strlen(zUserID)<3 ){
  }else if( strlen(zUserID)<6 ){
    iErrLine = 1;
    zErr = "User ID too short. Must be at least 3 characters.";
    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_copy_addr(zEAddr,0)==0 ){
  }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( db_exists("SELECT 1 FROM user WHERE login=%Q", zUserID) ){
    iErrLine = 1;
    zErr = "This User ID is already taken. Choose something different.";
  }else if(
  }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) ){
      /* If the email is found anywhere in USER.INFO... */
      db_exists("SELECT 1 FROM user WHERE info LIKE '%%%q%%'", zEAddr)
    ||
      /* Or if the email is a verify subscriber email with an associated
      ** user... */
      db_exists(
        "SELECT 1 FROM subscriber WHERE semail=%Q AND suname IS NOT NULL"
        " AND sverified",zEAddr)
   ){
    iErrLine = 3;
    zErr = "This email address is already claimed by another user";
    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, zPerms, zDName, zEAddr, g.zIpAddr);
       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);
    login_set_user_cookie(zUserID, uid, NULL, 0);
    if( doAlerts ){
      /* Also make the new user a subscriber. */
      Blob hdr, body;
      AlertSender *pSender;
      sqlite3_int64 id;   /* New subscriber Id */
      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( g.perm.Read )    ssub[nsub++] = 'c';
      if( g.perm.RdForum ) ssub[nsub++] = 'f';
      if( g.perm.RdTkt )   ssub[nsub++] = 't';
      if( g.perm.RdWiki )  ssub[nsub++] = 'w';
      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. */
      db_multi_exec(
      zCode = db_text(0,
        "INSERT INTO subscriber(semail,suname,"
        "  sverified,sdonotcall,sdigest,ssub,sctime,mtime,smip)"
        " VALUES(%Q,%Q,%d,0,%d,%Q,now(),now(),%Q)"
        "  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",
        "   SET suname=excluded.suname"
        " RETURNING hex(subscriberCode);",
        /* semail */    zEAddr,
        /* suname */    zUserID,
        /* sverified */ 0,
        /* sdigest */   0,
        /* ssub */      ssub,
        /* smip */      g.zIpAddr
      );
      id = db_last_insert_rowid();
      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();
      }
      zCode = db_text(0,
           "SELECT hex(subscriberCode) FROM subscriber WHERE subscriberId=%lld",
           id);
      /* 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 on in order to activate your
        @ hyperlink that you must click to activate your account.</p>
        @ subscription.</p>
      }
      alert_sender_free(pSender);
      if( zGoto ){
        @ <p><a href='%h(zGoto)'>Continue</a>
      }
      style_footer();
      style_finish_page();
      return;
    }
    redirect_to_g();
  }

  /* Prepare the captcha. */
  if( captchaIsCorrect ){
    uSeed = strtoul(P("captchaseed"),0,10);
  }else{
  uSeed = captcha_seed();
    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"))" />
    @ <input type="hidden" name="g" value="%h(P("g"))">
  }
  @ <p><input type="hidden" name="captchaseed" value="%u(uSeed)" />
  @ <p><input type="hidden" name="captchaseed" value="%u(uSeed)">
  @ <table class="login_out">
  @ <tr>
  @   <td class="form_label" align="right">User ID:</td>
  @   <td><input type="text" name="u" value="%h(zUserID)" size="30"></td>
  @   <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'>&uarr; %h(zErr)</span></td></tr>
  }
  @ <tr>
  @   <td class="form_label" align="right">Display Name:</td>
  @   <td><input type="text" name="dn" value="%h(zDName)" size="30"></td>
  @   <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'>&uarr; %h(zErr)</span></td></tr>
  }
  @ </tr>
  @ <tr>
  @   <td class="form_label" align="right">Email Address:</td>
  @   <td><input type="text" name="ea" value="%h(zEAddr)" size="30"></td>
  @   <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'>&uarr; %h(zErr)</span></td></tr>
    @ <tr><td><td><span class='loginError'>&uarr; %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">Email&nbsp;Alerts?</td>
    @   <td><select size='1' name='alerts'>
    @   <td class="form_label" align="right" id="emalrt">Email&nbsp;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">Password:</td>
  @   <td><input type="password" name="p" value="%h(zPasswd)" size="30"></td>
  @   <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'>&uarr; %h(zErr)</span></td></tr>
  }
  @ <tr>
  @   <td class="form_label" align="right">Confirm:</td>
  @   <td><input type="password" name="cp" value="%h(zConfirm)" size="30"></td>
  @   <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'>&uarr; %h(zErr)</span></td></tr>
  }
  @ <tr>
  @   <td class="form_label" align="right">Captcha:</td>
  @   <td><input type="text" name="captcha" value="" size="30"></td>
  @   <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'>&uarr; %h(zErr)</span></td></tr>
  }
  @ <tr><td></td>
  @ <td><input type="submit" name="new" value="Register" /></td></tr>
  @ <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'>&uarr; %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'>&uarr; %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_footer();
  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.
**
1806
1807
1808
1809
1810
1811
1812

1813
1814
1815
1816

1817
1818
1819
1820
1821
1822
1823
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428







+




+







    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
    );
1857
1858
1859
1860
1861
1862
1863

1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478

2479
2480
2481
2482
2483
2484
2485







+









-







/*
** 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 *zPwHash;             /* Password hash on 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 */
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
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







+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+








+








  /* 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;
    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,
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


































































































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







+











+

+




















+







+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
  );
  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);

}

Changes to src/lookslike.c.

268
269
270
271
272
273
274
275

276
277
278
279
280
281
282
268
269
270
271
272
273
274

275
276
277
278
279
280
281
282







-
+







  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?) */
  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' ){
341
342
343
344
345
346
347
348

349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368

369
370

371
372
373
374



375
376

377
378
379
380
381
382
383
341
342
343
344
345
346
347

348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367

368
369
370
371
372
373


374
375
376
377

378
379
380
381
382
383
384
385







-
+



















-
+


+


-
-
+
+
+

-
+







*/
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( blob_size(pContent)<bomSize ) return 0;
  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 short *z = (unsigned short *)blob_buffer(pContent);
  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[1]==0 ) goto noBom;  /* No: possible UTF-32. */
  if( z[0]==0xfeff ){
  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( z[0]==0xfffe ){
  }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. */
  }
396
397
398
399
400
401
402
403
404


405
406
407
408
409
410
411
398
399
400
401
402
403
404


405
406
407
408
409
410
411
412
413







-
-
+
+








/*
** COMMAND: test-looks-like-utf
**
** Usage:  %fossil test-looks-like-utf FILENAME
**
** Options:
**    -n|--limit <num> Repeat looks-like function <num> times, for
**                     performance measurement. Default = 1;
**    -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){
456
457
458
459
460
461
462
























































































































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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
  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.

22
23
24
25
26
27
28










29
30
31
32
33
34
35

36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62









63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55






56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87

88
89
90
91
92
93
94







+
+
+
+
+
+
+
+
+
+







+









-
-
-
-
-
-












+
+
+
+
+
+
+
+
+











-







#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 defined(FOSSIL_ENABLE_MINIZ)
#  define MINIZ_HEADER_FILE_ONLY
#  include "miniz.c"
#else
#  include <zlib.h>
#endif
#if INTERFACE
#ifdef FOSSIL_ENABLE_TCL
#  include "tcl.h"
#endif
#ifdef FOSSIL_ENABLE_JSON
#  include "cson_amalgamation.h" /* JSON API. */
#  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 Delete;           /* d: delete wiki or tickets */
  char Password;         /* p: change password */
  char Query;            /* q: create new reports */
  char Write;            /* i: xfer inbound. check-in */
  char Read;             /* o: xfer outbound. check-out */
  char Hyperlink;        /* h: enable the display of hyperlinks */
  char Clone;            /* g: clone */
  char RdWiki;           /* j: view wiki via web */
94
95
96
97
98
99
100
101

102
103

104
105
106
107
108
109
110
107
108
109
110
111
112
113

114
115
116
117
118
119
120
121
122
123
124







-
+


+







  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: Set or remove capability 4 on other users */
  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
130
131
132
133
134
135
136

137
138


139
140
141
142
143
144
145

146
147
148
149
150
151
152

153
154
155

156
157
158
159
160
161
162
163
164

165
166
167
168
169
170

171
172
173

174
175
176
177


178
179
180
181
182
183
184
185
186
187
188
189
190


191
192
193
194

195
196
197
198

199
200
201


202
203






204
205
206
207
208


209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230






231
232
233
234
235
236
237
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161

162
163
164
165
166
167
168
169
170
171
172

173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192

193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222

223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240

241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262


263
264
265
266
267
268
269
270
271
272
273
274
275







+


+
+






-
+







+


-
+









+






+


-
+




+
+













+
+




+



-
+



+
+


+
+
+
+
+
+




-
+
+




















-
-
+
+
+
+
+
+







  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 */
  const char *zConfigDbName;/* Path of the config database. NULL if not open */
  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 UUID */
  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 */
  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 javascriptHyperlink; /* If true, set href= using script, not HTML */
  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 */
  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[12];    /* Value of the anti-CSRF token */
  int okCsrf;             /* Anti-CSRF token is present and valid */
  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 */
246
247
248
249
250
251
252

253
254
255
256
257
258
259
260



261



262
263
264
265
266
267
268
284
285
286
287
288
289
290
291
292
293
294
295
296
297


298
299
300
301
302
303
304
305
306
307
308
309
310
311







+






-
-
+
+
+

+
+
+







  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 exit() with
                                  code 0 to avoid an HTTP 500 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
297
298
299
300
301
302
303


304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319


320
321
322
323
324
325
326

327
328
329
330
331
332
333
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372

373
374
375
376
377
378
379
380







+
+
















+
+






-
+







      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__)
#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)
347
348
349
350
351
352
353



354
355
356
357
358
359
360
361





362

363
364
365
366
367
368
369
370
371
372
373
374
375
376
377

378
379
380
381
382

383
384
385
386
387
388
389
390
391
392
393
394
395

396
397

398
399
400
401
402
403

404
405

406
407
408
409




410

411
412
413
414






415
416
417
418
419
420








421
422
423
424
425
426







427
428
429
430


431

432
433
434
435
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
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432

433
434
435
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







+
+
+








+
+
+
+
+

+














-
+





+




-
-
-





-
+

-
+

-
-
-
-
-
+
-
-
+




+
+
+
+
-
+


-
-
+
+
+
+
+
+

-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
-



+
+
-
+



-
-
+
+
+
+
+
+
+
+
-
-
+
-
+
+










+
+
+











-
+



+
+







  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.
*/
static void expand_args_option(int argc, void *argv){
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 */
#if defined(_WIN32)
  wchar_t buf[MAX_PATH];
#endif

  g.argc = argc;
  g.argv = argv;
  sqlite3_initialize();
#if defined(_WIN32) && defined(BROKEN_MINGW_CMDLINE)
  for(i=0; i<g.argc; i++) g.argv[i] = fossil_mbcs_to_utf8(g.argv[i]);
  for(i=0; (int)i<g.argc; i++) g.argv[i] = fossil_mbcs_to_utf8(g.argv[i]);
#else
  for(i=0; i<g.argc; i++) g.argv[i] = fossil_path_to_utf8(g.argv[i]);
  for(i=0; (int)i<g.argc; i++) g.argv[i] = fossil_path_to_utf8(g.argv[i]);
#endif
#if defined(_WIN32)
  GetModuleFileNameW(NULL, buf, MAX_PATH);
  g.nameOfExe = fossil_path_to_utf8(buf);
#else
  g.nameOfExe = g.argv[0];
  g.nameOfExe = file_fullexename(g.argv[0]);
#endif
  for(i=1; i<g.argc-1; i++){
  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
    if( z[0]==0 ) return;   /* Stop searching at "--" */
    ** differently when we stop at "--" here. */
    if( fossil_strcmp(z, "args")==0 ) break;
  }
  if( i>=g.argc-1 ) return;

  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];
  inFile = (0==strcmp("-",zFileName))
    ? stdin
    : fossil_fopen(zFileName,"rb");
  if(!inFile){
    fossil_fatal("Cannot open -args file [%s]", zFileName);
  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);
  }else{
    blob_read_from_channel(&file, inFile, -1);
    if(stdin != inFile){
      fclose(inFile);
    }
    inFile = NULL;
    }
  }
  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*)*(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( (n = blob_line(&file, &line))>0 ){
    if( n<1 ) continue
  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.
      /* 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( i<g.argc ) newArgv[j++] = g.argv[i++];
  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){
564
565
566
567
568
569
570



571
572
573
574

575
576
577
578
579
580
581
637
638
639
640
641
642
643
644
645
646
647
648
649

650
651
652
653
654
655
656
657







+
+
+



-
+







** 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_DEFAULT;
    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.
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
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







+
+
+
-
-
-
+
+
+
-
-
-
-
-
+
+
+
+
+

-
+
+
+
+
+
+
+




+

















+

+
+
+
+
-
-
+
+
+
+


+







    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(). */
#if defined(_WIN32) && !defined(BROKEN_MINGW_CMDLINE)
int _dowildcard = -1; /* This turns on command-line globbing in MinGW-w64 */
int wmain(int argc, wchar_t **argv)
#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); }
#else
#if defined(_WIN32)
int _CRT_glob = 0x0001; /* See MinGW bug #2062 */
#endif
int main(int 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()<3014000 ){
    fossil_panic("Unsuitable SQLite version %s, must be at least 3.14.0",
  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)
667
668
669
670
671
672
673
674

675
676
677
678
679
680
681
761
762
763
764
765
766
767

768
769
770
771
772
773
774
775







-
+







    sqlite3_vfs *pVfs = sqlite3_vfs_find(g.zVfsName);
    if( pVfs ){
      sqlite3_vfs_register(pVfs, 1);
    }else{
      fossil_fatal("no such VFS: \"%s\"", g.zVfsName);
    }
  }
  if( fossil_getenv("GATEWAY_INTERFACE")!=0 && !find_option("nocgi", 0, 0)){
  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",
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
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







+
+
+
















+
+
+






-
+


+


-
-
-
+
+
+










+
+
+
+
+
+
+
+
+
+







    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+2) );
      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 = 3;
          zNewArgv[2] = g.argv[i];
          zNewArgv[3] = 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
769
770
771
772
773
774
775

776















777
778
779
780
781
782
783
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







+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







      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;
    }
794
795
796
797
798
799
800
801

802
803
804
805
806
807







808
809
810
811
812
813
814
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







-
+






+
+
+
+
+
+
+







      }
    }
    fossil_exit(0);
#endif
  }else if( rc==2 ){
    Blob couldbe;
    blob_init(&couldbe,0,0);
    dispatch_matching_names(zCmdName, &couldbe);
    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.
  **
828
829
830
831
832
833
834

835

836
837
838
839
840
841
842
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978







+

+







    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
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
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







-
-
+
+
+
+

+
+
+
+

-
-
+
+
+
+
+
+
+
+









-





-
+
+










+





+







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+









-
+
+
+
+







    g.argv[i] = g.argv[j];
  }
  g.argc = i;
}


/*
** Look for a command-line option.  If present, return a pointer.
** Return NULL if missing.
** 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.  Return a pointer to the
** argument.
** 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;
    if( i+hasArg >= g.argc ) break;
    z = g.argv[i];
    if( z[0]!='-' ) continue;
    z++;
    if( z[0]=='-' ){
      if( z[1]==0 ){
        remove_from_argv(i, 1);
        /* 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 to free allocated memory.
** 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;
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
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







+
+
+
+
+
+
+
+
+
+
+
+




+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+



-















-
+

-
+









-
+
+


+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+




-
-
-
-
-



-
-
-
-
-
-
-






-

-








-
-
-
-
-
-
-
-













+

















-
+
+




-
+
-







  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( g.argv[i][0]=='-' && g.argv[i][1]!=0 ){
      fossil_fatal(
        "unrecognized command-line option, or missing argument: %s",
        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.
*/
static void get_version_blob(
void fossil_version_blob(
  Blob *pOut,                 /* Write the manifest here */
  int bVerbose                /* Non-zero for full information. */
  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( !bVerbose ) return;
  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 defined(FOSSIL_ENABLE_MINIZ)
  blob_appendf(pOut, "miniz %s, loaded %s\n", MZ_VERSION, mz_version());
#else
  blob_appendf(pOut, "zlib %s, loaded %s\n", ZLIB_VERSION, zlibVersion());
#endif
#if FOSSIL_HARDENED_SHA1
  blob_appendf(pOut, "hardened-SHA1 by Marc Stevens and Dan Shumow\n");
#endif
#if defined(FOSSIL_ENABLE_SSL)
  blob_appendf(pOut, "SSL (%s)\n", SSLeay_version(SSLEAY_VERSION));
#endif
#if defined(FOSSIL_HAVE_FUSEFS)
  blob_appendf(pOut, "libfuse %s, loaded %s\n", fusefs_inc_version(),
               fusefs_lib_version());
#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
#if defined(FOSSIL_ENABLE_LEGACY_MV_RM)
  blob_append(pOut, "FOSSIL_ENABLE_LEGACY_MV_RM\n", -1);
#endif
#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(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 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_append(pOut, "USE_SEE\n", -1);
  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
  blob_appendf(pOut, "SQLite %s %.30s\n", sqlite3_libversion(),

               sqlite3_sourceid());
  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);
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
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







-
+




-
+
+
+



-
+
+
+
+



-
+



















-
+


-
+



-
+













-
+









+
+
+



















+






+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+




-
-
+
+


-
-
-
+
+
+

+

+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+

-
+



-
+


+
+







+
+
+
+
+
+
+
+
-
+







-
-
+
+
+
+
+








-
+














+
+
+
+
+
+
+
+
+
+
+
+
+



-
-


-
+



-
+



















-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-















-
+
+

+
+
+
+
+
+
+
-
+
+



-
+
+














+



+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







  return version;
}


/*
** COMMAND: version
**
** Usage: %fossil version ?-verbose|-v?
** 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
** with.
**
** Repeat the -v option or use -vv for even more information.
*/
void version_cmd(void){
  Blob versionInfo;
  int verboseFlag = find_option("verbose","v",0)!=0;
  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();
  get_version_blob(&versionInfo, verboseFlag);
  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 = PD("verbose", 0) != 0;
  verboseFlag = P("verbose")!=0 ? 2 : 1;
  style_header("Version Information");
  style_submenu_element("Stat", "stat");
  get_version_blob(&versionInfo, verboseFlag);
  fossil_version_blob(&versionInfo, verboseFlag);
  @ <pre>
  @ %h(blob_str(&versionInfo))
  @ </pre>
  style_footer();
  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.
*/
static void set_base_url(const char *zAltBase){
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--;
    zMode = PD("HTTPS","off");
    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", zHost, i, zCur);
      g.zTop = &g.zBaseURL[8+strlen(zHost)];
      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", zHost, i, zCur);
      g.zTop = &g.zBaseURL[7+strlen(zHost)];
      g.zHttpsURL = mprintf("https://%s%.*s", zHost, i, zCur);
      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") ){
    if( !db_exists("SELECT 1 FROM config WHERE name='baseurl:%q'", g.zBaseURL)){
  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())", g.zBaseURL);
                    "VALUES('baseurl:%q',1,now())", zBase);
    }else{
      db_optional_sql("repository",
           "REPLACE INTO config(name,value,mtime)"
           "VALUES('baseurl:%q',1,now())", g.zBaseURL
           "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("%s%s", g.zTop, db_get("index-page", "/index"));
  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 might be a directory itself.  In that case chroot into
** the directory zRepo.
** 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.
*/
char *enter_chroot_jail(char *zRepo, int noJail){
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);
        }
        g.fJail = 1;
        zRepo = "/";
      }else{
        for(i=strlen(zDir)-1; i>0 && zDir[i]!='/'; i--){}
        if( zDir[i]!='/' ) fossil_panic("bad repository name: %s", zRepo);
        if( zDir[i]!='/' ) fossil_fatal("bad repository name: %s", zRepo);
        if( i>0 ){
          zDir[i] = 0;
          if( file_chdir(zDir, 1) ){
            fossil_panic("unable to chroot into %s", zDir);
            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 zRepo;
}

  return (char*)zRepo;  /* no longer const: always reassigned from blob_str() */
/*
** 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 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,
** return 0.
*/
static int repo_list_page(void){
  Blob base;
  int n = 0;
  int allRepo;

  assert( g.db==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);
    db_multi_exec("DELETE FROM sfile WHERE pathname NOT GLOB '*[^/].fossil'");
    allRepo = 0;
  }
  @ <html>
  @ <head>
  @ <base href="%s(g.zBaseURL)/" />
  @ <meta name="viewport" content="width=device-width, initial-scale=1.0">
  @ <title>Repository List</title>
  @ </head>
  @ <body>
  n = db_int(0, "SELECT count(*) FROM sfile");
  if( n>0 ){
    Stmt q;
    sqlite3_int64 iNow, iMTime;
    @ <h1 align="center">Fossil Repositories</h1>
    @ <table border="0" class="sortable" data-init-sort="1" \
    @ data-column-types="tnk"><thead>
    @ <tr><th>Filename<th width="20"><th>Last Modified</tr>
    @ </thead><tbody>
    db_prepare(&q, "SELECT pathname"
                   " FROM sfile ORDER BY pathname COLLATE nocase;");
    iNow = db_int64(0, "SELECT strftime('%%s','now')");
    while( db_step(&q)==SQLITE_ROW ){
      const char *zName = db_column_text(&q, 0);
      int nName = (int)strlen(zName);
      char *zUrl;
      char *zAge;
      char *zFull;
      if( nName<7 ) continue;
      zUrl = sqlite3_mprintf("%.*s", nName-7, 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);
      }
      iMTime = file_mtime(zFull, ExtFILE);
      fossil_free(zFull);
      if( iMTime<=0 ){
        zAge = mprintf("...");
      }else{
        zAge = human_readable_age((iNow - iMTime)/86400.0);
      }
      if( sqlite3_strglob("*.fossil", 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. */
        @ <tr><td>%h(zName)
      } else if( sqlite3_strglob("*/.*", zName)==0 ){
        /* Do not show hidden repos */
        @ <tr><td>%h(zName) (hidden)
      } else if( allRepo && sqlite3_strglob("[a-zA-Z]:/?*", zName)!=0 ){
        @ <tr><td><a href="%R/%T(zUrl)/home" target="_blank">/%h(zName)</a>
      }else{
        @ <tr><td><a href="%R/%T(zUrl)/home" target="_blank">%h(zName)</a>
      }
      @ <td></td><td data-sortkey='%010llx(iNow - iMTime)'>%h(zAge)</tr>
      fossil_free(zAge);
      sqlite3_free(zUrl);
    }
    @ </tbody></table>
  }else{
    @ <h1>No Repositories Found</h1>
  }
  @ <script>%s(builtin_text("sorttable.js"))</script>
  @ </body>
  @ </html>
  cgi_reply();
  sqlite3_close(g.db);
  g.db = 0;
  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();
}

/*
** 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");
  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, "\n(%d) %s", i, strings[i]);
      blob_appendf(&out, " %s", z);
    }
  }
  fossil_panic("%s", blob_str(&out));
#else
  fossil_panic("Segfault");
  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,
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
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







+

+










+
+
+
+
+
+
+
+
+
+
+
+
-
+
+




+
+
















+




-
+















-
+













+
+
+
+
+
+







  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.fossil",zBase,i,zPathInfo);
      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=strlen(zBase)+1, k=0; zRepo[j] && k<i-1; j++, k++){
      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)" -->
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
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







-












-
+







      ** 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 ){
          char zBuf[24];
          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( fossil_strcmp(&zRepo[j], ".fossil")==0 );
        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++;
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
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







-
-
+
+










+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





-
+



+
+
+
+
+
+
+

-
+






-
-
-
-
-
-
-
-
-
-
-
-
-
-
+











-
+




+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







        ** 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))
         && sqlite3_strglob("*.fossil*",zRepo)!=0
         && 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 an
      ** 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( strcmp(zPathInfo,"/")==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{
#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();
          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("%s%.*s", g.zTop, 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"
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
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







-
-
-
+
+
+
+
+
+
+







+
+
+
+
+


-
+


+


+


-
+















+



















-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-













-
+















-
+







        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 the PATH_INFO begins with "draft[1-9]" and if
  ** so activate the special handling for draft skins
  */

  /*
  ** 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("%s/draft%d", g.zTop, 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 
  /* 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.
  */
  while(1){
    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;
    }
  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;
  }
    break;
  }
#ifdef FOSSIL_ENABLE_JSON
  /*
  ** Workaround to allow us to customize some following behaviour for
  ** JSON mode.  The problem is, we don't always know if we're in JSON
  ** mode at this point (namely, for GET mode we don't know but POST
  ** we do), so we snoop g.zPath and cheat a bit.
  */
  if( !g.json.isJsonMode && g.zPath && (0==strncmp("json",g.zPath,4)) ){
    g.json.isJsonMode = 1;
  }
#endif
  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){
    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){
    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 ){
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
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







-
+









+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


-
+







          Th_WebpageNotify(g.zPath, 0);
        }
      }
#endif
    }
  }else if( pCmd->xFunc!=page_xfer && db_schema_is_outofdate() ){
#ifdef FOSSIL_ENABLE_JSON
    if(g.json.isJsonMode){
    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);
      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.
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
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







+









+
+
+




+








-
-
-
+
+
+
+





-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







-
+


-
+




-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+

+
+
+
-
+
+
+
+
+
+
+
+
+
+
+







        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" CGI parameter and for the
** first match, redirect to the corresponding URL with the "name" CGI
** parameter inserted.  Paint an error page if no match is found.
** 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.  Otherwise a
** very primitive error message is returned.
** 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("SCRIPT_NAME");
    zName = P("PATH_INFO");
    if( zName && zName[0]=='/' ) zName++;
  }
  if( zName && validate16(zName, strlen(zName)) ){
  if( zName ){
    for(i=0; i<nRedirect; i++){
      if( fossil_strcmp(azRedirect[i*2],"*")==0 ){
        zNotFound = azRedirect[i*2+1];
        continue;
      }
      db_open_repository(azRedirect[i*2]);
      if( db_exists("SELECT 1 FROM blob WHERE uuid GLOB '%q*'", zName) ||
          db_exists("SELECT 1 FROM ticket WHERE tkt_uuid GLOB '%q*'", zName) ){
        cgi_redirectf(azRedirect[i*2+1] /*works-like:"%s"*/, zName);
        return;
      }
      db_close(1);
    }
  }
      }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);
      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();
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
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







+
+
+
+
















-
+



+
+
+
+
+
+







+
+
+
+
+
+
+




+
+
+
+
+
-
+


-






-
+
-
-
-
-





+
+
+
+
+
+
+
+
+
+
+
-
+







**
**    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"
**
**    debug: FILE              Causing debugging information to be written
**    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
** See also: [[http]], [[server]], [[winsrv]]
*/
void cmd_cgi(void){
  const char *zFile;
  const char *zNotFound = 0;
  char **azRedirect = 0;             /* List of repositories to redirect to */
  int nRedirect = 0;                 /* Number of entries in azRedirect */
  Glob *pFileGlob = 0;               /* Pattern for files */
  int allowRepoList = 0;             /* Allow lists of repository files */
  Blob config, line, key, value, value2;
  if( g.argc==3 && fossil_strcmp(g.argv[1],"cgi")==0 ){
  /* Initialize the CGI environment. */
    zFile = g.argv[2];
  }else{
    zFile = g.argv[1];
  }
  g.httpOut = stdout;
  g.httpIn = stdin;
  fossil_binary_mode(g.httpOut);
  fossil_binary_mode(g.httpIn);
  g.cgiOutput = 1;
  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, zFile, ExtFILE);
  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
2094
2095
2096
2097
2098
2099
2100
















2101
2102
2103
2104
2105
2106
2107
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







      /* 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.
      */
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
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







-
-
-
-
-
-
-
-
-
-









+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+











-
+





-
+
+
+
+

+
-
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







      */
      blob_token(&line,&value2);
      fossil_setenv(blob_str(&value), blob_str(&value2));
      blob_reset(&value);
      blob_reset(&value2);
      continue;
    }
    if( blob_eq(&key, "debug:") && blob_token(&line, &value) ){
      /* debug: FILENAME
      **
      ** Causes output from cgi_debug() and CGIDEBUG(()) calls to go
      ** into FILENAME.
      */
      g.fDebug = fossil_fopen(blob_str(&value), "ab");
      blob_reset(&value);
      continue;
    }
    if( blob_eq(&key, "errorlog:") && blob_token(&line, &value) ){
      /* errorlog: FILENAME
      **
      ** Causes messages from warnings, errors, and panics to be appended
      ** to FILENAME.
      */
      g.zErrlog = mprintf("%s", blob_str(&value));
      blob_reset(&value);
      continue;
    }
    if( blob_eq(&key, "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:") && blob_token(&line, &value) ){
    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.
      ** 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);
      skin_use_alternative(blob_str(&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");
  }
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
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







-
-
+
+







+










-
+
















-
-
-
-
+
+
+
+

+

-
+
+






+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+








-
-
+
+














-
-
-
-
-
-
-
-
-

+
-
-
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+









+



-
-
-


+















+





+
+
+


+




+
+
+







+
+
+



+









-
-
-
-
+
+
-
-
-
-
-
+

-
+
+



+
+
+
+
+
+
+
+
+
+
+
+











-
+
+





+
+
+
+
+



+
+
+
+
+
+
















-
-


-
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
-
+
+




+
+


-
-
+
+
+
+
+
+
+


+
+
+

+



+
-
+






-
+



-
-

-
-
+
+

+
-
+
-
-
-
-
+
+
-
-
-
-
-
-
-
+
+
-
-
-
-
+
+
+
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+











-
-
-
+
+
+
+





+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+













-
+

-
-
+
+
+









+

+
+
+
+
+

-
+
+

+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


+
+


-
-
-
+
+
+
+
+
+
+
-
-
+


+

-
+

-
+










+

-
+






+

+
+
+
+
-
-
+
+
+
+










+
+
+










+

-
+






-
+
+
+






+
-
+
+
+
+



-
-
+
+
+
+





+
+
+
+
-
+
-
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+

-
+




+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





+
-
+
+







      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_AUTO;
        db_set_int("hash-policy", HPOLICY_AUTO, 0);
        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 defined(_WIN32) && USE_SEE
#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. */
  DWORD *pProcessId,   /* The extracted process identifier. */
  LPVOID *ppAddress,   /* The extracted pointer value. */
  SIZE_T *pnSize       /* The extracted size 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", pProcessId, ppAddress, &nSize)==3 ){
  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 argument is the name of the
** repository.
** 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.
**
** The --host option can be used to specify the hostname for the server.
** The --https option indicates that the request came from HTTPS rather
** than HTTP. If --nossl is given, then SSL connections will not be available,
** thus also no redirecting from http: to https: will take place.
**
** If the --localauth option is given, then automatic login is performed
** for requests coming from localhost, if the "localauth" setting is not
** enabled.
**
** Options:
**   --acme              Deliver files from the ".well-known" subdirectory
**   --baseurl URL    base URL (useful with reverse proxies)
**   --files GLOB     comma-separate glob patterns for static file to serve
**   --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
**   --localauth      enable automatic login for local connections
**   --host NAME      specify hostname of the server
**   --https          signal a request coming in via https
**   --in FILE        Take input from FILE instead of standard input
**   --ipaddr ADDR    Assume the request comes from the given IP address
**   --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          signal that no SSL connections are available
**   --notfound URL   use URL as "HTTP 404, object not found" page.
**   --out FILE       write results to FILE instead of to standard output
**   --repolist       If REPOSITORY is directory, URL "/" lists all repos
**   --scgi           Interpret input as 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.
**   --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
** 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;
#if defined(_WIN32) && USE_SEE
  const char *zPidKey;
#endif

  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);

#if defined(_WIN32) && USE_SEE
  zPidKey = find_option("usepidkey", 0, 1);
  if( zPidKey ){
  g.zMainMenuFile = find_option("mainmenu",0,1);
  if( g.zMainMenuFile!=0 && file_size(g.zMainMenuFile,ExtFILE)<0 ){
    DWORD processId = 0;
    LPVOID pAddress = NULL;
    SIZE_T nSize = 0;
    parse_pid_key_value(zPidKey, &processId, &pAddress, &nSize);
    db_read_saved_encryption_key_from_process(processId, pAddress, nSize);
    fossil_fatal("Cannot read --mainmenu file %s", g.zMainMenuFile);
  }
#endif
  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(g.zRepositoryName, noJail);
  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 );
}

/*
** Note that the following command is used by ssh:// processing.
**
** COMMAND: test-http
**
** Works like the http command but gives setup permission to all users.
** 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)
**   --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();
  login_set_capabilities("sx", 0);
  g.useLocalauth = 1;
  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 = cgi_ssh_remote_addr(0);
  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, 0);
    process_one_web_page(0, 0, 1);
  }
}

#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.
** Respond to a SIGALRM by writing a message to the error log (if there
** is one) and exiting.
*/
#ifndef _WIN32
static int binaryOnPath(const char *zBinary){
static int nAlarmSeconds = 0;
  const char *zPath = fossil_getenv("PATH");
  char *zFull;
  int i;
  int bExists;
static void sigalrm_handler(int x){
  sqlite3_uint64 tmUser = 0, tmKernel = 0;
  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;
  fossil_cpu_times(&tmUser, &tmKernel);
  if( fossil_strcmp(g.zPhase, "web-page reply")==0
    zPath += i;
  }
  return 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;
  }
#endif
#endif

/*
** Send a time-out reply
*/
void sigalrm_handler(int x){
  fossil_panic("TIMEOUT");
  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 argument is the name of the repository.
** The repository argument may be omitted if the working directory is
** within an open checkout.
** --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".
**
** The REPOSITORY can be a directory (aka folder) that contains one or
** 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 list global configuration
** 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.  See also the "fossil all ui"
** command.
** 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
**   --page PAGE         Start "ui" on PAGE.  ex: --page "timeline?y=ci"
**   --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             signal a request coming in via https
**   --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             signal that no SSL connections are available
**   --notfound URL      Redirect
**   -P|--port TCPPORT   listen to request on port TCPPORT
**   --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
**   --th-trace          trace TH1 execution (for debugging purposes)
**   --repolist          If REPOSITORY is dir, URL "/" lists repos.
**   --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.
**                       only necessary when using SEE on Windows or Linux.
**
** See also: cgi, http, winsrv
** 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 *zMaxLatency;   /* Maximum runtime of any single HTTP request */
  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 defined(_WIN32) && USE_SEE
  const char *zPidKey;


#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;
  zMaxLatency = find_option("max-latency",0,1);
  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", 0, 1);
    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 ) flags |= HTTP_SERVER_SCGI;
  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;
  if( find_option("https",0,0)!=0 ){
  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 defined(_WIN32) && USE_SEE
  zPidKey = find_option("usepidkey", 0, 1);
  if( find_option("acme",0,0)!=0 ) g.fAllowACME = 1;
  if( zPidKey ){
    DWORD processId = 0;
    LPVOID pAddress = NULL;
    SIZE_T nSize = 0;
    parse_pid_key_value(zPidKey, &processId, &pAddress, &nSize);
    db_read_saved_encryption_key_from_process(processId, pAddress, nSize);
  }

  /* 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(2, fCreate);
    find_server_repository(findServerArg, fCreate);
  }
  if( zInitPage==0 ){
    if( isUiCmd && g.localOpen ){
      zInitPage = "timeline?c=current";
    }else{
      zInitPage = "";
    }
  }
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
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







+




-
-
-
+
-
-
-
-
-
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
-
-
-
+
+
+
+
-
-
+
-
-
+
+
+
+
+
+
+




+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



+
+
+
+
+
+
+
+
+
-
+
-
-
+
+
+



-
-


-
-








-
+
+



+
+
+
+
+








-
-
-
+
-
-
+
-
-
+
-
-
+
-
-
-
-
-
-
+
+
+
-
-
-
+
+







        }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 !defined(_WIN32)
  /* Unix implementation */
  if( isUiCmd ){
  if( isUiCmd && !fNoBrowser ){
#if !defined(__DARWIN__) && !defined(__APPLE__) && !defined(__HAIKU__)
    zBrowser = db_get("web-browser", 0);
    if( zBrowser==0 ){
      static const char *const azBrowserProg[] =
          { "xdg-open", "gnome-open", "firefox", "google-chrome" };
    char *zBrowserArg;
    const char *zProtocol = g.httpUseSSL ? "https" : "http";
    db_open_config(0,0);
      int i;
      zBrowser = "echo";
      for(i=0; i<count(azBrowserProg); i++){
        if( binaryOnPath(azBrowserProg[i]) ){
          zBrowser = azBrowserProg[i];
          break;
    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);
    }
      } 
#else
    zBrowser = db_get("web-browser", "open");
#endif
    if( zIpAddr==0 ){
      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 ){
      zBrowserCmd = mprintf("%s http://localhost:%%d/%s &",
                            zBrowser, zInitPage);
            ssh_needs_path_argument(zRemote,99);
          }
          db_close_config();
          if( zBrowserCmd ){
    }else if( strchr(zIpAddr,':') ){
      zBrowserCmd = mprintf("%s http://[%s]:%%d/%s &",
                            zBrowser, zIpAddr, zInitPage);
            char *zCmd = mprintf(zBrowserCmd/*works-like:"%d"*/,iPort);
            fossil_system(zCmd);
            fossil_free(zCmd);
            fossil_free(zBrowserCmd);
    }else{
      zBrowserCmd = mprintf("%s http://%s:%%d/%s &",
            zBrowserCmd = 0;
                            zBrowser, zIpAddr, zInitPage);
    }
          }
        }
      }
      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( zMaxLatency ){
  if( zTimeout ){
    signal(SIGALRM, sigalrm_handler);
    alarm(atoi(zMaxLatency));
    fossil_set_timeout(atoi(zTimeout));
  }else{
    fossil_set_timeout(FOSSIL_DEFAULT_TIMEOUT);
  }
  g.httpIn = stdin;
  g.httpOut = stdout;

#if !defined(_WIN32)
  signal(SIGSEGV, sigsegv_handler);
  signal(SIGPIPE, sigpipe_handler);
#endif

  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(g.zRepositoryName, noJail);
    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());
  }
#else
  /* Win32 implementation */
  if( isUiCmd ){
#if FOSSIL_ENABLE_SSL
    zBrowser = db_get("web-browser", "start");
    if( zIpAddr==0 ){
  if( g.httpUseSSL && g.httpSSLConn ){
      zBrowserCmd = mprintf("%s http://localhost:%%d/%s &",
                            zBrowser, zInitPage);
    ssl_close_server(g.httpSSLConn);
    }else if( strchr(zIpAddr,':') ){
      zBrowserCmd = mprintf("%s http://[%s]:%%d/%s &",
    g.httpSSLConn = 0;
                            zBrowser, zIpAddr, zInitPage);
    }else{
      zBrowserCmd = mprintf("%s http://%s:%%d/%s &",
                            zBrowser, zIpAddr, zInitPage);
    }
  }
  }
#endif /* FOSSIL_ENABLE_SSL */

  if( g.repositoryOpen ) flags |= HTTP_SERVER_HAD_REPOSITORY;
  if( g.localOpen ) flags |= HTTP_SERVER_HAD_CHECKOUT;
  db_close(1);
#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);
  }
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
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







-
+



+








-
+







**     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.Setup && !g.perm.Admin ){
  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<=7; i++){
  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");
2889
2890
2891
2892
2893
2894
2895






2896
2897
2898

2899
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628

3629
3630







+
+
+
+
+
+


-
+

    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_footer();
  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
1
2

3
4
5
6
7
8
9
10
11
12
13

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87


-
+










-
+





+



+












+




+







+









+

+





+




+






+







#
##############################################################################
# WARNING: DO NOT EDIT, AUTOMATICALLY GENERATED FILE (SEE "src/makemake.tcl")
# 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) -I. -I$(SRCDIR) -I$(OBJDIR) $(TCCFLAGS)
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 \
92
93
94
95
96
97
98

99
100

101
102
103
104
105
106
107
108

109
110
111
112
113
114
115
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128







+


+








+







  $(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 \
124
125
126
127
128
129
130

131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155

156


157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178




179
180
181
182
183
184
185
186
187
188
189
190
191




192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211





212



213
214




















215

216
217
218
219
220
221
222
223





















224
225
226
227
228
229

230
231
232

233
234
235
236
237
238
239
240
241
242
243
244

245
246
247
248

249
250
251
252
253
254
255

256
257
258
259
260
261
262
263
264

265

266
267
268
269
270

271
272
273
274

275
276
277
278
279
280

281
282
283
284
285
286
287
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158

159
160
161
162

163
164
165
166
167
168

169
170
171
172
173
174
175
176
177
178
179
180
181
182
183









184
185
186
187
188
189
190
191

192
193
194
195




196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211




212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252

253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350







+














-




-





+
-
+
+













-
-
-
-
-
-
-
-
-
+
+
+
+




-




-
-
-
-
+
+
+
+












-
-
-
-




+
+
+
+
+

+
+
+


+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+




-



+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+






+



+












+




+







+









+

+





+




+






+







  $(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)/webmail.c \
  $(SRCDIR)/wiki.c \
  $(SRCDIR)/wikiformat.c \
  $(SRCDIR)/winfile.c \
  $(SRCDIR)/winhttp.c \
  $(SRCDIR)/wysiwyg.c \
  $(SRCDIR)/xfer.c \
  $(SRCDIR)/xfersetup.c \
  $(SRCDIR)/zip.c

EXTRA_FILES = \
  $(SRCDIR)/../extsrc/pikchr-worker.js \
  $(SRCDIR)/../skins/aht/details.txt \
  $(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/blitz_no_logo/css.txt \
  $(SRCDIR)/../skins/blitz_no_logo/details.txt \
  $(SRCDIR)/../skins/blitz_no_logo/footer.txt \
  $(SRCDIR)/../skins/blitz_no_logo/header.txt \
  $(SRCDIR)/../skins/blitz_no_logo/ticket.txt \
  $(SRCDIR)/../skins/bootstrap/css.txt \
  $(SRCDIR)/../skins/bootstrap/details.txt \
  $(SRCDIR)/../skins/bootstrap/footer.txt \
  $(SRCDIR)/../skins/bootstrap/header.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/default/js.txt \
  $(SRCDIR)/../skins/eagle/css.txt \
  $(SRCDIR)/../skins/eagle/details.txt \
  $(SRCDIR)/../skins/eagle/footer.txt \
  $(SRCDIR)/../skins/eagle/header.txt \
  $(SRCDIR)/../skins/enhanced1/css.txt \
  $(SRCDIR)/../skins/enhanced1/details.txt \
  $(SRCDIR)/../skins/enhanced1/footer.txt \
  $(SRCDIR)/../skins/enhanced1/header.txt \
  $(SRCDIR)/../skins/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/rounded1/css.txt \
  $(SRCDIR)/../skins/rounded1/details.txt \
  $(SRCDIR)/../skins/rounded1/footer.txt \
  $(SRCDIR)/../skins/rounded1/header.txt \
  $(SRCDIR)/../skins/xekri/css.txt \
  $(SRCDIR)/../skins/xekri/details.txt \
  $(SRCDIR)/../skins/xekri/footer.txt \
  $(SRCDIR)/../skins/xekri/header.txt \
  $(SRCDIR)/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)/sbsdiff.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 \
302
303
304
305
306
307
308

309
310

311
312
313
314
315
316
317
318

319
320
321
322
323
324
325
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391







+


+








+







  $(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 \
334
335
336
337
338
339
340

341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366

367
368
369

370
371
372
373
374
375
376
377
378
379
380
381

382
383
384
385

386
387
388
389
390
391
392

393
394
395
396
397
398
399
400
401

402

403
404
405
406
407

408
409
410
411

412
413
414
415
416
417

418
419
420
421
422
423
424
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421

422
423
424
425

426
427
428
429
430
431
432
433
434
435
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







+














-




-






+



+












+




+







+









+

+





+




+






+







  $(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)/webmail_.c \
  $(OBJDIR)/wiki_.c \
  $(OBJDIR)/wikiformat_.c \
  $(OBJDIR)/winfile_.c \
  $(OBJDIR)/winhttp_.c \
  $(OBJDIR)/wysiwyg_.c \
  $(OBJDIR)/xfer_.c \
  $(OBJDIR)/xfersetup_.c \
  $(OBJDIR)/zip_.c

OBJ = \
 $(OBJDIR)/add.o \
 $(OBJDIR)/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 \
439
440
441
442
443
444
445

446
447

448
449
450
451
452
453
454
455

456
457
458
459
460
461
462
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







+


+








+







 $(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 \
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
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







+














-




-



-
-
-
-
-


-
+









-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
-
-
-
+
+

















-
-
+
+
+
+

-
-
+
+



+






-




-





-



-
-
+
+
+



+






-




-





-



-

+
+







-
-
+
+
-
-

-
-
+
+


+
+
+

-
+
+

-
+
-
-
-
-
-
-











-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+







-
+
-

+







-
+

-
+

















-
+

+



+












+




+







+









+

+





+




+






+







 $(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)/webmail.o \
 $(OBJDIR)/wiki.o \
 $(OBJDIR)/wikiformat.o \
 $(OBJDIR)/winfile.o \
 $(OBJDIR)/winhttp.o \
 $(OBJDIR)/wysiwyg.o \
 $(OBJDIR)/xfer.o \
 $(OBJDIR)/xfersetup.o \
 $(OBJDIR)/zip.o

APPNAME = fossil$(E)



all:	$(OBJDIR) $(APPNAME)

install:	$(APPNAME)
install:	all
	mkdir -p $(INSTALLDIR)
	cp $(APPNAME) $(INSTALLDIR)

codecheck:	$(TRANS_SRC) $(OBJDIR)/codecheck1
	$(OBJDIR)/codecheck1 $(TRANS_SRC)

$(OBJDIR):
	-mkdir $(OBJDIR)

$(OBJDIR)/translate:	$(SRCDIR)/translate.c
	$(XBCC) -o $(OBJDIR)/translate $(SRCDIR)/translate.c
$(OBJDIR)/translate:	$(SRCDIR_tools)/translate.c
	$(XBCC) -o $(OBJDIR)/translate $(SRCDIR_tools)/translate.c

$(OBJDIR)/makeheaders:	$(SRCDIR)/makeheaders.c
	$(XBCC) -o $(OBJDIR)/makeheaders $(SRCDIR)/makeheaders.c
$(OBJDIR)/makeheaders:	$(SRCDIR_tools)/makeheaders.c
	$(XBCC) -o $(OBJDIR)/makeheaders $(SRCDIR_tools)/makeheaders.c

$(OBJDIR)/mkindex:	$(SRCDIR)/mkindex.c
	$(XBCC) -o $(OBJDIR)/mkindex $(SRCDIR)/mkindex.c
$(OBJDIR)/mkindex:	$(SRCDIR_tools)/mkindex.c
	$(XBCC) -o $(OBJDIR)/mkindex $(SRCDIR_tools)/mkindex.c

$(OBJDIR)/mkbuiltin:	$(SRCDIR)/mkbuiltin.c
	$(XBCC) -o $(OBJDIR)/mkbuiltin $(SRCDIR)/mkbuiltin.c
$(OBJDIR)/mkbuiltin:	$(SRCDIR_tools)/mkbuiltin.c
	$(XBCC) -o $(OBJDIR)/mkbuiltin $(SRCDIR_tools)/mkbuiltin.c

$(OBJDIR)/mkversion:	$(SRCDIR)/mkversion.c
	$(XBCC) -o $(OBJDIR)/mkversion $(SRCDIR)/mkversion.c
$(OBJDIR)/mkversion:	$(SRCDIR_tools)/mkversion.c
	$(XBCC) -o $(OBJDIR)/mkversion $(SRCDIR_tools)/mkversion.c

$(OBJDIR)/mkcss:	$(SRCDIR)/mkcss.c
	$(XBCC) -o $(OBJDIR)/mkcss $(SRCDIR)/mkcss.c

$(OBJDIR)/codecheck1:	$(SRCDIR)/codecheck1.c
	$(XBCC) -o $(OBJDIR)/codecheck1 $(SRCDIR)/codecheck1.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)/mkversion $(SRCDIR)/../manifest.uuid  $(SRCDIR)/../manifest  $(SRCDIR)/../VERSION >$(OBJDIR)/VERSION.h
$(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)/default_css.h:	$(SRCDIR)/default_css.txt $(OBJDIR)/mkcss
	$(OBJDIR)/mkcss $(SRCDIR)/default_css.txt $(OBJDIR)/default_css.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_GET_TABLE \
                 -DSQLITE_OMIT_PROGRESS_CALLBACK \
                 -DSQLITE_OMIT_SHARED_CACHE \
                 -DSQLITE_OMIT_LOAD_EXTENSION \
                 -DSQLITE_MAX_EXPR_DEPTH=0 \
                 -DSQLITE_USE_ALLOCA \
                 -DSQLITE_ENABLE_LOCKING_STYLE=0 \
                 -DSQLITE_DEFAULT_FILE_FORMAT=4 \
                 -DSQLITE_ENABLE_EXPLAIN_COMMENTS \
                 -DSQLITE_ENABLE_FTS4 \
                 -DSQLITE_ENABLE_DBSTAT_VTAB \
                 -DSQLITE_ENABLE_JSON1 \
                 -DSQLITE_ENABLE_FTS5 \
                 -DSQLITE_ENABLE_STMTVTAB \
                 -DSQLITE_HAVE_ZLIB \
                 -DSQLITE_INTROSPECTION_PRAGMAS \
                 -DSQLITE_ENABLE_DBPAGE_VTAB
                 -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_GET_TABLE \
                -DSQLITE_OMIT_PROGRESS_CALLBACK \
                -DSQLITE_OMIT_SHARED_CACHE \
                -DSQLITE_OMIT_LOAD_EXTENSION \
                -DSQLITE_MAX_EXPR_DEPTH=0 \
                -DSQLITE_USE_ALLOCA \
                -DSQLITE_ENABLE_LOCKING_STYLE=0 \
                -DSQLITE_DEFAULT_FILE_FORMAT=4 \
                -DSQLITE_ENABLE_EXPLAIN_COMMENTS \
                -DSQLITE_ENABLE_FTS4 \
                -DSQLITE_ENABLE_DBSTAT_VTAB \
                -DSQLITE_ENABLE_JSON1 \
                -DSQLITE_ENABLE_FTS5 \
                -DSQLITE_ENABLE_STMTVTAB \
                -DSQLITE_HAVE_ZLIB \
                -DSQLITE_INTROSPECTION_PRAGMAS \
                -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 miniz library.
MINIZ_OPTIONS = -DMINIZ_NO_STDIO \
# Setup the options used to compile the included Pikchr formatter.
PIKCHR_OPTIONS = -DPIKCHR_TOKEN_LIMIT=10000
                -DMINIZ_NO_TIME \
                -DMINIZ_NO_ARCHIVE_APIS

# The USE_SYSTEM_SQLITE variable may be undefined, set to 0, or set
# to 1. If it is set to 1, then there is no need to build or link
# The 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 =
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 FOSSIL_ENABLE_MINIZ variable may be undefined, set to 0, or
# set to 1.  If it is set to 1, the miniz library included in the
# source tree should be used; otherwise, it should not.
MINIZ_OBJ.0 =
MINIZ_OBJ.1 = $(OBJDIR)/miniz.o
MINIZ_OBJ.  = $(MINIZ_OBJ.0)

# 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, ordinary 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 = sqlite3.c
SQLITE3_SRC.1 = sqlite3-see.c
SQLITE3_SRC. = sqlite3.c
SQLITE3_SRC = $(SRCDIR)/$(SQLITE3_SRC.$(USE_SEE))
SQLITE3_SHELL_SRC.0 = shell.c
SQLITE3_SHELL_SRC.1 = shell-see.c
SQLITE3_SHELL_SRC. = shell.c
SQLITE3_SHELL_SRC = $(SRCDIR)/$(SQLITE3_SHELL_SRC.$(USE_SEE))
# 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.$(USE_SYSTEM_SQLITE)) \
 $(SQLITE3_OBJ.$(SQLITE3_ORIGIN)) \
 $(MINIZ_OBJ.$(FOSSIL_ENABLE_MINIZ)) \
 $(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 $(OBJ) $(EXTRAOBJ)
$(APPNAME):	$(OBJDIR)/headers $(OBJDIR)/codecheck1 $(EXTRAOBJ) $(OBJ)
	$(OBJDIR)/codecheck1 $(TRANS_SRC)
	$(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB)
	$(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)/default_css.h $(OBJDIR)/makeheaders $(OBJDIR)/VERSION.h
$(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 \
772
773
774
775
776
777
778

779
780

781
782
783
784
785
786
787
788

789
790
791
792
793
794
795
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







+


+








+







	$(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 \
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
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







+














-




-



+
-
+













+
+
+
+
+
+
+
+







	$(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)/webmail_.c:$(OBJDIR)/webmail.h \
	$(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h \
	$(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h \
	$(OBJDIR)/winfile_.c:$(OBJDIR)/winfile.h \
	$(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h \
	$(OBJDIR)/wysiwyg_.c:$(OBJDIR)/wysiwyg.h \
	$(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h \
	$(OBJDIR)/xfersetup_.c:$(OBJDIR)/xfersetup.h \
	$(OBJDIR)/zip_.c:$(OBJDIR)/zip.h \
	$(SRCDIR_extsrc)/pikchr.c:$(OBJDIR)/pikchr.h \
	$(SRCDIR)/sqlite3.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

865
866
867
868
869
870
871








872
873
874
875
876
877
878
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974







+
+
+
+
+
+
+
+







$(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

961
962
963
964
965
966
967








968
969
970
971
972
973
974
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078







+
+
+
+
+
+
+
+







$(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

993
994
995
996
997
998
999








1000
1001
1002
1003
1004
1005
1006
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118







+
+
+
+
+
+
+
+







$(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

1049
1050
1051
1052
1053
1054
1055








1056
1057
1058
1059
1060
1061
1062
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182







+
+
+
+
+
+
+
+







$(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

1121
1122
1123
1124
1125
1126
1127








1128
1129
1130
1131
1132
1133
1134
1135








1136
1137
1138
1139
1140
1141
1142
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







+
+
+
+
+
+
+
+








+
+
+
+
+
+
+
+







$(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

1169
1170
1171
1172
1173
1174
1175








1176
1177
1178
1179
1180
1181
1182
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326







+
+
+
+
+
+
+
+







$(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

1201
1202
1203
1204
1205
1206
1207








1208
1209
1210
1211
1212
1213
1214
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366







+
+
+
+
+
+
+
+







$(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

1249
1250
1251
1252
1253
1254
1255








1256
1257
1258
1259
1260
1261
1262
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422







+
+
+
+
+
+
+
+







$(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

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
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







+
+
+
+
+
+
+
+
















+
+
+
+
+
+
+
+







$(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

1553
1554
1555
1556
1557
1558
1559








1560
1561
1562
1563
1564
1565
1566
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750







+
+
+
+
+
+
+
+







$(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

1701
1702
1703
1704
1705
1706
1707
1708

1709
1710
1711
1712
1713
1714
1715
1885
1886
1887
1888
1889
1890
1891

1892
1893
1894
1895
1896
1897
1898
1899







-
+







	$(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 $(OBJDIR)/default_css.h $(SRCDIR)/config.h
$(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 >$@

1729
1730
1731
1732
1733
1734
1735








1736
1737
1738
1739
1740
1741
1742
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934







+
+
+
+
+
+
+
+







$(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

1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
2034
2035
2036
2037
2038
2039
2040








2041
2042
2043
2044
2045
2046
2047







-
-
-
-
-
-
-
-







	$(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)/webmail_.c:	$(SRCDIR)/webmail.c $(OBJDIR)/translate
	$(OBJDIR)/translate $(SRCDIR)/webmail.c >$@

$(OBJDIR)/webmail.o:	$(OBJDIR)/webmail_.c $(OBJDIR)/webmail.h $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/webmail.o -c $(OBJDIR)/webmail_.c

$(OBJDIR)/webmail.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
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
2066
2067
2068
2069
2070
2071
2072








2073
2074
2075
2076
2077
2078
2079







-
-
-
-
-
-
-
-







	$(OBJDIR)/translate $(SRCDIR)/winhttp.c >$@

$(OBJDIR)/winhttp.o:	$(OBJDIR)/winhttp_.c $(OBJDIR)/winhttp.h $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/winhttp.o -c $(OBJDIR)/winhttp_.c

$(OBJDIR)/winhttp.h:	$(OBJDIR)/headers

$(OBJDIR)/wysiwyg_.c:	$(SRCDIR)/wysiwyg.c $(OBJDIR)/translate
	$(OBJDIR)/translate $(SRCDIR)/wysiwyg.c >$@

$(OBJDIR)/wysiwyg.o:	$(OBJDIR)/wysiwyg_.c $(OBJDIR)/wysiwyg.h $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/wysiwyg.o -c $(OBJDIR)/wysiwyg_.c

$(OBJDIR)/wysiwyg.h:	$(OBJDIR)/headers

$(OBJDIR)/xfer_.c:	$(SRCDIR)/xfer.c $(OBJDIR)/translate
	$(OBJDIR)/translate $(SRCDIR)/xfer.c >$@

$(OBJDIR)/xfer.o:	$(OBJDIR)/xfer_.c $(OBJDIR)/xfer.h $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/xfer.o -c $(OBJDIR)/xfer_.c

$(OBJDIR)/xfer.h:	$(OBJDIR)/headers
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
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







-
+


-
+


-
-
+
+











-
-
+
+

-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







+

	$(OBJDIR)/translate $(SRCDIR)/zip.c >$@

$(OBJDIR)/zip.o:	$(OBJDIR)/zip_.c $(OBJDIR)/zip.h $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/zip.o -c $(OBJDIR)/zip_.c

$(OBJDIR)/zip.h:	$(OBJDIR)/headers

$(OBJDIR)/sqlite3.o:	$(SQLITE3_SRC)
$(SQLITE3_OBJ):	$(SQLITE3_SRC)
	$(XTCC) $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SEE_FLAGS) \
		-c $(SQLITE3_SRC) -o $@
$(OBJDIR)/shell.o:	$(SQLITE3_SHELL_SRC) $(SRCDIR)/sqlite3.h
$(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)/linenoise.c $(SRCDIR)/linenoise.h
	$(XTCC) -c $(SRCDIR)/linenoise.c -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)/miniz.o:	$(SRCDIR)/miniz.c
	$(XTCC) $(MINIZ_OPTIONS) -c $(SRCDIR)/miniz.c -o $@
$(OBJDIR)/pikchr.o:	$(SRCDIR_extsrc)/pikchr.c
	$(XTCC) $(PIKCHR_OPTIONS) -c $(SRCDIR_extsrc)/pikchr.c -o $@

$(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c
	$(XTCC) -c $(SRCDIR)/cson_amalgamation.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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
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































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
/*
** 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.
** appropriate header files.
*/
#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 null 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];
  sprintf(pDecl->zName,"%.*s",nName,zName);
  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];
  sprintf(pId->zName,"%.*s",nId,zId);
  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;
          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);
  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;
  }

  /*
  ** 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;
}


/*
** 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;
  }
  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 ){
      sprintf(pIf->zCondition,"%s(%.*s)",zPrefix,nText,zText);
    }else{
      sprintf(pIf->zCondition,"%.*s",nText,zText);
    }
  }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 );
    sprintf(pDecl->zDecl,"%.*s\n",pToken->nText,pToken->zText);
    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];
      sprintf(pInclude->zFile,"%.*s",nArg,zArg);
      sprintf(pInclude->zLabel,"%.*s:%s",nArg,zArg,zIf);
      pInclude->zIf = &pInclude->zLabel[nArg+1];
      SafeFree(zIf);
    }else{
      pInclude = SafeMalloc( sizeof(Include) + nArg + 1 );
      pInclude->zFile = (char*)&pInclude[1];
      sprintf(pInclude->zFile,"%.*s",nArg,zArg);
      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( 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);
    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);
    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 );
        }
      }
    }
  }
  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

Deleted src/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 &#8220;<code>.h</code>&#8221;
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> &#8220;<code>extern</code>&#8221; 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 &#8220;<code>.h</code>&#8221; suffix and definitions are
placed in &#8220;<code>.c</code>&#8221; files.
The .c files contain &#8220;<code>#include</code>&#8221; 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>&lt;stdio.h&gt</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 &#8220;exportable&#8221;.
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
&#8220;<code>alpha.c</code>&#8221;,
&#8220;<code>beta.c</code>&#8221; and
&#8220;<code>gamma.c</code>&#8221;
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 &#8220;<code>-f</code>&#8221; 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 &#8220;<code>mkhdr.dat</code>&#8221;
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 &#8220;<code>-local</code>&#8221; option causes makeheaders to
generate of prototypes for &#8220;<code>static</code>&#8221; functions and
procedures.
Such prototypes are normally omitted.
</p>

<p>
Finally, makeheaders also includes a &#8220;<code>-doc</code>&#8221; 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 preceeds 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 &#8220;<code>--help</code>&#8221; or
&#8220;<code>-?</code>&#8221;)
and it will print a summary of the available options on standard
error.
If you need to process a file whose name begins with
&#8220;<code>-</code>&#8221;,
you can prepend a &#8220;<code>./</code>&#8221; to its name in order to get it
accepted by the command line parser.
Or, you can insert the special option &#8220;<code>--</code>&#8221; on the
command line to cause all subsequent command line arguments to be treated as
filenames even if their names begin with &#8220;<code>-</code>&#8221;.
</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)
&#8220;<code>alpha.c</code>&#8221;
makeheaders will attempt to generate a corresponding header file
named &#8220;<code>alpha.h</code>&#8221;.
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 intented for use with makeheaders is very
simple.
All you have to do is add a single &#8220;<code>#include</code>&#8221; to the
top of the file that sources the header file that makeheaders will generate.
Hence, the beginning of a source file named &#8220;<code>alpha.c</code>&#8221;
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
&#8220;<code>#if</code>&#8221; 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 &#8220;<code>beta.h</code>&#8221;,
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 &#8220;<code>#if</code>&#8221; 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,
&#8220;<code>*.[ch]</code>&#8221;,
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 &#8220;<code>static</code>&#8221; keyword of C appears at the
beginning of the function definition, the prototype is suppressed.
If you use the &#8220;<code>LOCAL</code>&#8221; keyword where you would normally
say &#8220;<code>static</code>&#8221;, 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 &#8220;<code>LOCAL</code>&#8221; 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 &#8220;<code>-local</code>&#8221;
command-line option, then it treats the &#8220;<code>static</code>&#8221;
keyword like &#8220;<code>LOCAL</code>&#8221; 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
&#8220;<code>extern</code>&#8221;
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 &#8220;<code>struct</code>&#8221;, &#8220;<code>union</code>&#8221; or
&#8220;<code>enum</code>&#8221; 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 suppose 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 &#8220;<code>X</code>&#8221; requires a
prior declaration of another structure &#8220;<code>Y</code>&#8221;, 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 for 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 noticable 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
&#8220;<code>#if INTERFACE</code>&#8221; but before the corresponding
&#8220;<code>#endif</code>&#8221; are eligable to be copied into the
automatically generated
.h files of other .c files.
</p>

<p>
If you use the &#8220;<code>#if INTERFACE</code>&#8221; 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
&#8220;<code>#if INTERFACE</code>&#8221; 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
&#8220;<code>#if INTERFACE</code>&#8221; 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 &#8220;<code>export</code>&#8221;.
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 for 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 &#8220;<code>static</code>&#8221; 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: &#8220;<code>EXPORT</code>&#8221;
</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 &#8220;<code>#if EXPORT_INTERFACE</code>&#8221; mechanism can be used in
either .c or .h files.
(The &#8220;<code>#if INTERFACE</code>&#8221; 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 &#8220;<code>private</code>&#8221;.
</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 &#8220;<code>LOCAL_INTERFACE</code>&#8221; block works very much like the
&#8220;<code>INTERFACE</code>&#8221; and
&#8220;<code>EXPORT_INTERFACE</code>&#8221;
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 &#8220;<code>class</code>&#8221;
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 &#8220;<code>alpha.cpp</code>&#8221; will induce makeheaders to
generate a header file named &#8220;<code>alpha.hpp</code>&#8221;.
</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 defintion 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&amp;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&amp;R function.
Therefore you should take care to avoid putting K&amp;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. &#8220;<code>enum {X, Y, Z};</code>&#8221;.
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 &#8220;<code>-doc</code>&#8221; 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 &#8220;<code>-doc</code>&#8221; 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: &#8220;<code>#define</code>&#8221;)
</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 preceeds the declarations.
<li> If the declaration occurred inside a preprocessor conditional
     (&#8220;<code>#if</code>&#8221;) 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://www.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>

Deleted src/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
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





































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
#!/usr/bin/tclsh
#
# Run this Tcl script to generate the various makefiles for a variety
# of platforms.  Files generated include:
#
#     src/main.mk           # makefile for all unix systems
#     win/Makefile.mingw    # makefile for mingw on windows
#     win/Makefile.*        # makefiles for other windows compilers
#
# Run this script while in the "src" subdirectory.  Like this:
#
#      tclsh makemake.tcl
#
# 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.
#
#############################################################################

# 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
  alerts
  allrepo
  attach
  backoffice
  bag
  bisect
  blob
  branch
  browse
  builtin
  bundle
  cache
  capabilities
  captcha
  cgi
  checkin
  checkout
  clearsign
  clone
  comformat
  configure
  content
  cookies
  db
  delta
  deltacmd
  descendants
  diff
  diffcmd
  dispatch
  doc
  encode
  etag
  event
  export
  file
  finfo
  foci
  forum
  fshell
  fusefs
  glob
  graph
  gzip
  hname
  http
  http_socket
  http_transport
  import
  info
  json
  json_artifact
  json_branch
  json_config
  json_diff
  json_dir
  json_finfo
  json_login
  json_query
  json_report
  json_status
  json_tag
  json_timeline
  json_user
  json_wiki
  leaf
  loadctrl
  login
  lookslike
  main
  manifest
  markdown
  markdown_html
  md5
  merge
  merge3
  moderate
  name
  path
  piechart
  pivot
  popen
  pqueue
  printf
  publish
  purge
  rebuild
  regexp
  report
  rss
  schema
  search
  security_audit
  setup
  setupuser
  sha1
  sha1hard
  sha3
  shun
  sitemap
  skins
  smtp
  sqlcmd
  stash
  stat
  statrep
  style
  sync
  tag
  tar
  th_main
  timeline
  tkt
  tktsetup
  undo
  unicode
  unversioned
  update
  url
  user
  utf8
  util
  verify
  vfile
  webmail
  wiki
  wikiformat
  winfile
  winhttp
  wysiwyg
  xfer
  xfersetup
  zip
  http_ssl
}

# Additional resource files that get built into the executable.
#
set extra_files {
  diff.tcl
  markdown.md
  wiki.wiki
  *.js
  ../skins/*/*.txt
}

# Options used to compile the included SQLite library.
#
set SQLITE_OPTIONS {
  -DNDEBUG=1
  -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_GET_TABLE
  -DSQLITE_OMIT_PROGRESS_CALLBACK
  -DSQLITE_OMIT_SHARED_CACHE
  -DSQLITE_OMIT_LOAD_EXTENSION
  -DSQLITE_MAX_EXPR_DEPTH=0
  -DSQLITE_USE_ALLOCA
  -DSQLITE_ENABLE_LOCKING_STYLE=0
  -DSQLITE_DEFAULT_FILE_FORMAT=4
  -DSQLITE_ENABLE_EXPLAIN_COMMENTS
  -DSQLITE_ENABLE_FTS4
  -DSQLITE_ENABLE_DBSTAT_VTAB
  -DSQLITE_ENABLE_JSON1
  -DSQLITE_ENABLE_FTS5
  -DSQLITE_ENABLE_STMTVTAB
  -DSQLITE_HAVE_ZLIB
  -DSQLITE_INTROSPECTION_PRAGMAS
  -DSQLITE_ENABLE_DBPAGE_VTAB
}
#lappend SQLITE_OPTIONS -DSQLITE_ENABLE_FTS3=1
#lappend SQLITE_OPTIONS -DSQLITE_ENABLE_STAT4
#lappend SQLITE_OPTIONS -DSQLITE_WIN32_NO_ANSI
#lappend SQLITE_OPTIONS -DSQLITE_WINNT_MAX_PATH_CHARS=4096

# Options used to compile the included SQLite shell.
#
set SHELL_OPTIONS [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
}]

# miniz (libz drop-in alternative) precompiler flags.
#
set MINIZ_OPTIONS {
  -DMINIZ_NO_STDIO
  -DMINIZ_NO_TIME
  -DMINIZ_NO_ARCHIVE_APIS
}

# Options used to compile the included SQLite shell on Windows.
#
set SHELL_WIN32_OPTIONS $SHELL_OPTIONS
lappend SHELL_WIN32_OPTIONS -Daccess=file_access
lappend SHELL_WIN32_OPTIONS -Dsystem=fossil_system
lappend SHELL_WIN32_OPTIONS -Dgetenv=fossil_getenv
lappend SHELL_WIN32_OPTIONS -Dfopen=fossil_fopen

# 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 {
  foreach x [glob -nocomplain $file] {
    lappend new_extra_files $x
  }
}
set extra_files $new_extra_files

##############################################################################
##############################################################################
##############################################################################
# Start by generating the "main.mk" makefile used for all unix systems.
#
puts "building main.mk"
set output_file [open main.mk w]
fconfigure $output_file -translation binary

writeln {#
##############################################################################
# WARNING: DO NOT EDIT, AUTOMATICALLY GENERATED FILE (SEE "src/makemake.tcl")
##############################################################################
#
# This file is automatically generated.  Instead of editing this
# file, edit "makemake.tcl" then run "tclsh makemake.tcl"
# to regenerate this file.
#
# This file is included by primary Makefile.
#

XBCC = $(BCC) $(BCCFLAGS)
XTCC = $(TCC) -I. -I$(SRCDIR) -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 "\n"
writeln "APPNAME = $name\$(E)"
writeln "\n"

writeln [string map [list \
    <<<SQLITE_OPTIONS>>> [join $SQLITE_OPTIONS " \\\n                 "] \
    <<<SHELL_OPTIONS>>> [join $SHELL_OPTIONS " \\\n                "] \
    <<<MINIZ_OPTIONS>>> [join $MINIZ_OPTIONS " \\\n                "]] {
all:	$(OBJDIR) $(APPNAME)

install:	$(APPNAME)
	mkdir -p $(INSTALLDIR)
	cp $(APPNAME) $(INSTALLDIR)

codecheck:	$(TRANS_SRC) $(OBJDIR)/codecheck1
	$(OBJDIR)/codecheck1 $(TRANS_SRC)

$(OBJDIR):
	-mkdir $(OBJDIR)

$(OBJDIR)/translate:	$(SRCDIR)/translate.c
	$(XBCC) -o $(OBJDIR)/translate $(SRCDIR)/translate.c

$(OBJDIR)/makeheaders:	$(SRCDIR)/makeheaders.c
	$(XBCC) -o $(OBJDIR)/makeheaders $(SRCDIR)/makeheaders.c

$(OBJDIR)/mkindex:	$(SRCDIR)/mkindex.c
	$(XBCC) -o $(OBJDIR)/mkindex $(SRCDIR)/mkindex.c

$(OBJDIR)/mkbuiltin:	$(SRCDIR)/mkbuiltin.c
	$(XBCC) -o $(OBJDIR)/mkbuiltin $(SRCDIR)/mkbuiltin.c

$(OBJDIR)/mkversion:	$(SRCDIR)/mkversion.c
	$(XBCC) -o $(OBJDIR)/mkversion $(SRCDIR)/mkversion.c

$(OBJDIR)/mkcss:	$(SRCDIR)/mkcss.c
	$(XBCC) -o $(OBJDIR)/mkcss $(SRCDIR)/mkcss.c

$(OBJDIR)/codecheck1:	$(SRCDIR)/codecheck1.c
	$(XBCC) -o $(OBJDIR)/codecheck1 $(SRCDIR)/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)/mkversion $(SRCDIR)/../manifest.uuid \
		$(SRCDIR)/../manifest \
		$(SRCDIR)/../VERSION >$(OBJDIR)/VERSION.h

$(OBJDIR)/default_css.h:	$(SRCDIR)/default_css.txt $(OBJDIR)/mkcss
	$(OBJDIR)/mkcss $(SRCDIR)/default_css.txt $(OBJDIR)/default_css.h

# Setup the options used to compile the included SQLite library.
SQLITE_OPTIONS = <<<SQLITE_OPTIONS>>>

# Setup the options used to compile the included SQLite shell.
SHELL_OPTIONS = <<<SHELL_OPTIONS>>>

# Setup the options used to compile the included miniz library.
MINIZ_OPTIONS = <<<MINIZ_OPTIONS>>>

# The USE_SYSTEM_SQLITE variable may be undefined, set to 0, or set
# to 1. If it is set to 1, then there is no need to build or link
# the sqlite3.o object. Instead, the system SQLite will be linked
# using -lsqlite3.
SQLITE3_OBJ.0 = $(OBJDIR)/sqlite3.o
SQLITE3_OBJ.1 =
SQLITE3_OBJ.  = $(SQLITE3_OBJ.0)

# The FOSSIL_ENABLE_MINIZ variable may be undefined, set to 0, or
# set to 1.  If it is set to 1, the miniz library included in the
# source tree should be used; otherwise, it should not.
MINIZ_OBJ.0 =
MINIZ_OBJ.1 = $(OBJDIR)/miniz.o
MINIZ_OBJ.  = $(MINIZ_OBJ.0)

# 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, ordinary 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 = sqlite3.c
SQLITE3_SRC.1 = sqlite3-see.c
SQLITE3_SRC. = sqlite3.c
SQLITE3_SRC = $(SRCDIR)/$(SQLITE3_SRC.$(USE_SEE))
SQLITE3_SHELL_SRC.0 = shell.c
SQLITE3_SHELL_SRC.1 = shell-see.c
SQLITE3_SHELL_SRC. = shell.c
SQLITE3_SHELL_SRC = $(SRCDIR)/$(SQLITE3_SHELL_SRC.$(USE_SEE))
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.$(USE_SYSTEM_SQLITE)) <<<NEXT_LINE>>>
 $(MINIZ_OBJ.$(FOSSIL_ENABLE_MINIZ)) <<<NEXT_LINE>>>
 $(LINENOISE_OBJ.$(USE_LINENOISE)) <<<NEXT_LINE>>>
 $(OBJDIR)/shell.o <<<NEXT_LINE>>>
 $(OBJDIR)/th.o <<<NEXT_LINE>>>
 $(OBJDIR)/th_lang.o <<<NEXT_LINE>>>
 $(OBJDIR)/th_tcl.o <<<NEXT_LINE>>>
 $(OBJDIR)/cson_amalgamation.o
}]

writeln {
$(APPNAME):	$(OBJDIR)/headers $(OBJDIR)/codecheck1 $(OBJ) $(EXTRAOBJ)
	$(OBJDIR)/codecheck1 $(TRANS_SRC)
	$(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB)

# This rule prevents make from using its default rules to try build
# an executable named "manifest" out of the file named "manifest.c"
#
$(SRCDIR)/../manifest:
	# noop

clean:
	-rm -rf $(OBJDIR)/* $(APPNAME)

}

set mhargs {}
foreach s [lsort $src] {
  append mhargs "\$(OBJDIR)/${s}_.c:\$(OBJDIR)/$s.h <<<NEXT_LINE>>>"
  set extra_h($s) { }
}
append mhargs "\$(SRCDIR)/sqlite3.h <<<NEXT_LINE>>>"
append mhargs "\$(SRCDIR)/th.h <<<NEXT_LINE>>>"
#append mhargs "\$(SRCDIR)/cson_amalgamation.h <<<NEXT_LINE>>>"
append mhargs "\$(OBJDIR)/VERSION.h "
set mhargs [string map [list <<<NEXT_LINE>>> \\\n\t] $mhargs]
writeln "\$(OBJDIR)/page_index.h: \$(TRANS_SRC) \$(OBJDIR)/mkindex"
writeln "\t\$(OBJDIR)/mkindex \$(TRANS_SRC) >\$@\n"

writeln "\$(OBJDIR)/builtin_data.h: \$(OBJDIR)/mkbuiltin \$(EXTRA_FILES)"
writeln "\t\$(OBJDIR)/mkbuiltin --prefix \$(SRCDIR)/ \$(EXTRA_FILES) >\$@\n"

writeln "\$(OBJDIR)/headers:\t\$(OBJDIR)/page_index.h \$(OBJDIR)/builtin_data.h \$(OBJDIR)/default_css.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 "
set extra_h(style) " \$(OBJDIR)/default_css.h "

foreach s [lsort $src] {
  writeln "\$(OBJDIR)/${s}_.c:\t\$(SRCDIR)/$s.c \$(OBJDIR)/translate"
  writeln "\t\$(OBJDIR)/translate \$(SRCDIR)/$s.c >\$@\n"
  writeln "\$(OBJDIR)/$s.o:\t\$(OBJDIR)/${s}_.c \$(OBJDIR)/$s.h$extra_h($s)\$(SRCDIR)/config.h"
  writeln "\t\$(XTCC) -o \$(OBJDIR)/$s.o -c \$(OBJDIR)/${s}_.c\n"
  writeln "\$(OBJDIR)/$s.h:\t\$(OBJDIR)/headers\n"
}

writeln "\$(OBJDIR)/sqlite3.o:\t\$(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)/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)/linenoise.c \$(SRCDIR)/linenoise.h"
writeln "\t\$(XTCC) -c \$(SRCDIR)/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 {
$(OBJDIR)/miniz.o:	$(SRCDIR)/miniz.c
	$(XTCC) $(MINIZ_OPTIONS) -c $(SRCDIR)/miniz.c -o $@

$(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c
	$(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $@

#
# The list of all the targets that do not correspond to real files. This stops
# 'make' from getting confused when someone makes an error in a rule.
#

.PHONY: all install test clean
}

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 "src/makemake.tcl")
##############################################################################
#
# This file is automatically generated.  Instead of editing this
# file, edit "makemake.tcl" then run "tclsh makemake.tcl"
# to regenerate this file.
#
# This 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

#### 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 (http://www.json.org) support using "cson"
#
# FOSSIL_ENABLE_JSON = 1

#### Enable HTTPS support via OpenSSL (links to libssl and libcrypto)
#
# FOSSIL_ENABLE_SSL = 1

#### Automatically build OpenSSL when building Fossil (causes rebuild
#    issues when building incrementally).
#
# FOSSIL_BUILD_SSL = 1

#### Enable relative paths in external diff/gdiff
#
# FOSSIL_ENABLE_EXEC_REL_PATHS = 1

#### Enable legacy treatment of mv/rm (skip checkout files)
#
FOSSIL_ENABLE_LEGACY_MV_RM = 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 miniz compression library
#
# FOSSIL_ENABLE_MINIZ = 1

#### Use the Tcl source directory instead of the install directory?
#    This is useful when Tcl has been compiled statically with MinGW.
#
FOSSIL_TCL_SOURCE = 1

#### Check if the workaround for the MinGW command line handling needs to
#    be enabled by default.  This check may be somewhat fragile due to the
#    use of "findstring".
#
ifndef MINGW_IS_32BIT_ONLY
ifeq (,$(findstring w64-mingw32,$(PREFIX)))
MINGW_IS_32BIT_ONLY = 1
endif
endif

#### The directories where the zlib include and library files are located.
#
ZINCDIR = $(SRCDIR)/../compat/zlib
ZLIBDIR = $(SRCDIR)/../compat/zlib

#### Make an attempt to detect if Fossil is being built for the x64 processor
#    architecture.  This check may be somewhat fragile due to "findstring".
#
ifndef X64
ifneq (,$(findstring x86_64-w64-mingw32,$(PREFIX)))
X64 = 1
endif
endif

#### Determine if the optimized assembly routines provided with zlib should be
#    used, taking into account whether zlib is actually enabled and the target
#    processor architecture.
#
ifndef X64
SSLCONFIG = mingw
ifndef FOSSIL_ENABLE_MINIZ
ZLIBCONFIG = LOC="-DASMV -DASMINF" OBJA="inffas86.o match.o"
ZLIBTARGETS = $(ZLIBDIR)/inffas86.o $(ZLIBDIR)/match.o
else
ZLIBCONFIG =
ZLIBTARGETS =
endif
else
SSLCONFIG = mingw64
ZLIBCONFIG =
ZLIBTARGETS =
endif

#### Disable creation of the OpenSSL shared libraries.  Also, disable support
#    for both SSLv2 and SSLv3 (i.e. thereby forcing the use of TLS).
#
SSLCONFIG += no-ssl2 no-ssl3 no-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).
#
ifndef FOSSIL_ENABLE_MINIZ
SSLCONFIG +=  --with-zlib-lib=$(PWD)/$(ZLIBDIR) --with-zlib-include=$(PWD)/$(ZLIBDIR) zlib
endif

#### The directories where the OpenSSL include and library files are located.
#    The recommended usage here is to use the Sysinternals junction tool
#    to create a hard link between an "openssl-1.x" sub-directory of the
#    Fossil source code directory and the target OpenSSL source directory.
#
OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.0.2p
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

#### Add the necessary command line options to build with debugging
#    symbols, if enabled.
#
ifdef FOSSIL_ENABLE_SYMBOLS
TCC += -g
else
TCC += -Os
endif

#### When not using the miniz compression library, zlib is required.
#
ifndef FOSSIL_ENABLE_MINIZ
TCC += -L$(ZLIBDIR) -I$(ZINCDIR)
endif

#### Compile resources for use in building executables that will run
#    on the target platform.
#
RCC = $(PREFIX)windres -I$(SRCDIR)

ifndef FOSSIL_ENABLE_MINIZ
RCC += -I$(ZINCDIR)
endif

# With HTTPS support
ifdef FOSSIL_ENABLE_SSL
TCC += -L$(OPENSSLLIBDIR) -I$(OPENSSLINCDIR)
RCC += -I$(OPENSSLINCDIR)
endif

# With Tcl support
ifdef FOSSIL_ENABLE_TCL
ifdef FOSSIL_TCL_SOURCE
TCC += -L$(TCLSRCDIR)/win -I$(TCLSRCDIR)/generic -I$(TCLSRCDIR)/win
RCC += -I$(TCLSRCDIR)/generic -I$(TCLSRCDIR)/win
else
TCC += -L$(TCLLIBDIR) -I$(TCLINCDIR)
RCC += -I$(TCLINCDIR)
endif
endif

# With miniz (i.e. instead of zlib)
ifdef FOSSIL_ENABLE_MINIZ
TCC += -DFOSSIL_ENABLE_MINIZ=1
RCC += -DFOSSIL_ENABLE_MINIZ=1
endif

# With MinGW command line handling workaround
ifdef MINGW_IS_32BIT_ONLY
TCC += -DBROKEN_MINGW_CMDLINE=1
RCC += -DBROKEN_MINGW_CMDLINE=1
endif

# With HTTPS support
ifdef FOSSIL_ENABLE_SSL
TCC += -DFOSSIL_ENABLE_SSL=1
RCC += -DFOSSIL_ENABLE_SSL=1
endif

# With 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 legacy treatment of mv/rm
ifdef FOSSIL_ENABLE_LEGACY_MV_RM
TCC += -DFOSSIL_ENABLE_LEGACY_MV_RM=1
RCC += -DFOSSIL_ENABLE_LEGACY_MV_RM=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

#### When not using the miniz compression library, zlib is required.
#
ifndef FOSSIL_ENABLE_MINIZ
LIB += -lz
endif

#### These libraries MUST appear in the same order as they do for Tcl
#    or linking with it will not work (exact reason unknown).
#
ifdef FOSSIL_ENABLE_TCL
ifdef FOSSIL_ENABLE_TCL_STUBS
LIB += -lkernel32 -lws2_32
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)
MKCSS       = $(subst /,\,$(OBJDIR)/mkcss.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
MKCSS       = $(OBJDIR)/mkcss.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 $(OBJDIR)/default_css.h
ifdef USE_WINDOWS
	$(CAT) $(subst /,\,$(SRCDIR)\miniz.c) | $(GREP) "define MZ_VERSION" > $(subst /,\,$(OBJDIR)\minizver.h)
	$(CP) $(subst /,\,$(SRCDIR)\..\win\fossil.rc) $(subst /,\,$(OBJDIR))
	$(CP) $(subst /,\,$(SRCDIR)\..\win\fossil.ico) $(subst /,\,$(OBJDIR))
	$(CP) $(subst /,\,$(SRCDIR)\..\win\fossil.exe.manifest) $(subst /,\,$(OBJDIR))
else
	$(CAT) $(SRCDIR)/miniz.c | $(GREP) "define MZ_VERSION" > $(OBJDIR)/minizver.h
	$(CP) $(SRCDIR)/../win/fossil.rc $(OBJDIR)
	$(CP) $(SRCDIR)/../win/fossil.ico $(OBJDIR)
	$(CP) $(SRCDIR)/../win/fossil.exe.manifest $(OBJDIR)
endif
	$(RCC) $(OBJDIR)/fossil.rc -o $(OBJDIR)/fossil.o

install:	$(OBJDIR) $(APPNAME)
ifdef USE_WINDOWS
	$(MKDIR) $(subst /,\,$(INSTALLDIR))
	$(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)/translate.c
	$(XBCC) -o $@ $(SRCDIR)/translate.c

$(MAKEHEADERS):	$(SRCDIR)/makeheaders.c
	$(XBCC) -o $@ $(SRCDIR)/makeheaders.c

$(MKINDEX):	$(SRCDIR)/mkindex.c
	$(XBCC) -o $@ $(SRCDIR)/mkindex.c

$(MKBUILTIN):	$(SRCDIR)/mkbuiltin.c
	$(XBCC) -o $@ $(SRCDIR)/mkbuiltin.c

$(MKVERSION): $(SRCDIR)/mkversion.c
	$(XBCC) -o $@ $(SRCDIR)/mkversion.c

$(MKCSS): $(SRCDIR)/mkcss.c
	$(XBCC) -o $@ $(SRCDIR)/mkcss.c

$(CODECHECK1):	$(SRCDIR)/codecheck1.c
	$(XBCC) -o $@ $(SRCDIR)/codecheck1.c

# WARNING. DANGER. Running the test suite modifies the repository the
# build is done from, i.e. the checkout belongs to. Do not sync/push
# the repository after running the tests.
test:	$(OBJDIR) $(APPNAME)
	$(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME)

$(OBJDIR)/VERSION.h:	$(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(MKVERSION)
	$(MKVERSION) $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION >$@

$(OBJDIR)/default_css.h:	$(SRCDIR)/default_css.txt $(MKCSS)
	$(MKCSS) $(SRCDIR)/default_css.txt $@

# The USE_SYSTEM_SQLITE variable may be undefined, set to 0, or set
# to 1. If it is set to 1, then there is no need to build or link
# the sqlite3.o object. Instead, the system SQLite will be linked
# using -lsqlite3.
SQLITE3_OBJ.0 = $(OBJDIR)/sqlite3.o
SQLITE3_OBJ.1 =
SQLITE3_OBJ.  = $(SQLITE3_OBJ.0)

# The FOSSIL_ENABLE_MINIZ variable may be undefined, set to 0, or
# set to 1.  If it is set to 1, the miniz library included in the
# source tree should be used; otherwise, it should not.
MINIZ_OBJ.0 =
MINIZ_OBJ.1 = $(OBJDIR)/miniz.o
MINIZ_OBJ.  = $(MINIZ_OBJ.0)

# The USE_SEE variable may be undefined, 0 or 1.  If undefined or
# 0, ordinary 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 = sqlite3.c
SQLITE3_SRC.1 = sqlite3-see.c
SQLITE3_SRC. = sqlite3.c
SQLITE3_SRC = $(SRCDIR)/$(SQLITE3_SRC.$(USE_SEE))
SQLITE3_SHELL_SRC.0 = shell.c
SQLITE3_SHELL_SRC.1 = shell-see.c
SQLITE3_SHELL_SRC. = shell.c
SQLITE3_SHELL_SRC = $(SRCDIR)/$(SQLITE3_SHELL_SRC.$(USE_SEE))
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.$(USE_SYSTEM_SQLITE)) <<<NEXT_LINE>>>
 $(MINIZ_OBJ.$(FOSSIL_ENABLE_MINIZ)) <<<NEXT_LINE>>>
 $(OBJDIR)/shell.o <<<NEXT_LINE>>>
 $(OBJDIR)/th.o <<<NEXT_LINE>>>
 $(OBJDIR)/th_lang.o <<<NEXT_LINE>>>
 $(OBJDIR)/th_tcl.o <<<NEXT_LINE>>>
 $(OBJDIR)/cson_amalgamation.o
}]

writeln {
$(ZLIBDIR)/inffas86.o:
	$(TCC) -c -o $@ -DASMINF -I$(ZLIBDIR) -O3 $(ZLIBDIR)/contrib/inflate86/inffas86.c

$(ZLIBDIR)/match.o:
	$(TCC) -c -o $@ -DASMV $(ZLIBDIR)/contrib/asm686/match.S

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

ifdef FOSSIL_ENABLE_MINIZ
BLDTARGETS =
else
BLDTARGETS = zlib
endif

openssl:	$(BLDTARGETS)
	cd $(OPENSSLLIBDIR);./Configure --cross-compile-prefix=$(PREFIX) $(SSLCONFIG)
	$(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) $(OBJ) $(EXTRAOBJ) $(OBJDIR)/fossil.o
	$(CODECHECK1) $(TRANS_SRC)
	$(TCC) -o $@ $(OBJ) $(EXTRAOBJ) $(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 " \\\n\t\t"}
  append mhargs "\$(OBJDIR)/${s}_.c:\$(OBJDIR)/$s.h"
  set extra_h($s) { }
}
append mhargs " \\\n\t\t\$(SRCDIR)/sqlite3.h"
append mhargs " \\\n\t\t\$(SRCDIR)/th.h"
append mhargs " \\\n\t\t\$(OBJDIR)/VERSION.h"
writeln "\$(OBJDIR)/page_index.h: \$(TRANS_SRC) \$(MKINDEX)"
writeln "\t\$(MKINDEX) \$(TRANS_SRC) >\$@\n"

writeln "\$(OBJDIR)/builtin_data.h:\t\$(MKBUILTIN) \$(EXTRA_FILES)"
writeln "\t\$(MKBUILTIN) --prefix \$(SRCDIR)/ \$(EXTRA_FILES) >\$@\n"

writeln "\$(OBJDIR)/headers:\t\$(OBJDIR)/page_index.h \$(OBJDIR)/builtin_data.h \$(OBJDIR)/default_css.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 "
set extra_h(style) " \$(OBJDIR)/default_css.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 MINIZ_WIN32_OPTIONS $MINIZ_OPTIONS

set j " \\\n                 "
writeln "SQLITE_OPTIONS = [join $MINGW_SQLITE_OPTIONS $j]\n"
set j " \\\n                "
writeln "SHELL_OPTIONS = [join $SHELL_WIN32_OPTIONS $j]\n"
set j " \\\n                "
writeln "MINIZ_OPTIONS = [join $MINIZ_WIN32_OPTIONS $j]\n"

writeln "\$(OBJDIR)/sqlite3.o:\t\$(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)/cson_amalgamation.c"
writeln "\t\$(XTCC) -c \$(SRCDIR)/cson_amalgamation.c -o \$@\n"
writeln "\$(OBJDIR)/json.o \$(OBJDIR)/json_artifact.o \$(OBJDIR)/json_branch.o \$(OBJDIR)/json_config.o \$(OBJDIR)/json_diff.o \$(OBJDIR)/json_dir.o \$(OBJDIR)/jsos_finfo.o \$(OBJDIR)/json_login.o \$(OBJDIR)/json_query.o \$(OBJDIR)/json_report.o \$(OBJDIR)/json_status.o \$(OBJDIR)/json_tag.o \$(OBJDIR)/json_timeline.o \$(OBJDIR)/json_user.o \$(OBJDIR)/json_wiki.o : \$(SRCDIR)/json_detail.h\n"

writeln "\$(OBJDIR)/shell.o:\t\$(SQLITE3_SHELL_SRC) \$(SRCDIR)/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)/miniz.o:\t\$(SRCDIR)/miniz.c"
writeln "\t\$(XTCC) \$(MINIZ_OPTIONS) -c \$(SRCDIR)/miniz.c -o \$@\n"

close $output_file
#
# End of the win/Makefile.mingw output
##############################################################################
##############################################################################
##############################################################################
# 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 "src/makemake.tcl")
##############################################################################
#
# This file is automatically generated.  Instead of editing this
# file, edit "makemake.tcl" then run "tclsh makemake.tcl"
# to regenerate this file.
#
B      = ..
SRCDIR = $B\src
OBJDIR = .
O      = .obj
E      = .exe


# Maybe DMDIR, SSL or INCL needs adjustment
DMDIR  = c:\DM
INCL   = -I. -I$(SRCDIR) -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 -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)\translate.c
	$(BCC) -o$@ $**

makeheaders$E: $(SRCDIR)\makeheaders.c
	$(BCC) -o$@ $**

mkindex$E: $(SRCDIR)\mkindex.c
	$(BCC) -o$@ $**

mkbuiltin$E: $(SRCDIR)\mkbuiltin.c
	$(BCC) -o$@ $**

mkversion$E: $(SRCDIR)\mkversion.c
	$(BCC) -o$@ $**

mkcss$E: $(SRCDIR)\mkcss.c
	$(BCC) -o$@ $**

codecheck1$E: $(SRCDIR)\codecheck1.c
	$(BCC) -o$@ $**

$(OBJDIR)\shell$O : $(SRCDIR)\shell.c
	$(TCC) -o$@ -c $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) $**

$(OBJDIR)\sqlite3$O : $(SRCDIR)\sqlite3.c
	$(TCC) -o$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $**

$(OBJDIR)\th$O : $(SRCDIR)\th.c
	$(TCC) -o$@ -c $**

$(OBJDIR)\th_lang$O : $(SRCDIR)\th_lang.c
	$(TCC) -o$@ -c $**

$(OBJDIR)\cson_amalgamation.h : $(SRCDIR)\cson_amalgamation.h
	cp $@ $@

VERSION.h : mkversion$E $B\manifest.uuid $B\manifest $B\VERSION
	+$** > $@

default_css.h : mkcss$E $B\src\default_css.txt
	+$** $B\src\default_css.txt $@

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 mkcss$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 default_css.h VERSION.h\n\t +makeheaders\$E "
foreach s [lsort $src] {
  writeln -nonewline "${s}_.c:$s.h "
}
writeln "\$(SRCDIR)\\sqlite3.h \$(SRCDIR)\\th.h VERSION.h \$(SRCDIR)\\cson_amalgamation.h"
writeln "\t@copy /Y nul: headers"

close $output_file
#
# 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 "src/makemake.tcl")
##############################################################################
#
# This Makefile will only function correctly if used from a sub-directory
# that is a direct child of the top-level directory for this project.
#
!if !exist("..\.fossil-settings")
!error "Please change the current directory to the one containing this file."
!endif

#
# This file is automatically generated.  Instead of editing this
# file, edit "makemake.tcl" then run "tclsh makemake.tcl"
# to regenerate this file.
#
B       = ..
SRCDIR  = $B\src
OBJDIR  = .
OX      = .
O       = .obj
E       = .exe
P       = .pdb

# Perl is only necessary if OpenSSL support is enabled and it must
# be built from source code.  The PERLDIR variable should point to
# the directory containing the main Perl binary (i.e. "perl.exe").
PERLDIR = C:\Perl\bin
PERL    = perl.exe

# Enable debugging symbols?
!ifndef DEBUG
DEBUG = 0
!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 legacy treatment of the mv/rm commands?
!ifndef FOSSIL_ENABLE_LEGACY_MV_RM
FOSSIL_ENABLE_LEGACY_MV_RM = 1
!endif

# Enable use of miniz instead of zlib?
!ifndef FOSSIL_ENABLE_MINIZ
FOSSIL_ENABLE_MINIZ = 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-1.0.2p
SSLINCDIR = $(SSLDIR)\inc32
!if $(FOSSIL_DYNAMIC_BUILD)!=0
SSLLIBDIR = $(SSLDIR)\out32dll
!else
SSLLIBDIR = $(SSLDIR)\out32
!endif
SSLLFLAGS = /nologo /opt:ref /debug
SSLLIB    = ssleay32.lib libeay32.lib user32.lib gdi32.lib crypt32.lib
!if "$(PLATFORM)"=="amd64" || "$(PLATFORM)"=="x64"
!message Using 'x64' platform for OpenSSL...
# BUGBUG (OpenSSL): Using "no-ssl*" here breaks the build.
# SSLCONFIG = VC-WIN64A no-asm no-ssl2 no-ssl3 no-weak-ssl-ciphers
SSLCONFIG = VC-WIN64A no-asm
!if $(FOSSIL_DYNAMIC_BUILD)!=0
SSLCONFIG = $(SSLCONFIG) shared
!else
SSLCONFIG = $(SSLCONFIG) no-shared
!endif
SSLSETUP  = ms\do_win64a.bat
!if $(FOSSIL_DYNAMIC_BUILD)!=0
SSLNMAKE  = ms\ntdll.mak all
!else
SSLNMAKE  = ms\nt.mak all
!endif
# BUGBUG (OpenSSL): Using "OPENSSL_NO_SSL*" here breaks dynamic builds.
!if $(FOSSIL_DYNAMIC_BUILD)==0
SSLCFLAGS = -DOPENSSL_NO_SSL2 -DOPENSSL_NO_SSL3 -DOPENSSL_NO_WEAK_SSL_CIPHERS
!endif
!elseif "$(PLATFORM)"=="ia64"
!message Using 'ia64' platform for OpenSSL...
# BUGBUG (OpenSSL): Using "no-ssl*" here breaks the build.
# SSLCONFIG = VC-WIN64I no-asm no-ssl2 no-ssl3 no-weak-ssl-ciphers
SSLCONFIG = VC-WIN64I no-asm
!if $(FOSSIL_DYNAMIC_BUILD)!=0
SSLCONFIG = $(SSLCONFIG) shared
!else
SSLCONFIG = $(SSLCONFIG) no-shared
!endif
SSLSETUP  = ms\do_win64i.bat
!if $(FOSSIL_DYNAMIC_BUILD)!=0
SSLNMAKE  = ms\ntdll.mak all
!else
SSLNMAKE  = ms\nt.mak all
!endif
# BUGBUG (OpenSSL): Using "OPENSSL_NO_SSL*" here breaks dynamic builds.
!if $(FOSSIL_DYNAMIC_BUILD)==0
SSLCFLAGS = -DOPENSSL_NO_SSL2 -DOPENSSL_NO_SSL3 -DOPENSSL_NO_WEAK_SSL_CIPHERS
!endif
!else
!message Assuming 'x86' platform for OpenSSL...
# BUGBUG (OpenSSL): Using "no-ssl*" here breaks the build.
# SSLCONFIG = VC-WIN32 no-asm no-ssl2 no-ssl3 no-weak-ssl-ciphers
SSLCONFIG = VC-WIN32 no-asm
!if $(FOSSIL_DYNAMIC_BUILD)!=0
SSLCONFIG = $(SSLCONFIG) shared
!else
SSLCONFIG = $(SSLCONFIG) no-shared
!endif
SSLSETUP  = ms\do_ms.bat
!if $(FOSSIL_DYNAMIC_BUILD)!=0
SSLNMAKE  = ms\ntdll.mak all
!else
SSLNMAKE  = ms\nt.mak all
!endif
# BUGBUG (OpenSSL): Using "OPENSSL_NO_SSL*" here breaks dynamic builds.
!if $(FOSSIL_DYNAMIC_BUILD)==0
SSLCFLAGS = -DOPENSSL_NO_SSL2 -DOPENSSL_NO_SSL3 -DOPENSSL_NO_WEAK_SSL_CIPHERS
!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$(SRCDIR) /I$B\win\include

!if $(FOSSIL_ENABLE_MINIZ)==0
INCL      = $(INCL) /I$(ZINCDIR)
!endif

!if $(FOSSIL_ENABLE_SSL)!=0
INCL      = $(INCL) /I$(SSLINCDIR)
!endif

!if $(FOSSIL_ENABLE_TCL)!=0
INCL      = $(INCL) /I$(TCLINCDIR)
!endif

CFLAGS    = /nologo
LDFLAGS   =

!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)
!if "$(PLATFORM)"=="amd64" || "$(PLATFORM)"=="x64"
XPLDFLAGS = $(XPLDFLAGS) /SUBSYSTEM:CONSOLE,5.02
!else
XPLDFLAGS = $(XPLDFLAGS) /SUBSYSTEM:CONSOLE,5.01
!endif
LDFLAGS   = $(LDFLAGS) $(XPLDFLAGS)
!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 $(DEBUG)!=0
CFLAGS    = $(CFLAGS) /Zi $(CRTFLAGS) /Od
LDFLAGS   = $(LDFLAGS) /DEBUG
!else
CFLAGS    = $(CFLAGS) $(CRTFLAGS) /O2
!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

!if $(FOSSIL_ENABLE_MINIZ)==0
LIBS      = $(LIBS) $(ZLIB)
LIBDIR    = $(LIBDIR) /LIBPATH:$(ZLIBDIR)
!endif

!if $(FOSSIL_ENABLE_MINIZ)!=0
TCC       = $(TCC) /DFOSSIL_ENABLE_MINIZ=1
RCC       = $(RCC) /DFOSSIL_ENABLE_MINIZ=1
!endif

!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_LEGACY_MV_RM)!=0
TCC       = $(TCC) /DFOSSIL_ENABLE_LEGACY_MV_RM=1
RCC       = $(RCC) /DFOSSIL_ENABLE_LEGACY_MV_RM=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 $MINIZ_WIN32_OPTIONS { }] {/D} MSC_MINIZ_OPTIONS
set j " \\\n                "
writeln "MINIZ_OPTIONS = [join $MSC_MINIZ_OPTIONS $j]\n"

writeln -nonewline "SRC   = "
set i 0
foreach s [lsort $src] {
  if {$i > 0} {
    writeln " \\"
    writeln -nonewline "        "
  }
  writeln -nonewline "${s}_.c"; incr i
}
writeln "\n"
writeln -nonewline "EXTRA_FILES   = "
set i 0
foreach s [lsort $extra_files] {
  if {$i > 0} {
    writeln " \\"
    writeln -nonewline "        "
  }
  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]
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 "!if \$(FOSSIL_ENABLE_MINIZ)!=0"
writeln -nonewline "        "
writeln "\$(OX)\\miniz\$O \\"; incr i
writeln "!endif"
writeln -nonewline "        \$(OX)\\fossil.res\n\n"
writeln [string map [list <<<NEXT_LINE>>> \\] {
APPNAME    = $(OX)\fossil$(E)
PDBNAME    = $(OX)\fossil$(P)
APPTARGETS =

all: $(OX) $(APPNAME)

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

!if $(FOSSIL_ENABLE_SSL)!=0
openssl:
	@echo Building OpenSSL from "$(SSLDIR)"...
!if "$(PERLDIR)" != ""
	@set PATH=$(PERLDIR);$(PATH)
!endif
	@pushd "$(SSLDIR)" && $(PERL) Configure $(SSLCONFIG) && popd
	@pushd "$(SSLDIR)" && call $(SSLSETUP) && popd
!if $(FOSSIL_ENABLE_WINXP)!=0
	@pushd "$(SSLDIR)" && $(MAKE) /f $(SSLNMAKE) "CC=cl $(SSLCFLAGS) $(XPCFLAGS)" "LFLAGS=$(SSLLFLAGS) $(XPLDFLAGS)" && popd
!else
	@pushd "$(SSLDIR)" && $(MAKE) /f $(SSLNMAKE) "CC=cl $(SSLCFLAGS)" && popd
!endif
!endif

!if $(FOSSIL_ENABLE_MINIZ)==0
!if $(FOSSIL_BUILD_ZLIB)!=0
APPTARGETS = $(APPTARGETS) zlib
!endif
!endif

!if $(FOSSIL_ENABLE_SSL)!=0
!if $(FOSSIL_BUILD_SSL)!=0
APPTARGETS = $(APPTARGETS) openssl
!endif
!endif

$(APPNAME) : $(APPTARGETS) translate$E mkindex$E codecheck1$E headers $(OBJ) $(OX)\linkopts
	cd $(OX)
	codecheck1$E $(SRC)
	link $(LDFLAGS) /OUT:$@ $(LIBDIR) Wsetargv.obj fossil.res @linkopts
	if exist $@.manifest <<<NEXT_LINE>>>
		$(MTC) -nologo -manifest $@.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 "!if \$(FOSSIL_ENABLE_MINIZ)!=0"
writeln "\techo \$(OX)\\miniz.obj $redir \$@"
writeln "!endif"
writeln "\techo \$(LIBS) $redir \$@"
writeln {
$(OX):
	@-mkdir $@

translate$E: $(SRCDIR)\translate.c
	$(BCC) $**

makeheaders$E: $(SRCDIR)\makeheaders.c
	$(BCC) $**

mkindex$E: $(SRCDIR)\mkindex.c
	$(BCC) $**

mkbuiltin$E: $(SRCDIR)\mkbuiltin.c
	$(BCC) $**

mkversion$E: $(SRCDIR)\mkversion.c
	$(BCC) $**

mkcss$E: $(SRCDIR)\mkcss.c
	$(BCC) $**

codecheck1$E: $(SRCDIR)\codecheck1.c
	$(BCC) $**

!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)\sqlite3-see.c
!else
SEE_FLAGS =
SQLITE3_SHELL_SRC = $(SRCDIR)\shell.c
SQLITE3_SRC = $(SRCDIR)\sqlite3.c
!endif

$(OX)\shell$O : $(SQLITE3_SHELL_SRC) $B\win\Makefile.msc
	$(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) $(SEE_FLAGS) -c $(SQLITE3_SHELL_SRC)

$(OX)\sqlite3$O : $(SQLITE3_SRC) $B\win\Makefile.msc
	$(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SEE_FLAGS) $(SQLITE3_SRC)

$(OX)\th$O : $(SRCDIR)\th.c
	$(TCC) /Fo$@ -c $**

$(OX)\th_lang$O : $(SRCDIR)\th_lang.c
	$(TCC) /Fo$@ -c $**

$(OX)\th_tcl$O : $(SRCDIR)\th_tcl.c
	$(TCC) /Fo$@ -c $**

$(OX)\miniz$O : $(SRCDIR)\miniz.c
	$(TCC) /Fo$@ -c $(MINIZ_OPTIONS) $(SRCDIR)\miniz.c

VERSION.h : mkversion$E $B\manifest.uuid $B\manifest $B\VERSION
	$** > $@

$(OX)\cson_amalgamation$O : $(SRCDIR)\cson_amalgamation.c
	$(TCC) /Fo$@ /c $**

default_css.h: mkcss$E $(SRCDIR)\default_css.txt
	$** $@

page_index.h: mkindex$E $(SRC)
	$** > $@

builtin_data.h:	mkbuiltin$E $(EXTRA_FILES)
	mkbuiltin$E --prefix $(SRCDIR)/ $(EXTRA_FILES) > $@

clean:
	-del $(OX)\*.obj 2>NUL
	-del *.obj 2>NUL
	-del *_.c 2>NUL
	-del *.h 2>NUL
	-del *.ilk 2>NUL
	-del *.map 2>NUL
	-del *.res 2>NUL
	-del headers 2>NUL
	-del linkopts 2>NUL
	-del vc*.pdb 2>NUL

realclean: clean
	-del $(APPNAME) 2>NUL
	-del $(PDBNAME) 2>NUL
	-del translate$E 2>NUL
	-del translate$P 2>NUL
	-del mkindex$E 2>NUL
	-del mkindex$P 2>NUL
	-del makeheaders$E 2>NUL
	-del makeheaders$P 2>NUL
	-del mkversion$E 2>NUL
	-del mkversion$P 2>NUL
	-del mkcss$E 2>NUL
	-del mkcss$P 2>NUL
	-del codecheck1$E 2>NUL
	-del codecheck1$P 2>NUL
	-del mkbuiltin$E 2>NUL
	-del mkbuiltin$P 2>NUL

$(OBJDIR)\json$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_artifact$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_branch$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_config$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_diff$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_dir$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_finfo$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_login$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_query$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_report$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_status$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_tag$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_timeline$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_user$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_wiki$O : $(SRCDIR)\json_detail.h
}
foreach s [lsort $src] {
  writeln "\$(OX)\\$s\$O : ${s}_.c ${s}.h"
  writeln "\t\$(TCC) /Fo\$@ -c ${s}_.c\n"
  writeln "${s}_.c : \$(SRCDIR)\\$s.c"
  writeln "\ttranslate\$E \$** > \$@\n"
}

writeln "fossil.res : \$B\\win\\fossil.rc"
writeln "\t\$(RCC)  /fo \$@ \$**\n"

writeln "headers: makeheaders\$E page_index.h builtin_data.h default_css.h VERSION.h"
writeln -nonewline "\tmakeheaders\$E "
set i 0
foreach s [lsort $src] {
  if {$i > 0} {
    writeln " \\"
    writeln -nonewline "\t\t\t"
  }
  writeln -nonewline "${s}_.c:$s.h"; incr i
}
writeln " \\\n\t\t\t\$(SRCDIR)\\sqlite3.h \\"
writeln "\t\t\t\$(SRCDIR)\\th.h \\"
writeln "\t\t\tVERSION.h \\"
writeln "\t\t\t\$(SRCDIR)\\cson_amalgamation.h"
writeln "\t@copy /Y nul: headers"


close $output_file
#
# End of the win/Makefile.msc output
##############################################################################
##############################################################################
##############################################################################
# Begin win/Makefile.PellesCGMake output
#
puts "building ../win/Makefile.PellesCGMake"
set output_file [open ../win/Makefile.PellesCGMake w]
fconfigure $output_file -translation binary

writeln [string map [list \
    <<<SQLITE_OPTIONS>>> [join $SQLITE_WIN32_OPTIONS { }] \
    <<<SHELL_OPTIONS>>> [join $SHELL_WIN32_OPTIONS { }]] {#
##############################################################################
# WARNING: DO NOT EDIT, AUTOMATICALLY GENERATED FILE (SEE "src/makemake.tcl")
##############################################################################
#
# This file is automatically generated.  Instead of editing this
# file, edit "makemake.tcl" then run "tclsh makemake.tcl"
# to regenerate this file.
#
# HowTo
# -----
#
# This is a Makefile to compile fossil with PellesC from
#  http://www.smorgasbordet.com/pellesc/index.htm
# In addition to the Compiler envrionment, you need
#  gmake from http://sourceforge.net/projects/unxutils/, Pelles make version
#        couldn't handle the complex dependencies in this build
#  zlib sources
# Then you do
# 1. create a directory PellesC in the project root directory
# 2. Change the variables PellesCDir/ZLIBSRCDIR to the path of your installation
# 3. open a dos prompt window and change working directory into PellesC (step 1)
# 4. run gmake -f ..\win\Makefile.PellesCGMake
#
# this file is tested with
#   PellesC         5.00.13
#   gmake           3.80
#   zlib sources    1.2.5
#   Windows XP SP 2
# and
#   PellesC         6.00.4
#   gmake           3.80
#   zlib sources    1.2.5
#   Windows 7 Home Premium
#

#
PellesCDir=c:\Programme\PellesC

# Select between 32/64 bit code, default is 32 bit
#TARGETVERSION=64

ifeq ($(TARGETVERSION),64)
# 64 bit version
TARGETMACHINE_CC=amd64
TARGETMACHINE_LN=amd64
TARGETEXTEND=64
else
# 32 bit version
TARGETMACHINE_CC=x86
TARGETMACHINE_LN=ix86
TARGETEXTEND=
endif

# define the project directories
B=..
SRCDIR=$(B)/src/
WINDIR=$(B)/win/
ZLIBSRCDIR=../../zlib/

# define linker command and options
LINK=$(PellesCDir)/bin/polink.exe
LINKFLAGS=-subsystem:console -machine:$(TARGETMACHINE_LN) /LIBPATH:$(PellesCDir)\lib\win$(TARGETEXTEND) /LIBPATH:$(PellesCDir)\lib kernel32.lib advapi32.lib delayimp$(TARGETEXTEND).lib Wsock32.lib dnsapi.lib Crtmt$(TARGETEXTEND).lib

# define standard C-compiler and flags, used to compile
# the fossil binary. Some special definitions follow for
# special files follow
CC=$(PellesCDir)\bin\pocc.exe
DEFINES=-D_pgmptr=g.argv[0]
CCFLAGS=-T$(TARGETMACHINE_CC)-coff -Ot -W2 -Gd -Go -Ze -MT $(DEFINES)
INCLUDE=/I $(PellesCDir)\Include\Win /I $(PellesCDir)\Include /I $(ZLIBSRCDIR) /I $(SRCDIR)

# define commands for building the windows resource files
RESOURCE=fossil.res
RC=$(PellesCDir)\bin\porc.exe
RCFLAGS=$(INCLUDE) -D__POCC__=1 -D_M_X$(TARGETVERSION)

# define the special utilities files, needed to generate
# the automatically generated source files
UTILS=translate.exe mkindex.exe makeheaders.exe mkbuiltin.exe
UTILS_OBJ=$(UTILS:.exe=.obj)
UTILS_SRC=$(foreach uf,$(UTILS),$(SRCDIR)$(uf:.exe=.c))

# define the SQLite files, which need special flags on compile
SQLITESRC=sqlite3.c
ORIGSQLITESRC=$(foreach sf,$(SQLITESRC),$(SRCDIR)$(sf))
SQLITEOBJ=$(foreach sf,$(SQLITESRC),$(sf:.c=.obj))
SQLITEDEFINES=<<<SQLITE_OPTIONS>>>

# define the SQLite shell files, which need special flags on compile
SQLITESHELLSRC=shell.c
ORIGSQLITESHELLSRC=$(foreach sf,$(SQLITESHELLSRC),$(SRCDIR)$(sf))
SQLITESHELLOBJ=$(foreach sf,$(SQLITESHELLSRC),$(sf:.c=.obj))
SQLITESHELLDEFINES=<<<SHELL_OPTIONS>>>

# define the th scripting files, which need special flags on compile
THSRC=th.c th_lang.c
ORIGTHSRC=$(foreach sf,$(THSRC),$(SRCDIR)$(sf))
THOBJ=$(foreach sf,$(THSRC),$(sf:.c=.obj))

# define the zlib files, needed by this compile
ZLIBSRC=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
ORIGZLIBSRC=$(foreach sf,$(ZLIBSRC),$(ZLIBSRCDIR)$(sf))
ZLIBOBJ=$(foreach sf,$(ZLIBSRC),$(sf:.c=.obj))

# define all fossil sources, using the standard compile and
# source generation. These are all files in SRCDIR, which are not
# mentioned as special files above:
ORIGSRC=$(filter-out $(UTILS_SRC) $(ORIGTHSRC) $(ORIGSQLITESRC) $(ORIGSQLITESHELLSRC),$(wildcard $(SRCDIR)*.c))
SRC=$(subst $(SRCDIR),,$(ORIGSRC))
TRANSLATEDSRC=$(SRC:.c=_.c)
TRANSLATEDOBJ=$(TRANSLATEDSRC:.c=.obj)

# main target file is the application
APPLICATION=fossil.exe

# define the standard make target
.PHONY:	default
default:	page_index.h builtin_data.h headers $(APPLICATION)

# symbolic target to generate the source generate utils
.PHONY:	utils
utils:	$(UTILS)

# link utils
$(UTILS) version.exe:	%.exe:	%.obj
	$(LINK) $(LINKFLAGS) -out:"$@" $<

# compiling standard fossil utils
$(UTILS_OBJ):	%.obj:	$(SRCDIR)%.c
	$(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@"

# compile special windows utils
version.obj:	$(SRCDIR)mkversion.c
	$(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@"

# generate the translated c-source files
$(TRANSLATEDSRC):	%_.c:	$(SRCDIR)%.c translate.exe
	translate.exe $< >$@

# generate the index source, containing all web references,..
page_index.h:	$(TRANSLATEDSRC) mkindex.exe
	mkindex.exe $(TRANSLATEDSRC) >$@

builtin_data.h:	$(EXTRA_FILES) mkbuiltin.exe
	mkbuiltin.exe --prefix $(SRCDIR)/ $(EXTRA_FILES) >$@

# extracting version info from manifest
VERSION.h:	version.exe ..\manifest.uuid ..\manifest ..\VERSION
	version.exe ..\manifest.uuid ..\manifest ..\VERSION  >$@

default_css.h:	mkcss.exe default_css.txt
	mkcss.exe default_css.txt $@

# generate the simplified headers
headers: makeheaders.exe page_index.h builtin_data.h default_css.h VERSION.h ../src/sqlite3.h ../src/th.h
	makeheaders.exe $(foreach ts,$(TRANSLATEDSRC),$(ts):$(ts:_.c=.h)) ../src/sqlite3.h ../src/th.h VERSION.h
	echo Done >$@

# compile C sources with relevant options

$(TRANSLATEDOBJ):	%_.obj:	%_.c %.h
	$(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@"

$(SQLITEOBJ):	%.obj:	$(SRCDIR)%.c $(SRCDIR)%.h
	$(CC) $(CCFLAGS) $(SQLITEDEFINES) $(INCLUDE) "$<" -Fo"$@"

$(SQLITESHELLOBJ):	%.obj:	$(SRCDIR)%.c
	$(CC) $(CCFLAGS) $(SQLITESHELLDEFINES) $(INCLUDE) "$<" -Fo"$@"

$(THOBJ):	%.obj:	$(SRCDIR)%.c $(SRCDIR)th.h
	$(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@"

$(ZLIBOBJ):	%.obj:	$(ZLIBSRCDIR)%.c
	$(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@"

# create the windows resource with icon and version info
$(RESOURCE):	%.res:	../win/%.rc ../win/*.ico
	$(RC) $(RCFLAGS) $< -Fo"$@"

# link the application
$(APPLICATION):	$(TRANSLATEDOBJ) $(SQLITEOBJ) $(SQLITESHELLOBJ) $(THOBJ) $(ZLIBOBJ) headers $(RESOURCE)
	$(LINK) $(LINKFLAGS) -out:"$@" $(TRANSLATEDOBJ) $(SQLITEOBJ) $(SQLITESHELLOBJ) $(THOBJ) $(ZLIBOBJ) $(RESOURCE)

# cleanup

.PHONY: clean
clean:
	-del /F $(TRANSLATEDOBJ) $(SQLITEOBJ) $(THOBJ) $(ZLIBOBJ) $(UTILS_OBJ) version.obj
	-del /F $(TRANSLATEDSRC)
	-del /F *.h headers
	-del /F $(RESOURCE)

.PHONY: clobber
clobber: clean
	-del /F *.exe
}]

Changes to src/manifest.c.

66
67
68
69
70
71
72
73

74
75
76
77
78
79
80
66
67
68
69
70
71
72

73
74
75
76
77
78
79
80







-
+







/*
** 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 */
  char *zBaseline;      /* Baseline manifest.  The B card. */
  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. */
128
129
130
131
132
133
134
135

136
137
138
139
140
141
142
143
144
145

146
147
148
149
150
151
152
128
129
130
131
132
133
134

135
136
137
138
139
140
141
142
143
144

145
146
147
148
149
150
151
152







-
+









-
+







} 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 */ { "DLNPUWZ",       "DLUWZ"       },
  /* 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 *azNameOfMType[] = {
static const char *const azNameOfMType[] = {
  "manifest",
  "cluster",
  "tag",
  "wiki",
  "ticket",
  "attachment",
  "technote",
166
167
168
169
170
171
172

























173
174
175
176
177
178
179
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+








/*
** 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);
287
288
289
290
291
292
293
294
295


296
297
298
299
300
301
302
312
313
314
315
316
317
318


319
320
321
322
323
324
325
326
327







-
-
+
+







  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(char **pz, int *pn){
  char *z = *pz;
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;
316
317
318
319
320
321
322
323


324
325
326
327

328

329
330



331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352

353
354
355
356
357





358
359

360
361
362
363
364
365
366
341
342
343
344
345
346
347

348
349
350
351
352
353
354

355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379

380

381
382




383
384
385
386
387


388
389
390
391
392
393
394
395







-
+
+




+
-
+


+
+
+



















-

-
+

-
-
-
-
+
+
+
+
+
-
-
+







** 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){
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], md5sum_finish(0), 32)==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 *z;
  char *zStart;
  int c;
  int n;
  if( p->atEol ) return 0;
  zStart = z = p->z;
  while( (c=(*z))!=' ' && c!='\n' ){ z++; }
  *z = 0;
  p->z = &z[1];
  zStart = p->z;
  n = strcspn(p->z, " \n");
  p->atEol = p->z[n]=='\n';
  p->z[n] = 0;
  p->z += n+1;
  p->atEol = c=='\n';
  if( pLen ) *pLen = z - zStart;
  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.
*/
380
381
382
383
384
385
386













387
388
389
390
391
392
393
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435







+
+
+
+
+
+
+
+
+
+
+
+
+







  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.
**
424
425
426
427
428
429
430
431
432
433
434
435
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
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







-







-
+



-
+










+
-
+
+





-
+






+
-
+
+




+
+
+
-
+

-


+
+
+
+
+
+















-
+
+
+
+
+
+
+
+
+
+
+
+
+



+


















-
+







  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 */
  static Bag seen;
  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(&seen, rid) ){
  }else if( bag_find(&seenManifests, rid) ){
    isRepeat = 1;
  }else{
    isRepeat = 0;
    bag_insert(&seen, rid);
    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");
      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(&z, &n);
  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");
      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)==2 ){
  if( verify_z_card(z, n, pErr)==2 ){
    blob_reset(pContent);
    blob_appendf(pErr, "incorrect Z-card cksum");
    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 && cType>=cPrevType ){
  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(zName, 0) ){
        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");
        }
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
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







+



















-
+




+








-
+









+
+
+
+
+
+
+
+
+
+




-
-
-







      ** 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(zName, 0) ){
        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(zPriorName, 0) ){
          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;
        if( i>0 && fossil_strcmp(p->aFile[i-1].zName, zName)>=0 ){
          SYNTAX("incorrect F-card sort order");
        }
        p->type = CFTYPE_MANIFEST;
        break;
      }

      /*
      **    G <hash>
      **
1049
1050
1051
1052
1053
1054
1055
1056

1057

1058


1059
1060
1061

1062
1063
1064
1065





1066
1067
1068
1069
1070
1071
1072
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







-
+

+
-
+
+



+
-
-
-
-
+
+
+
+
+








  md5sum_init();
  if( !isRepeat ) g.parseCnt[p->type]++;
  return p;

manifest_syntax_error:
  {
    char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
    char *zUuid = rid_to_uuid(rid);
    if( zUuid ){
      if(pErr!=0){
      blob_appendf(pErr, "artifact [%s] ", zUuid);
        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);
    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;
}

/*
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
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+














+
-
+






+
+
+






+
-
+
+
+
+
+
+
+
+
+
+



+





-
+





+
+
+
+
+
+
+
+







+
+
+

+
+
+

+
+
+
-
-
+
+
+





+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+







  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(&copy, 0, 0);
  blob_init(&errmsg, 0, 0);
  blob_append(&copy, zIn, nIn);
  pManifest = manifest_parse(&copy, 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(0,0);
  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 ){
    if( p==0 ) fossil_print("ERROR: %s\n", blob_str(&err));
      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
** 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");
  while( db_step(&q)==SQLITE_ROW ){
    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++;
      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);
}
1305
1306
1307
1308
1309
1310
1311
1312

1313
1314
1315
1316
1317
1318
1319
1476
1477
1478
1479
1480
1481
1482

1483
1484
1485
1486
1487
1488
1489
1490







-
+







  return fnid;
}

/*
** Compute an appropriate mlink.mperm integer for the permission string
** of a file.
*/
int manifest_file_mperm(ManifestFile *pFile){
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;
    }
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
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







+
+
+
+
+
+
+
















-
+





+
















-
+
+
+
+
+








-
+







  */
  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 **azParent            /* hashes for each parent */
  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);
  if( nParent>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, nParent
      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);
1748
1749
1750
1751
1752
1753
1754
1755

1756
1757
1758
1759
1760
1761
1762
1931
1932
1933
1934
1935
1936
1937

1938
1939
1940
1941
1942
1943
1944
1945







-
+







                    isPublic, 1, manifest_file_mperm(&p->aFile[i]));
    }
  }
  return parentid;
}

/*
** There exists a "parent" tag against checkin rid that has value zValue.
** 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;
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
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







-
-
-
-
+
-







-
-
+
+
















+


-
+








+
+
+
+
+
+
+
+
+
+
+







    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;
  }
  if( !db_exists("SELECT 1 FROM plink WHERE cid=%d AND pid=%d",
                 rid, uuid_to_rid(azParent[0],0))
  ){
    p = manifest_get(rid, CFTYPE_MANIFEST, 0);
  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);
    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_tkt(uuid TEXT UNIQUE);"
     "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 */
1857
1858
1859
1860
1861
1862
1863
1864

1865
1866
1867
1868
1869












1870
1871
1872
1873

1874
1875
1876
1877
1878
1879
1880
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







-
+

-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+



-
+







  );
  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 uuid FROM pending_tkt");
  db_prepare(&q, "SELECT id FROM pending_xlink");
  while( db_step(&q)==SQLITE_ROW ){
    const char *zUuid = db_column_text(&q, 0);
    ticket_rebuild_entry(zUuid);
    if( permitHooks && rc==TH_OK ){
      rc = xfer_run_script(zScript, zUuid, 0);
    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_tkt");
  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"
1903
1904
1905
1906
1907
1908
1909






















1910
1911
1912
1913
1914
1915
1916
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_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 */
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
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







+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+


















-
+



-
+
+



-
+

-
+







-
+




















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+








+







    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(
    "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)
  );
    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){
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 manifest.\n";
      "# 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(&z, &n);
  remove_pgp_signature((const char **)&z, &n);
  if( z==zOrig ){
    blob_append(p, zExtraLine, -1);
    blob_appendf(p, zExtraLine/*works-like:"%s"*/, zType);
  }else{
    int iEnd;
    Blob copy;
    memcpy(&copy, p, sizeof(copy));
    blob_init(p, 0, 0);
    iEnd = (int)(&z[n] - zOrig);
    blob_append(p, zOrig, iEnd);
    blob_append(p, zExtraLine, -1);
    blob_appendf(p, zExtraLine/*works-like:"%s"*/, zType);
    blob_append(p, &zOrig[iEnd], -1);
    blob_zero(&copy);
  }
}

/*
** 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 key:
** 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.
2072
2073
2074
2075
2076
2077
2078

2079
2080
2081
2082
2083

2084

2085

2086
2087
2088
2089
2090
2091
2092
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350

2351

2352
2353
2354
2355
2356
2357
2358
2359







+





+
-
+
-
+







  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]",
      fossil_error(1, "syntax error in manifest [%S]", zErrUuid);
                   db_text(0, "SELECT uuid FROM blob WHERE rid=%d",rid));
      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");
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
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







+
+
+
+
+
+
+
+
+
+
+





+
-
+










-
+
+






-
-
-
+







    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 );
      db_multi_exec(
      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);",
        "  (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
      );
      zCom = db_text(0, "SELECT coalesce(ecomment, comment) FROM event"
                        " WHERE rowid=last_insert_rowid()");
      wiki_extract_links(zCom, rid, 0, p->rDate, 1, WIKI_INLINE);
      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 ){
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
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







+










+
+
+
+


-
-
-
+
+


+





-
-
-
-
-
-
+
+
+



-
-
+
+
+
+

-
+


+
+
+
+
+
+

-
+
-
-
+
-
-
-
-
+
-
-
-

-




-
+








+
-
+
-
-
-
-
-
+

+
+







      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 tagid = tag_findid(zTag, 1);
    int prior;
    char *zComment;
    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);
    prior = db_int(0,
      "SELECT rid FROM tagxref"
      " WHERE tagid=%d AND mtime<%.17g"
      " ORDER BY mtime DESC",
      tagid, p->rDate
    );
    if(p->nParent){
      prior = fast_uuid_to_rid(p->azParent[0]);
    }
    if( prior ){
      content_deltify(prior, &rid, 1, 0);
    }
    if( nWiki>0 ){
      zComment = mprintf("Changes to wiki page [%h]", p->zWikiTitle);
    if( nWiki<=0 ){
      cPrefix = '-';
    }else if( !prior ){
      cPrefix = '+';
    }else{
      zComment = mprintf("Deleted wiki page [%h]", p->zWikiTitle);
      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,"
      "REPLACE INTO event(type,mtime,objid,user,comment)"
      "                  bgcolor,euser,ecomment)"
      "VALUES('w',%.17g,%d,%Q,%Q,"
      "VALUES('w',%.17g,%d,%Q,'%c%q');",
      "  (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>1),"
      "  (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d),"
      "  (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));",
      p->rDate, rid, p->zUser, zComment,
      p->rDate, rid, p->zUser, cPrefix, p->zWikiTitle
      TAG_BGCOLOR, rid,
      TAG_USER, rid,
      TAG_COMMENT, rid
    );
    fossil_free(zComment);
  }
  if( p->type==CFTYPE_EVENT ){
    char *zTag = mprintf("event-%s", p->zEventId);
    int tagid = tag_findid(zTag, 1);
    int prior, subsequent;
    int 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 = db_int(0,
      prior = fast_uuid_to_rid(p->azParent[0]);
      "SELECT rid FROM tagxref"
      " WHERE tagid=%d AND mtime<%.17g AND rid!=%d"
      " ORDER BY mtime DESC",
      tagid, p->rDate, rid
    );
    }
    subsequent = db_int(0,
      /* 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);
2267
2268
2269
2270
2271
2272
2273

2274
2275
2276
2277
2278
2279
2280
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560







+







        );
      }
    }
    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
      );
2313
2314
2315
2316
2317
2318
2319
2320
2321

2322
2323
2324
2325
2326
2327
2328
2593
2594
2595
2596
2597
2598
2599


2600
2601
2602
2603
2604
2605
2606
2607







-
-
+







  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);
    db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)",
                  p->zTicketUuid);
    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 ){
2351
2352
2353
2354
2355
2356
2357
2358

2359
2360
2361
2362
2363
2364
2365
2630
2631
2632
2633
2634
2635
2636

2637
2638
2639
2640
2641
2642
2643
2644







-
+







    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_uuid(p->zAttachTarget) ){
    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)
            ){
2408
2409
2410
2411
2412
2413
2414

2415
2416
2417
2418
2419
2420
2421
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701







+







             "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);
  }
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
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







+












+
















+







        blob_appendf(&comment, " with note \"%h\".", zValue);
      }else{
        blob_appendf(&comment, ".");
      }
    }
    /*blob_appendf(&comment, " &#91;[/info/%S | details]&#93;");*/
    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
2571
2572
2573
2574
2575
2576
2577

2578
2579
2580
2581
2582
2583
2584



2585



2586
2587
2588
2589
2590
2591
2592
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







+







+
+
+
-
+
+
+







      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);
    }
  }

Changes to src/markdown.c.

43
44
45
46
47
48
49

50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67


68
69
70
71
72

73
74
75
76
77
78
79
80
81
82
83


84
85
86
87
88
89
90
91
92
93
94
95
96
97
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74

75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94

95
96
97
98
99
100
101







+


















+
+




-
+











+
+






-







};

/* 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, 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 */
  int max_work_stack; /* prevent arbitrary deep recursion, cf README */
  const char *emph_chars; /* chars that trigger emphasis rendering */
  void *opaque; /* opaque data send to every rendering callback */
};



/*********
107
108
109
110
111
112
113
114

115
116
117
118
119
120
121
122
123
124
125


126
127

128
129
130
131
132

133


134
135

136
137
138
139
















140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158



159
160








161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176






177
178
179

180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210

211



212
213
214
215
216
217
218
111
112
113
114
115
116
117

118



119







120
121


122
123
124
125
126
127
128

129
130
131

132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174


175
176
177
178
179
180
181
182
183

184
185
186
187
188
189
190
191
192
193
194
195
196

197
198
199
200
201
202
203


204











205




206



207





208
209
210
211
212
213

214
215
216
217
218
219
220
221
222
223







-
+
-
-
-

-
-
-
-
-
-
-
+
+
-
-
+





+
-
+
+

-
+




+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



















+
+
+
-
-
+
+
+
+
+
+
+
+

-













-
+
+
+
+
+
+

-
-
+
-
-
-
-
-
-
-
-
-
-
-

-
-
-
-

-
-
-

-
-
-
-
-





+
-
+
+
+







#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 */
/**********************
 * EXPORTED FUNCTIONS *
 **********************/

/* markdown -- parses the input buffer and renders it into the output buffer */
void markdown(
  struct Blob *ob,
  struct Blob *ib,
  const struct mkd_renderer *rndr);


#define BLOB_COUNT(pBlob,el_type) (blob_size(pBlob)/sizeof(el_type))
#define COUNT_FOOTNOTES(pBlob) BLOB_COUNT(pBlob,struct footnote)
#endif /* INTERFACE */

#define CAST_AS_FOOTNOTES(pBlob) ((struct footnote*)blob_buffer(pBlob))

/***************
 * LOCAL TYPES *
 ***************/

/*
/* link_ref -- reference to a link */
** link_ref -- reference to a link.
*/
struct link_ref {
  struct Blob id;
  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 */
  int work_active;
  struct Blob *work;

  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 */
/* 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[] = {
  { "p",            1 },
  { "dl",           2 },
  { "html",         4 },
  { "h1",           2 },
  { "h2",           2 },
  { "h3",           2 },
  { "h4",           2 },
  { "h5",           2 },
  { "h6",           2 },
  { "ol",           2 },
  { "ul",           2 },
  { "del",          3 },
  { "div",          3 },
  { "ins",          3 },
  { "pre",          3 },
  { "form",         4 },
  { "math",         4 },
  { "table",        5 },
  { "iframe",       6 },
  { "script",       6 },
  { "fieldset",     8 },
  { "noscript",     8 },
  { "blockquote",  10 }
};

#define INS_TAG (block_tags + 12)
#define DEL_TAG (block_tags + 10)



/***************************
 * STATIC HELPER FUNCTIONS *
 ***************************/

/*
/* build_ref_id -- collapse whitespace from input text to make it a ref id */
** 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++;
236
237
238
239
240
241
242
243

244
245
246
247
248
249
250
241
242
243
244
245
246
247

248
249
250
251
252
253
254
255







-
+







    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(id, " ", 1);
    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';
263
264
265
266
267
268
269



















270






























271
272
273
274
275
276






277
278
279
280
281
282
283
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293

294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327


328
329
330
331
332
333
334
335
336
337
338
339
340







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+




-
-
+
+
+
+
+
+







/* 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;
  if( hta->size!=htb->size ) return hta->size-htb->size;
  return fossil_strnicmp(hta->text, htb->text, hta->size);
  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;
298
299
300
301
302
303
304



305
306




307
308
309
310
311




312
313


314

315
316
317
318
319

320
321
322
323








324
325
326
327
328
329
330
355
356
357
358
359
360
361
362
363
364


365
366
367
368
369




370
371
372
373


374
375
376
377
378
379
380
381

382
383
384


385
386
387
388
389
390
391
392
393
394
395
396
397
398
399







+
+
+
-
-
+
+
+
+

-
-
-
-
+
+
+
+
-
-
+
+

+




-
+


-
-
+
+
+
+
+
+
+
+







  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;

/* new_work_buffer -- get a new working buffer from the stack or create one */
}

/* 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 = 0;

  if( rndr->work_active < rndr->make.max_work_stack ){
    ret = rndr->work + rndr->work_active;
  struct Blob *ret;
  rndr->iDepth++;
  if( rndr->nBlobCache ){
    ret = rndr->aBlobCache[--rndr->nBlobCache];
    rndr->work_active += 1;
    blob_reset(ret);
  }else{
    ret = fossil_malloc(sizeof(*ret));
  }
  *ret = empty_blob;
  return ret;
}


/* release_work_buffer -- release the given working buffer */
/* release the given working buffer back to the cache */
static void release_work_buffer(struct render *rndr, struct Blob *buf){
  if( !buf ) return;
  assert(rndr->work_active>0 && buf==(rndr->work+rndr->work_active-1));
  rndr->work_active -= 1;
  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 *
 ****************************/
357
358
359
360
361
362
363




364
365
366
367
368
369
370
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443







+
+
+
+







  /* 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
419
420
421
422
423
424
425




426
427
428
429
430
431
432
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509







+
+
+
+







  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++;
    }
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
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


-
+













-
-
+
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+







    }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 = 1;
  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;

    /* skipping a code span */
    if( data[i]=='`' ){
    if( data[i]=='`' ){            /* skip a code span */
      size_t span_nb = 0, bt;
      size_t tmp_i = 0;

      skip_codespan(data, size, &i);
      /* counting the number of opening backticks */
      while( i<size && data[i]=='`' ){
        i++;
        span_nb++;
      }
      if( i>=size ) return 0;

      /* finding the matching closing sequence */
      bt = 0;
      while( i<size && bt<span_nb ){
        if( !tmp_i && data[i]==c ) tmp_i = i;
        if( data[i]=='`' ) bt += 1; else bt = 0;
        i++;
      }
      if( i>=size ) return tmp_i;
      i++;

    /* skipping a link */
    }else if( data[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++;
      }
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
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+










+
















+

-
-
-
+
+
+


-









-
-
+
+
+










+







+



-
-
-
-
+
+
+


-










-
-
-
+
+
+
+







      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;

/* parse_emph1 -- parsing single emphasis */
/* closed by a symbol not preceded by whitespace and not followed by symbol */
}
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
     && data[i-1]!=' '
     && data[i-1]!='\t'
     && data[i-1]!='\n'
     && right_flanking(data[i-1],after)
     && (c!='_' || !fossil_isalnum(after))
     && !too_deep(rndr)
    ){
      work = new_work_buffer(rndr);
      if( !work ) return 0;
      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 */
/*
** 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
     && i
     && data[i-1]!=' '
     && data[i-1]!='\t'
     && data[i-1]!='\n'
     && right_flanking(data[i-1],after)
     && (c!='_' || !fossil_isalnum(after))
     && !too_deep(rndr)
    ){
      work = new_work_buffer(rndr);
      if( !work ) return 0;
      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 */
/*
** 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
){
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
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







+



-

















-
-
+
+
+








+



-
-
-
-
+
+








-
-
-
+
+








-
-
-
+
+









-
-
+
+
+













-
-
+
+
+








+


-
+




-
+















-
+

-
+





+
-
+
+



















-
-
-
+
+
+
+







      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);
      if( !work ) return 0;
      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 */
/*
** 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 ){
    /* whitespace cannot follow an opening emphasis */
    if( data[1]==' '
     || data[1]=='\t'
     || data[1]=='\n'
    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( data[2]==' '
     || data[2]=='\t'
     || data[2]=='\n'
    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( data[3]==' '
     || data[3]=='\t'
     || data[3]=='\n'
    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) */
/*
** 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) */
/*
** 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]=='`' ){ nb++; }
  while( nb<size && data[nb]==delim ){ nb++; }

  /* finding the next delimiter */
  i = 0;
  for(end=nb; end<size && i<nb; end++){
    if( data[end]=='`' ) i++; else i = 0;
    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, rndr->make.opaque) ) end = 0;
    if( !rndr->make.codespan(ob, &work, nb, rndr->make.opaque) ) end = 0;
  }else{
    if( !rndr->make.codespan(ob, 0, rndr->make.opaque) ) end = 0;
    if( !rndr->make.codespan(ob, 0, nb, rndr->make.opaque) ) end = 0;
  }
  return end;
}


/*
/* char_escape -- '\\' backslash escape */
** 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]+; */
/*
** 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
){
810
811
812
813
814
815
816
817
818



819
820
821
822
823
824
825
949
950
951
952
953
954
955


956
957
958
959
960
961
962
963
964
965







-
-
+
+
+







    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 */
/*
** 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
){
840
841
842
843
844
845
846
847
848


849
850
851
852
853
854
855
980
981
982
983
984
985
986


987
988
989
990
991
992
993
994
995







-
-
+
+







  if( !ret ){
    return 0;
  }else{
    return end;
  }
}


/* get_link_inline -- extract inline-style link and title from
/*
** 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
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
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







-
+




















+
-
+
+








+



-
+


-
+







+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
-
+
+





-
+

+
-
-
-
-
-
+
+
+
+
+
+



+
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
+
-
-
-
-
+
-
-
-
-
-
+
+


-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
-
-
+
+
-
-
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
-
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
+
+
+
+

-
-
+
+




-
-
+
+
+
+

+
+
+
+
+











-







       || data[link_e-1]=='\n')
  ){
    link_e--;
  }

  /* remove optional angle brackets around the link */
  if( data[link_b]=='<' ) link_b += 1;
  if( data[link_e-1]=='>' ) link_e -= 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 */
** 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( build_ref_id(link, data, size)<0 ) return -1;
  if( !sz || build_ref_id(link, data, size)<0 ) return -1;
  lr = bsearch(link,
               blob_buffer(&rndr->refs),
               blob_size(&rndr->refs)/sizeof(struct link_ref),
               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;
}
  blob_append(link, blob_buffer(&lr->link), blob_size(&lr->link));
  blob_append(title, blob_buffer(&lr->title), blob_size(&lr->title));

/*
** 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 */
** 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
  size_t size     /* parse_inline() ensures that size > 0 */
){
  const int bFsfn = (size>3 && data[1]=='^'); /*free-standing footnote ref*/
  int is_img = (offset && data[-1] == '!'), level;
  size_t i = 1, txt_e;
  struct Blob *content = 0;
  struct Blob *link = 0;
  struct Blob *title = 0;
  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( (is_img && !rndr->make.image) || (!is_img && !rndr->make.link) ){
    return 0;
  }

    if( (bImg && !rndr->make.image) || (!bImg && !rndr->make.link) ){
      return 0;
    }
  }
  /* looking for the matching closing bracket */
  for(level=1; i<size; i++){
    if( data[i]=='\n' )        /* do nothing */;
    else if( data[i-1]=='\\' ) continue;
    else if( data[i]=='[' )    level += 1;
    else if( data[i]==']' ){
      level--;
      if( level<=0 ) break;
    }

  }
  if( i>=size ) return 0;
  txt_e = i;
  i++;

  /* looking for the matching closing bracket */
  /* skip any amount of whitespace or newline */
  /* (this is much more laxist than original markdown syntax) */
  while( i<size && (data[i]==' ' || data[i]=='\t' || data[i]=='\n') ){ i++; }

  txt_e = matching_bracket_offset(data, data+size);
  /* allocate temporary buffers to store content, link and title */
  content = new_work_buffer(rndr);
  link = new_work_buffer(rndr);
  title = new_work_buffer(rndr);
  if( !title ) return 0;
  if( !txt_e ) return 0;
  i = txt_e + 1;
  ret = 0; /* error if we don't get to the callback */

  /* inline style link */
  if( i<size && data[i]=='(' ){
    size_t span_end = i;
    while( span_end<size
     && !(data[span_end]==')' && (span_end==i || data[span_end-1]!='\\'))
    ){
      span_end++;
    }
  /* 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 */
    if( span_end>=size
     || get_link_inline(link, title, data+i+1, span_end-(i+1))<0

        const size_t k = matching_bracket_offset(data+i, data+size);
    ){
      goto char_link_cleanup;
        if( !k ) goto char_link_cleanup;
    }

    i = span_end+1;

  /* reference style link */
  }else if( i<size && data[i]=='[' ){
    char *id_data;
    size_t id_size, id_end = i;
        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++; }
      while( id_end<size && data[id_end]!=']' ){ id_end++; }

    if( id_end>=size ) goto char_link_cleanup;

    if( i+1==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( get_link_ref(rndr, link, title, id_data, id_size)<0 ){
      goto char_link_cleanup;
    }
      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;
      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;
    }
    /* shortcut reference style link */
    }else{
      if( get_link_ref(rndr, link, title, data+1, txt_e-1)<0 ){
        goto char_link_cleanup;
      }

    /* rewinding the whitespace */
    i = txt_e+1;
  }

      /* rewinding an "inter-bracket-whitespace" */
      i = txt_e+1;
    }
  }
  /* building content: img alt is escaped, link content is parsed */
  if( txt_e>1 ){
    if( is_img ) blob_append(content, data+1, txt_e-1);
  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( is_img ){
    if( blob_size(ob)>0 && blob_buffer(ob)[blob_size(ob)-1]=='!' ) ob->nUsed--;
  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 */
1157
1158
1159
1160
1161
1162
1163




1164
1165
1166
1167
1168
1169
1170
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524







+
+
+
+








  /* 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 */
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
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







+
+
+
+
+
+
+
+
+
+
+
+












-
+







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]!=')')
   || (data[i+1]!=' ' && data[i+1]!='\t')
  ){
   return 0;
  }
  i = i+2;
  while( i<size && (data[i]==' ' || data[i]=='\t') ){ i++; }
  return i;
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
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







-
+


+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+









+
-
+

















-
-
+


-
+

-
+

















-


+
+
+
-
+
+
+





-
+
+
+
+
+












-
-
+
-
-
-
-
+












-
-
+
-
-
-

-
+











-
-
-
+
+
-
-
-
-
















-



+
+
+
-
+
+
+










-
+










-
+




















-







/* 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;
  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
             && 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 ){
    struct Blob fallback = BLOB_INITIALIZER;
    if( out ){
    if( !too_deep(rndr) ){
      parse_block(out, rndr, work_data, work_size);
    }else{
      blob_init(&fallback, work_data, work_size);
      blob_append(out, work_data, work_size);
    }
    rndr->make.blockquote(ob, out ? out : &fallback, rndr->make.opaque);
    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;
  struct Blob fallback = BLOB_INITIALIZER;

  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++);
    **    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) ){
    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);
      if( tmp ){
        parse_inline(tmp, rndr, work_data, work_size);
      parse_inline(tmp, rndr, work_data, work_size);
      }else{
        blob_init(&fallback, work_data, work_size);
      }
      rndr->make.paragraph(ob, tmp ? tmp : &fallback, rndr->make.opaque);
      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);
        if( tmp ){
          parse_inline(tmp, rndr, work_data, work_size);
        parse_inline(tmp, rndr, work_data, work_size);
        }else{
          blob_init (&fallback, work_data, work_size);
        }
        if( rndr->make.paragraph ){
          rndr->make.paragraph(ob, tmp ? tmp : &fallback, rndr->make.opaque);
          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);
      if( span ){
        parse_inline(span, rndr, work_data, work_size);
        rndr->make.header(ob, span, level, rndr->make.opaque);
      parse_inline(span, rndr, work_data, work_size);
      rndr->make.header(ob, span, level, rndr->make.opaque);
      }else{
        blob_init(&fallback, work_data, work_size);
        rndr->make.header(ob, &fallback, 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);
  if( !work ) work = ob;

  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++);
    **   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(work, "\n", 1);
        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(work, "\n", 1);
  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 fallback = BLOB_INITIALIZER;
  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;
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1845
1846
1847
1848
1849
1850
1851

1852
1853
1854
1855
1856
1857
1858







-







  /* 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);
  if( !work ) work = &fallback;

  /* putting the first line into the working buffer */
  blob_append(work, data+beg, end-beg);
  beg = end;

  /* process the following lines */
  while( beg<size ){
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
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







-
+














-
+
-







      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(work, "\n", 1);
      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);
    }
    if( work!=&fallback ) release_work_buffer(rndr, work);
    release_work_buffer(rndr, work);
    blob_reset(&fallback);
    return beg;
  }

  /* render of li contents */
  if( has_inside_empty ) *flags |= MKD_LI_BLOCK;
  if( *flags & MKD_LI_BLOCK ){
    /* intermediate render of block li */
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
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







-
+
-












-


-








-
+
-
















-
+

+











-

-
-
-
+
-
-
-
-
+


















-
+







  }

  /* render of li itself */
  if( rndr->make.listitem ){
    rndr->make.listitem(ob, inter, *flags, rndr->make.opaque);
  }
  release_work_buffer(rndr, inter);
  if( work!=&fallback ) release_work_buffer(rndr, work);
  release_work_buffer(rndr, work);
  blob_reset(&fallback);
  return beg;
}


/* parse_list -- parsing ordered or unordered list block */
static size_t parse_list(
  struct Blob *ob,
  struct render *rndr,
  char *data,
  size_t size,
  int flags
){
  struct Blob fallback = BLOB_INITIALIZER;
  struct Blob *work = new_work_buffer(rndr);
  size_t i = 0, j;
  if( !work ) work = &fallback;

  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);
  if( work!=&fallback ) release_work_buffer(rndr, work);
  release_work_buffer(rndr, work);
  blob_reset(&fallback);
  return i;
}


/* parse_atxheader -- parsing of atx-style headers */
static size_t parse_atxheader(
  struct Blob *ob,
  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<size && level<6 && data[level]=='#' ){ level++; }
  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 fallback = BLOB_INITIALIZER;
    struct Blob *span = new_work_buffer(rndr);

    if( span ){
      parse_inline(span, rndr, data+span_beg, span_size);
    parse_inline(span, rndr, data+span_beg, span_size);
    }else{
      blob_init(&fallback, data+span_beg, span_size);
    }
    rndr->make.header(ob, span ? span : &fallback, level, rndr->make.opaque);
    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)>=size
  if( (tag->size+3)>(int)size
    || fossil_strnicmp(data+2, tag->text, tag->size)
    || data[tag->size+2]!='>'
  ){
    return 0;
  }

  /* checking white lines */
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
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







-
-
+
+


-


-
+
-
-
-
+







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-



+
+
+
+
+
+
+
-
+
+















-

-
-
-
+
-
-
-
-
+







      }
    }

    /* no special case recognised */
    return 0;
  }

  /* looking for an unindented matching closing tag */
  /*  followed by a blank line */
  /* looking for an matching closing tag */
  /* followed by a blank line */
  i = 1;
  found = 0;
#if 0
  while( i<size ){
    i++;
    while( i<size && !(data[i-2]=='\n' && data[i-1]=='<' && data[i]=='/') ){
    while( i<size && !(data[i-1]=='<' && data[i]=='/') ){ i++; }
      i++;
    }
    if( (i+2+curtag->size)>=size ) break;
    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;
    }
  }
#endif

  /* if not found, trying a second pass looking for indented match */
  /* but not if tag is "ins" or "del" (following original Markdown.pl) */
  if( !found && curtag!=INS_TAG && curtag!=DEL_TAG ){
    i = 1;
    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);
    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 fallback = BLOB_INITIALIZER;
  struct Blob *span = new_work_buffer(rndr);

  if( span ){
    parse_inline(span, rndr, data, size);
  parse_inline(span, rndr, data, size);
  }else{
    blob_init(&fallback, data, size);
  }
  rndr->make.table_cell(ob, span ? span : &fallback, flags, rndr->make.opaque);
  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 */
1830
1831
1832
1833
1834
1835
1836
1837







1838
1839
1840
1841
1842
1843
1844
2187
2188
2189
2190
2191
2192
2193

2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207







-
+
+
+
+
+
+
+







    }

    /* 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' ){ i++; }
    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 */
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
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







+
-
+
+





-
-
+
-
-
-
-
-















-


-









-
+







    /* (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 ){
    if( cells ) parse_table_cell(cells, rndr, data+beg, end-beg, align|flags);
      parse_table_cell(cells, rndr, data+beg, end-beg, align|flags);
    }

    col++;
  }

  /* render the whole row and clean up */
  if( cells ){
    rndr->make.table_row(ob, cells, flags, rndr->make.opaque);
  rndr->make.table_row(ob, cells, flags, rndr->make.opaque);
  }else{
    struct Blob fallback = BLOB_INITIALIZER;
    blob_init(&fallback, data, total ? total : size);
    rndr->make.table_row(ob, &fallback, 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 fallback = BLOB_INITIALIZER;
  struct Blob *head = 0;
  struct Blob *rows = new_work_buffer(rndr);
  if( !rows ) rows = &fallback;

  /* 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);
    if( rows!=&fallback ) release_work_buffer(rndr, rows);
    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
1919
1920
1921
1922
1923
1924
1925
1926
1927

1928
1929
1930
1931
1932
1933
1934
1935
2276
2277
2278
2279
2280
2281
2282


2283

2284
2285
2286
2287
2288
2289
2290







-
-
+
-







  }

  if( i<size && data[i]=='\n' ){
    align_size++;

    /* render the header row */
    head = new_work_buffer(rndr);
    if( head ){
      parse_table_row(head, rndr, data, head_end, 0, 0, MKD_CELL_HEAD);
    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;

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
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







-
-
+
+














+
+
-
+

-
+
+







    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 */
  if( head ) release_work_buffer(rndr, head);
  if( rows!=&fallback ) release_work_buffer(rndr, rows);
  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;
  int has_table = (rndr->make.table
  has_table = (rndr->make.table
    && rndr->make.table_row
    && rndr->make.table_cell);
    && 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);
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
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







+
+
+
+














-
+







      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(
  char *data,         /* input text */
  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;
2059
2060
2061
2062
2063
2064
2065


2066
2067
2068
2069
2070
2071
2072
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436







+
+







    }
  }
  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++;
2087
2088
2089
2090
2091
2092
2093

2094
2095
2096
2097
2098
2099
2100
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465







+







   && 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'
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







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







+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+








-
+



-
-
+
+
+

-
+
+
+




-
-
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+













+




+
-
+

+
-
+
-
-
+
+
+



-
+



-
-
+
+

-
+
-
-
-
+














+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



+



-
+





+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
  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 */
  struct Blob *ib,                   /* input blob in markdown */
  const struct Blob *ib,             /* input blob in markdown */
  const struct mkd_renderer *rndrer  /* renderer descriptor (callbacks) */
){
  struct link_ref *lr;
  struct Blob text = BLOB_INITIALIZER;
  size_t i, beg, end = 0;
  struct footnote *fn;
  int i;
  size_t beg, end = 0;
  struct render rndr;
  char *ib_data;
  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;
  if( rndr.make.max_work_stack<1 ) rndr.make.max_work_stack = 1;
  rndr.work_active = 0;
  rndr.nBlobCache = 0;
  rndr.iDepth = 0;
  rndr.work = fossil_malloc(rndr.make.max_work_stack * sizeof *rndr.work);
  for(i=0; i<rndr.make.max_work_stack; i++) rndr.work[i] = text;
  rndr.refs = text;
  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,
  /* first pass: looking for references, copying everything else */
   * copying everything else into "text" */
  beg = 0;
  for(size = blob_size(ib); beg<size ;){
  ib_data = blob_buffer(ib);
    const char* const data = blob_buffer(ib);
  while( beg<blob_size(ib) ){ /* iterating over lines */
    if( is_ref(ib_data, beg, blob_size(ib), &end, &rndr.refs) ){
    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<blob_size(ib) && ib_data[end]!='\n' && ib_data[end]!='\r' ){
      while( end<size && data[end]!='\n' && data[end]!='\r' ){
        end += 1;
      }
      /* adding the line body if present */
      if( end>beg ) blob_append(&text, ib_data + beg, end - beg);
      while( end<blob_size(ib) && (ib_data[end]=='\n' || ib_data[end]=='\r') ){
      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( ib_data[end]=='\n'
        if( data[end]=='\n' || (end+1<size && data[end+1]!='\n') ){
         || (end+1<blob_size(ib) && ib_data[end+1]!='\n')
        ){
          blob_append(&text, "\n", 1);
          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<end; i++){
  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);
  blobarray_zero(rndr.work, rndr.make.max_work_stack);
  fossil_free(rndr.work);
}
    blob_reset(&fn[i].text);
  }
  blob_reset(&rndr.notes.all);
  for(i=0; i<rndr.nBlobCache; i++){
    fossil_free(rndr.aBlobCache[i]);
  }
}

Changes to src/markdown.md.

24
25
26
27
28
29
30

31
32
33
34
35




36
37
38
39
40
41
42












43





44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62







63
64
65
66
67
68
69




70
71
72
73
74
75
76
77
78
79

80
81





















82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
















































102
103
104
105


106
107
108
109
110

111
112

113
114
115
116
117
118
119
120
121
122



24
25
26
27
28
29
30
31
32




33
34
35
36

37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73





74
75
76
77
78
79
80
81
82
83
84



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99


100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191

192
193
194
195
196
197
198
199
200

201
202
203
204
205
206
207
208
209
210
211
212
213
214







+

-
-
-
-
+
+
+
+
-






+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+














-
-
-
-
-
+
+
+
+
+
+
+




-
-
-
+
+
+
+










+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+




















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



-
+
+





+

-
+










+
+
+
> 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\)**

> **URL** may optionally be written **\<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
> 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.
> indenting.

> * **\[label\]:&nbsp;URL**
> * **\[label\]:&nbsp;URL&nbsp;"Title"**
> * **\[label\]:&nbsp;URL&nbsp;'Title'**
> * **\[label\]:&nbsp;URL&nbsp;(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>
## Fonts ##

> 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
> ~~~
   *   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
       * secondary item
> ~~~
   *  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 ##
> Begin each line with at least four spaces or one tab to produce a verbatim
> code block.

> 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. **\[^**&nbsp;label&nbsp;**\]**
> 4. **\[** fragment of text **\]\[^**&nbsp;label&nbsp;**\]**
> 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.
> *   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 complex examples.
> *   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)

Changes to src/markdown_html.c.

27
28
29
30
31
32
33









34















35
36
37

38
39
40
41
42

43
44
45
46
47
48
49
50
51
































52
53
54
55
56
57
58
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

61
62




63









64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102







+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


-
+

-
-
-
-
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







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(ob, "\n", 1); } while (0)
  do { if( blob_size(ob)>0 ) blob_append_char(ob, '\n'); } while (0)

/* BLOB_APPEND_LITERAL -- append a string literal to a blob */
#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.
   */

/* BLOB_APPEND_BLOB -- append blob contents to another */
#define BLOB_APPEND_BLOB(dest, src) \
  blob_append((dest), blob_buffer(src), blob_size(src))

** 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 &lt;, > to &gt;, and & to &amp;.
** html_quote() goes further and converts " into &quot; and ' in &#39;.
*/
static void html_quote(struct Blob *ob, const char *data, size_t size){
67
68
69
70
71
72
73
74

75
76

77
78

79
80

81
82

83
84
85
86
87
88
89
111
112
113
114
115
116
117

118
119

120
121

122
123

124
125

126
127
128
129
130
131
132
133







-
+

-
+

-
+

-
+

-
+







     && data[i]!='\''
    ){
      i++;
    }
    blob_append(ob, data+beg, i-beg);
    while( i<size ){
      if( data[i]=='<' ){
        BLOB_APPEND_LITERAL(ob, "&lt;");
        blob_append_literal(ob, "&lt;");
      }else if( data[i]=='>' ){
        BLOB_APPEND_LITERAL(ob, "&gt;");
        blob_append_literal(ob, "&gt;");
      }else if( data[i]=='&' ){
        BLOB_APPEND_LITERAL(ob, "&amp;");
        blob_append_literal(ob, "&amp;");
      }else if( data[i]=='"' ){
        BLOB_APPEND_LITERAL(ob, "&quot;");
        blob_append_literal(ob, "&quot;");
      }else if( data[i]=='\'' ){
        BLOB_APPEND_LITERAL(ob, "&#39;");
        blob_append_literal(ob, "&#39;");
      }else{
        break;
      }
      i++;
    }
  }
}
97
98
99
100
101
102
103
104

105
106

107
108

109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125

126
127
128
129
130
131

132
133
134

135
136
137

138
139
140
141
142
143
144
145
146
147

148
149
150
151
152
153

154
155
156
157
158

159
160

161
162
163
164
165
166
167



168
169
170
171
172
173
174
175
176

177
178
179
180

181
182
183
184
185

186
187
188
189
190
191

192
193
194
195
196
197
198
199
200
201
202
203
204
205
206

207
208
209
210
211
212
213
214
215
216
217
218
219

220
221

222
223
224
225
226
227
228



229
230
231
232
233
234
235
236
237
238
239

240
241
242
243



244
245
246

247
248
249

250
251

252
253
254
255
256
257
258
259
260
261

262
263

264
265
266
267

268
269
270
271

272
273
274
275

276
277
278
279
280


281
282

283
284

285
286
287
288
289
290
291
292
293
294
295
296
































































































































































































































297
298








299


300
301
302
303

304
305

306
307
308
309
310
311
312
313
314
315
316
317


318
319

320
321
322
323
324
325
326

327
328
329















































































































330
331
332











333
334


























335
336
337
338
339
340
341
342
343
344
345
346
347



348
349
350
351
352
353
354
355
356
357
358
359



360
361
362
363
364
365
366
367
368
369
370

371
372

373
374
375

376
377
378

379
380
381
382
383


384
385
386
387
388
389
390
391
392
393
394


395
396
397









398
399
400
401
402
403
404




405
406

407
408
409
410
411
412
413
414
415
416
417
418
419
420



421
422
423
424
425
426
427
141
142
143
144
145
146
147

148
149

150
151

152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168

169
170
171
172
173
174

175
176
177

178
179
180

181
182
183
184
185
186
187
188
189
190

191
192
193
194
195
196

197
198
199
200
201

202
203

204
205
206
207
208



209
210
211
212
213
214
215
216
217
218
219

220
221
222
223

224
225
226
227
228

229
230
231
232
233
234

235
236
237
238
239
240
241
242
243
244
245
246
247
248
249

250
251
252
253
254
255
256
257
258
259
260
261
262

263
264

265
266
267
268
269



270
271
272
273
274
275
276
277
278
279
280
281
282

283
284



285
286
287
288
289

290
291
292

293
294

295
296
297
298
299
300
301
302
303
304

305
306

307
308
309
310

311
312
313
314

315
316
317
318

319
320
321
322


323
324
325

326
327

328
329
330
331
332
333
334
335
336
337



338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
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







-
+

-
+

-
+
















-
+





-
+


-
+


-
+









-
+





-
+




-
+

-
+




-
-
-
+
+
+








-
+



-
+




-
+





-
+














-
+












-
+

-
+




-
-
-
+
+
+










-
+

-
-
-
+
+
+


-
+


-
+

-
+









-
+

-
+



-
+



-
+



-
+



-
-
+
+

-
+

-
+









-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


+
+
+
+
+
+
+
+
-
+
+



-
+
-
-
+










-
-
+
+

-
+






-
+



+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+

-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+










-
-
-
+
+
+









-
-
-
+
+
+










-
+

-
+


-
+


-
+



-
-
+
+











+
+
-
-
-
+
+
+
+
+
+
+
+
+
-
-

-
-
-
-
+
+
+
+

-
+
-
-









-
-
-
+
+
+







     && data[i]!='&'
    ){
      i++;
    }
    blob_append(ob, data+beg, i-beg);
    while( i<size ){
      if( data[i]=='<' ){
        BLOB_APPEND_LITERAL(ob, "&lt;");
        blob_append_literal(ob, "&lt;");
      }else if( data[i]=='>' ){
        BLOB_APPEND_LITERAL(ob, "&gt;");
        blob_append_literal(ob, "&gt;");
      }else if( data[i]=='&' ){
        BLOB_APPEND_LITERAL(ob, "&amp;");
        blob_append_literal(ob, "&amp;");
      }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");
  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");
  blob_append_literal(ob, "</div>\n");
}

static void html_raw_block(struct Blob *ob, struct Blob *text, void *opaque){
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 = (Blob*)opaque;
  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 = htmlTagLength(data);
    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");
  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>");
  blob_append_literal(ob, "<pre><code>");
  html_escape(ob, blob_buffer(text), blob_size(text));
  BLOB_APPEND_LITERAL(ob, "</code></pre>\n");
  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_APPEND_BLOB(ob, text);
  BLOB_APPEND_LITERAL(ob, "</blockquote>\n");
  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 = 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_APPEND_BLOB(title, text);
    blob_appendb(title, text);
    return;
  }
  INTER_BLOCK(ob);
  blob_appendf(ob, "<h%d>", level);
  BLOB_APPEND_BLOB(ob, text);
  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");
  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_APPEND_BLOB(ob, text);
  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_literal(ob, "<li>");
  blob_append(ob, text_data, text_size);
  BLOB_APPEND_LITERAL(ob, "</li>\n");
  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_APPEND_BLOB(ob, text);
  BLOB_APPEND_LITERAL(ob, "</p>\n");
  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");
  blob_append_literal(ob, "<table>\n");
  if( head_row && blob_size(head_row)>0 ){
    BLOB_APPEND_LITERAL(ob, "<thead>\n");
    BLOB_APPEND_BLOB(ob, head_row);
    BLOB_APPEND_LITERAL(ob, "</thead>\n<tbody>\n");
    blob_append_literal(ob, "<thead>\n");
    blob_appendb(ob, head_row);
    blob_append_literal(ob, "</thead>\n<tbody>\n");
  }
  if( rows ){
    BLOB_APPEND_BLOB(ob, rows);
    blob_appendb(ob, rows);
  }
  if( head_row && blob_size(head_row)>0 ){
    BLOB_APPEND_LITERAL(ob, "</tbody>\n");
    blob_append_literal(ob, "</tbody>\n");
  }
  BLOB_APPEND_LITERAL(ob, "</table>\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");
    blob_append_literal(ob, "    <th");
  }else{
    BLOB_APPEND_LITERAL(ob, "    <td");
    blob_append_literal(ob, "    <td");
  }
  switch( flags & MKD_CELL_ALIGN_MASK ){
    case MKD_CELL_ALIGN_LEFT: {
      BLOB_APPEND_LITERAL(ob, " align=\"left\"");
      blob_append_literal(ob, " align=\"left\"");
      break;
    }
    case MKD_CELL_ALIGN_RIGHT: {
      BLOB_APPEND_LITERAL(ob, " align=\"right\"");
      blob_append_literal(ob, " align=\"right\"");
      break;
    }
    case MKD_CELL_ALIGN_CENTER: {
      BLOB_APPEND_LITERAL(ob, " align=\"center\"");
      blob_append_literal(ob, " align=\"center\"");
      break;
    }
  }
  BLOB_APPEND_LITERAL(ob, ">");
  BLOB_APPEND_BLOB(ob, text);
  blob_append_literal(ob, ">");
  blob_appendb(ob, text);
  if( flags & MKD_CELL_HEAD ){
    BLOB_APPEND_LITERAL(ob, "</th>\n");
    blob_append_literal(ob, "</th>\n");
  }else{
    BLOB_APPEND_LITERAL(ob, "</td>\n");
    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_APPEND_BLOB(ob, cells);
  BLOB_APPEND_LITERAL(ob, "  </tr>\n");
  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," &hellip;");
    }
    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," &hellip;");
    }
    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[^&nbsp;<code>");
    html_escape(ob, blob_buffer(id), blob_size(id));
    blob_append_literal(ob, "</code>&nbsp;]<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_span(struct Blob *ob, struct Blob *text, void *opaque){
static int html_raw_html_tag(struct Blob *ob, struct Blob *text, void *opaque){
  /* If the document begins with a <h1> markup, take that as the header. */
  BLOB_APPEND_BLOB(ob, text);
  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:");
  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, "\">");
  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>");
  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_code_span(struct Blob *ob, struct Blob *text, void *opaque){
  if( text ){
    BLOB_APPEND_LITERAL(ob, "<code>");
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>");
    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_APPEND_BLOB(ob, text);
  BLOB_APPEND_LITERAL(ob, "</strong>");
  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_APPEND_BLOB(ob, text);
  BLOB_APPEND_LITERAL(ob, "</em>");
  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=\"");
  blob_append_literal(ob, "<img src=\"");
  html_quote(ob, blob_buffer(link), blob_size(link));
  BLOB_APPEND_LITERAL(ob, "\" alt=\"");
  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=\"");
    blob_append_literal(ob, "\" title=\"");
    html_quote(ob, blob_buffer(title), blob_size(title));
  }
  BLOB_APPEND_LITERAL(ob, "\" />");
  blob_append_literal(ob, "\">");
  return 1;
}

static int html_line_break(struct Blob *ob, void *opaque){
  BLOB_APPEND_LITERAL(ob, "<br />\n");
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];
  BLOB_APPEND_LITERAL(ob, "<a href=\"");
  if( zLink && zLink[0]=='/' && g.zTop ){
    /* For any hyperlink that begins with "/", make it refer to the root

  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);
    ** of the Fossil repository */
    blob_append(ob, g.zTop, -1);
  }
  html_quote(ob, blob_buffer(link), blob_size(link));
  if( title && blob_size(title)>0 ){
    BLOB_APPEND_LITERAL(ob, "\" title=\"");
    html_quote(ob, blob_buffer(title), blob_size(title));
  if( blob_size(content)==0 ){
    if( link ) blob_appendb(ob, link);
  }else{
    blob_appendb(ob, content);
  }
  BLOB_APPEND_LITERAL(ob, "\">");
  blob_append(ob, zClose, -1);
  BLOB_APPEND_BLOB(ob, content);
  BLOB_APPEND_LITERAL(ob, "</a>");
  return 1;
}

static int html_triple_emphasis(
  struct Blob *ob,
  struct Blob *text,
  char c,
  void *opaque
){
  BLOB_APPEND_LITERAL(ob, "<strong><em>");
  BLOB_APPEND_BLOB(ob, text);
  BLOB_APPEND_LITERAL(ob, "</em></strong>");
  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));
}
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
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







+




-
+








+



-
+



-
+

-
+

+


-
+



-
-
-
+
+

+
+
+
+
+
+
+
+
+
+
-
+




  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_raw_block,
    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_code_span,
    html_codespan,
    html_double_emphasis,
    html_emphasis,
    html_image,
    html_line_break,
    html_linebreak,
    html_link,
    html_raw_span,
    html_raw_html_tag,
    html_triple_emphasis,
    html_footnote_ref,

    /* low level elements */
    0,  /* entities are copied verbatim */
    0,    /* entity */
    html_normal_text,

    /* misc. parameters */
    64, /* maximum stack */
    "*_", /* emphasis characters */
    0 /* opaque data */
    "*_", /* 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 = output_title;
  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.

20
21
22
23
24
25
26
27




28
29
30
31
32
33
34
20
21
22
23
24
25
26

27
28
29
30
31
32
33
34
35
36
37







-
+
+
+
+







 */
#include "config.h"
#include <string.h>
#include <stdio.h>
#include <sqlite3.h>
#include "md5.h"

#ifdef FOSSIL_ENABLE_SSL
#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

298
299
300
301
302
303
304
305

306
307
308
309
310
311
312
301
302
303
304
305
306
307

308
309
310
311
312
313
314
315







-
+







    zBuf[j++] = zEncode[(a>>4)&0xf];
    zBuf[j++] = zEncode[a & 0xf];
  }
  zBuf[j] = 0;
}

/*
** The state of a incremental MD5 checksum computation.  Only one
** 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.

Changes to src/merge.c.

45
46
47
48
49
50
51
52

53
54
55
56
57
58
59
45
46
47
48
49
50
51

52
53
54
55
56
57
58
59







-
+







    }
    fossil_print("%-*s [%S] by %s on %s\n%*s",
       indent-1, zLabel,
       db_column_text(&q, 3),
       db_column_text(&q, 1),
       db_column_text(&q, 0),
       indent, "");
    comment_print(zCom, db_column_text(&q,2), indent, -1, g.comFmtFlags);
    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)
133
134
135
136
137
138
139
140
141
142



143
144
145
146
147
148

149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169










170

































































































171

172
173
174
175
176
177
178



179
180
181





182
183
184
185
186
187
188
189
190

191



192
193
194
195
196
197
198
199
200
201
202
203




204
205

206
207

208
209
210




211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229


230
231
232
233
234
235
236
237
238

239
240
241
242
243
244
245
246
247
248
249
250



251
252
253
254
255
256
257
258
259
260


















261
262
263
264
265
266
267



268
269

270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286

287
288

289
290
291
292
293
294
295
133
134
135
136
137
138
139



140
141
142
143
144
145
146
147

148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283



284
285
286
287
288

289
290
291
292
293
294
295
296
297





298

299
300
301
302
303
304
305

306
307
308




309
310
311
312


313


314



315
316
317
318
319

320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346

347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364

365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400

401

402
403
404
405
406
407
408
409
410
411
412
413
414
415
416

417
418

419
420
421
422
423
424
425
426







-
-
-
+
+
+





-
+





















+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+




-
-
-
+
+
+


-
+
+
+
+
+




-
-
-
-
-
+
-
+
+
+




-



-
-
-
-
+
+
+
+
-
-
+
-
-
+
-
-
-
+
+
+
+

-

















+
+








-
+












+
+
+


-







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







+
+
+

-
+
-















-
+

-
+








/*
** 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,            /* Version N's RID */
  int revOk,          /* Ok to move backwards (child->parent) if true */
  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);
  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 checkout.  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
** 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
** checkout rather than added.
** 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.
**
** Only file content is merged.  The result continues to use the
** file and directory names from the current checkout even if those
** names might have been changed in the branch being merged in.
**
** Other options:
** 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.
**
**   --case-sensitive BOOL   Override the case-sensitive setting.  If false,
**                           files whose names differ only in case are taken
**                           to be the same file.
**   --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.
**   -f|--force              Force the merge even if it would be a no-op
**
**   --force-missing         Force the merge even if there is missing content.
**   --force-missing         Force the merge even if there is missing content
**
**   --integrate             Merged branch will be closed when committing.
**
**   --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 checkout
  **      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;
  debugFlag = find_option("debug",0,0)!=0;
  zBinGlob = find_option("binary",0,1);
  dryRunFlag = find_option("dry-run","n",0)!=0;
  if( !dryRunFlag ){
    dryRunFlag = find_option("nochange",0,0)!=0; /* deprecated */
  }
  forceFlag = find_option("force","f",0)!=0;
  zPivot = find_option("baseline",0,1);
  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,
    if( autosync_loop(SYNC_PULL + SYNC_VERBOSE*verboseFlag, 1, "merge") ){
                      db_get_int("autosync-tries", 1), 1) ){
      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 checkout and (3)
    ** 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 checkout.
    ** 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));
309
310
311
312
313
314
315
316

317
318
319
320
321
322
323
440
441
442
443
444
445
446

447
448
449
450
451
452
453
454







-
+







      " 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, g.comFmtFlags);
      comment_print(zCom, db_column_text(&q,2), 0, -1, get_comment_format());
      fossil_free(zCom);
    }
    db_finalize(&q);
  }else{
    usage("?OPTIONS? ?VERSION?");
    return;
  }
348
349
350
351
352
353
354
355

356
357
358
359
360
361
362
479
480
481
482
483
484
485

486
487
488
489
490
491
492
493







-
+







      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 "
                     "checkout and %s", g.argv[2]);
                     "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);
377
378
379
380
381
382
383







384
385
386
387
388
389
390
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528







+
+
+
+
+
+
+







    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);
406
407
408
409
410
411
412
413

414
415

416
417

418
419


420

421
422
423
424
425

426
427
428
429
430
431
432
433
434
435
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
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







-
+

-
+

-
+

-
+
+

+




-
+



-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+











+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+














+
+
+
+












+
+
+
+










+
+
+
+










-
-
-
-
+
+
+
+




-
-
-
-
+
+
+
+
+
+
-
-
-
-
-
+
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-







      "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=%d %z\n", nid, z);
    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=%d %z\n", pid, z);
    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=%d %z\n", mid, z);
    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=%d %z\n", vid, z);
    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 checkout, the pivot, and the version being merged.
  ** 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 UNIQUE %s,"        /* The filename */
    "  idv INTEGER DEFAULT 0,"    /* VFILE entry for current version */
    "  idp INTEGER DEFAULT 0,"    /* VFILE entry for the pivot */
    "  idm INTEGER DEFAULT 0,"    /* VFILE entry for version merging in */
    "  chnged BOOLEAN,"           /* True if current version has been edited */
    "  ridv INTEGER DEFAULT 0,"   /* Record ID for current version */
    "  ridp INTEGER DEFAULT 0,"   /* Record ID for pivot */
    "  ridm INTEGER DEFAULT 0,"   /* Record ID for merge */
    "  isexe BOOLEAN,"            /* Execute permission enabled */
    "  fnp TEXT UNIQUE %s,"       /* The filename in the pivot */
    "  fnm TEXT UNIQUE %s,"       /* The filename in the merged version */
    "  fnn TEXT UNIQUE %s,"       /* The filename in the name pivot */
    "  islinkv BOOLEAN,"          /* True if current version is a symlink */
    "  islinkm BOOLEAN"           /* True if merged version in is a symlink */
    "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((SELECT id FROM vfile WHERE vid=%d AND fnp=pathname),0),"
      " ridp=coalesce((SELECT rid FROM vfile WHERE vid=%d AND fnp=pathname),0)",
      pid, pid
      "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((SELECT id FROM vfile WHERE vid=%d AND fnm=pathname),0),"
    " ridm=coalesce((SELECT rid FROM vfile WHERE vid=%d AND fnm=pathname),0),"
    " islinkm=coalesce((SELECT islink FROM vfile"
                    " WHERE vid=%d AND fnm=pathname),0),"
    " 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",
    " isexe=coalesce((SELECT isexe FROM vfile WHERE vid=%d AND fnm=pathname),"
    "   isexe)",
    mid, mid, mid, mid
  );

    mid
  if( debugFlag ){
    db_prepare(&q,
       "SELECT rowid, fn, fnp, fnm, chnged, ridv, ridp, ridm, "
       "       isexe, islinkv, islinkm, fnn FROM fv"
    );
  );
    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);
  }

  /*
  ** 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"
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
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







-
+
-
-
-
-
+
-
-
-
-
+
-
-
-
-
-
-

-

+
+
+
-
-
+
+

















-
-
+
+
+
+








+
+
+
+



-
+







      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 ){
  /*
  ** Find files in M and V but not in P and report conflicts.
  ** The file in M will be ignored.  It will be treated as if it
  ** does not exist.
    fossil_print("******** FV final *******\n");
  */
  db_prepare(&q,
    "SELECT idm FROM fv WHERE idp=0 AND idv>0 AND idm>0"
  );
    debug_fv_dump( debugFlag>=2 );
  while( db_step(&q)==SQLITE_ROW ){
    int idm = db_column_int(&q, 0);
    char *zName = db_text(0, "SELECT pathname FROM vfile WHERE id=%d", idm);
    fossil_warning("WARNING: no common ancestor for %s", zName);
    free(zName);
    db_multi_exec("UPDATE fv SET idm=0 WHERE idm=%d", idm);
  }
  db_finalize(&q);

  /************************************************************************
  ** 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.
  /*
  ** Find files that have changed from P->M but not P->V.
  **
  ** 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 "
        " WHERE id=%d", ridm, integrateFlag?4:2, islinkm, idv
        "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 idp>0 AND idv>0 AND idm>0"
    " 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);
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
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







+









-
+
+










-
-
+







      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 conflicts in %s\n", rc, zName);
          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);
    }
    db_multi_exec("INSERT OR IGNORE INTO vmerge(id,merge) VALUES(%d,%d)",
                  idv,ridm);
    vmerge_insert(idv, ridm);
  }
  db_finalize(&q);

  /*
  ** Drop files that are in P and V but not in M
  */
  db_prepare(&q,
739
740
741
742
743
744
745
746

747
748
749
750
751
752
753
931
932
933
934
935
936
937

938
939
940
941
942
943
944
945







-
+







      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, "");
        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));
        }
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
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







-
+










-
-
+
+
+
+
+
+






+
+
+
+
+
-
+
+
+







  */
  db_multi_exec(
    "UPDATE vfile SET pathname=origname || ' (overwritten by rename)'"
    " WHERE pathname IS NULL"
  );

  /*
  ** Add to V files that are not in V or P but are in M
  ** 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)"
      "  SELECT %d,%d,0,rid,mrid,isexe,islink,pathname FROM vfile WHERE id=%d",
      "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)\n", zName);
      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);
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
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







-
+









-
+

-
+

-
+




  }

  /*
  ** Clean up the mid and pid VFILE entries.  Then commit the changes.
  */
  db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid);
  if( pickFlag ){
    db_multi_exec("INSERT OR IGNORE INTO vmerge(id,merge) VALUES(-1,%d)",mid);
    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 ){
    db_multi_exec("INSERT OR IGNORE INTO vmerge(id,merge) VALUES(-2,%d)",pid);
    vmerge_insert(-2, pid);
  }else if( integrateFlag ){
    db_multi_exec("INSERT OR IGNORE INTO vmerge(id,merge) VALUES(-4,%d)",mid);
    vmerge_insert(-4, mid);
  }else{
    db_multi_exec("INSERT OR IGNORE INTO vmerge(id,merge) VALUES(0,%d)", mid);
    vmerge_insert(0, mid);
  }
  if( !dryRunFlag ) undo_finish();
  db_end_transaction(dryRunFlag);
}

Changes to src/merge3.c.

114
115
116
117
118
119
120
121


122
123
124
125
126

127
128
129
130
131


132
133
134
135
136
137
138
139
140
141
142
143
144
145
146




147
148









































149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168



169
170

















171
172
173
174
175
176
177
178
179

180
181


182
183
184
185
186
187
188
114
115
116
117
118
119
120

121
122
123
124
125
126

127
128
129
130


131
132
133
134
135
136
137
138
139
140
141
142
143




144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
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 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);
      blob_copy_lines(pOut, pSrc, sz);  *pLn += sz;
      aC[i] -= sz;
      break;
    }
    blob_copy_lines(pOut, pSrc, aC[i]);
    blob_copy_lines(pOut, pSrc, aC[i+2]);
    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 <<<<<<<<<<<<<<<\n",
  "======= COMMON ANCESTOR content follows ============================\n",
  "======= MERGED IN content follows ==================================\n",
  ">>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"
  "<<<<<<< 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, 0, 0);
  aC2 = text_diff(pPivot, pV2, 0, 0, 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 */
206
207
208
209
210
211
212

213
214
215
216
217
218
219
220
221
222
223
224



225
226
227
228
229
230
231
232
233
234
235



236
237
238
239
240
241
242
243
244
245
246



247
248
249
250
251
252
253
254
255
256
257
258



259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279











280
281
282
283
284
285
286
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285



286
287
288
289
290
291
292
293
294
295
296



297
298
299
300
301
302
303
304
305
306
307



308
309
310
311
312
313
314
315
316
317
318
319



320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336







337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354







+









-
-
-
+
+
+








-
-
-
+
+
+








-
-
-
+
+
+









-
-
-
+
+
+














-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+








  /* 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);
      blob_copy_lines(0, pV1, nCpy);
      blob_copy_lines(0, pV2, 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);
      blob_copy_lines(0, pV1, nDel);
      blob_copy_lines(pOut, pV2, 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);
      blob_copy_lines(0, pV2, nDel);
      blob_copy_lines(pOut, pV1, 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);
      blob_copy_lines(pOut, pV1, nIns);
      blob_copy_lines(0, pV2, 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); )
      blob_append(pOut, mergeMarker[0], -1);
      i1 = output_one_side(pOut, pV1, aC1, i1, sz);
      blob_append(pOut, mergeMarker[1], -1);
      blob_copy_lines(pOut, pPivot, sz);
      blob_append(pOut, mergeMarker[2], -1);
      i2 = output_one_side(pOut, pV2, aC2, i2, sz);
      blob_append(pOut, mergeMarker[3], -1);

      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;
317
318
319
320
321
322
323
324
325




326
327

328
329
330
331
332
333
334
385
386
387
388
389
390
391


392
393
394
395
396

397
398
399
400
401
402
403
404







-
-
+
+
+
+

-
+







  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;
    }
      if( (memcmp(&z[i], mergeMarker[j], len)==0) ){
        return 1;
      }
    }
    while( i<n && z[i]!='\n' ){ i++; }
    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.
*/
384
385
386
387
388
389
390
391

392
393
394
395
396
397
398
454
455
456
457
458
459
460

461
462
463
464
465
466
467
468







-
+







  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])<blob_size(&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);
434
435
436
437
438
439
440






441
442
443
444
445
446
447
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523







+
+
+
+
+
+







}

#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:
**
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
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







+



+
-
+
+
+











-
-
-




-










-
-
-





+
+
+
+
+








  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( rc!=0 && (mergeFlags & MERGE_DRYRUN)==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 ){
      const char *zGMerge;   /* Name of the gmerge command */

      zGMerge = db_get("gmerge-command", 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(zPivot);
          file_delete(zOrig);
          file_delete(zOther);
          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/miniz.c.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
3799
3800
3801
3802
3803
3804
3805
3806
3807
3808
3809
3810
3811
3812
3813
3814
3815
3816
3817
3818
3819
3820
3821
3822
3823
3824
3825
3826
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
3841
3842
3843
3844
3845
3846
3847
3848
3849
3850
3851
3852
3853
3854
3855
3856
3857
3858
3859
3860
3861
3862
3863
3864
3865
3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
3881
3882
3883
3884
3885
3886
3887
3888
3889
3890
3891
3892
3893
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
3909
3910
3911
3912
3913
3914
3915
3916
3917
3918
3919
3920
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
3986
3987
3988
3989
3990
3991
3992
3993
3994
3995
3996
3997
3998
3999
4000
4001
4002
4003
4004
4005
4006
4007
4008
4009
4010
4011
4012
4013
4014
4015
4016
4017
4018
4019
4020
4021
4022
4023
4024
4025
4026
4027
4028
4029
4030
4031
4032
4033
4034
4035
4036
4037
4038
4039
4040
4041
4042
4043
4044
4045
4046
4047
4048
4049
4050
4051
4052
4053
4054
4055
4056
4057
4058
4059
4060
4061
4062
4063
4064
4065
4066
4067
4068
4069
4070
4071
4072
4073
4074
4075
4076
4077
4078
4079
4080
4081
4082
4083
4084
4085
4086
4087
4088
4089
4090
4091
4092
4093
4094
4095
4096
4097
4098
4099
4100
4101
4102
4103
4104
4105
4106
4107
4108
4109
4110
4111
4112
4113
4114
4115
4116
4117
4118
4119
4120
4121
4122
4123
4124
4125
4126
4127
4128
4129
4130
4131
4132
4133
4134
4135
4136
4137
4138
4139
4140
4141
4142
4143
4144
4145
4146
4147
4148
4149
4150
4151
4152
4153
4154
4155
4156
4157
4158
4159
4160
4161
4162
4163
4164
4165
4166
4167
4168
4169
4170
4171
4172
4173
4174
4175
4176
4177
4178
4179
4180
4181
4182
4183
4184
4185
4186
4187
4188
4189
4190
4191
4192
4193
4194
4195
4196
4197
4198
4199
4200
4201
4202
4203
4204
4205
4206
4207
4208
4209
4210
4211
4212
4213
4214
4215
4216
4217
4218
4219
4220
4221
4222
4223
4224
4225
4226
4227
4228
4229
4230
4231
4232
4233
4234
4235
4236
4237
4238
4239
4240
4241
4242
4243
4244
4245
4246
4247
4248
4249
4250
4251
4252
4253
4254
4255
4256
4257
4258
4259
4260
4261
4262
4263
4264
4265
4266
4267
4268
4269
4270
4271
4272
4273
4274
4275
4276
4277
4278
4279
4280
4281
4282
4283
4284
4285
4286
4287
4288
4289
4290
4291
4292
4293
4294
4295
4296
4297
4298
4299
4300
4301
4302
4303
4304
4305
4306
4307
4308
4309
4310
4311
4312
4313
4314
4315
4316
4317
4318
4319
4320
4321
4322
4323
4324
4325
4326
4327
4328
4329
4330
4331
4332
4333
4334
4335
4336
4337
4338
4339
4340
4341
4342
4343
4344
4345
4346
4347
4348
4349
4350
4351
4352
4353
4354
4355
4356
4357
4358
4359
4360
4361
4362
4363
4364
4365
4366
4367
4368
4369
4370
4371
4372
4373
4374
4375
4376
4377
4378
4379
4380
4381
4382
4383
4384
4385
4386
4387
4388
4389
4390
4391
4392
4393
4394
4395
4396
4397
4398
4399
4400
4401
4402
4403
4404
4405
4406
4407
4408
4409
4410
4411
4412
4413
4414
4415
4416
4417
4418
4419
4420
4421
4422
4423
4424
4425
4426
4427
4428
4429
4430
4431
4432
4433
4434
4435
4436
4437
4438
4439
4440
4441
4442
4443
4444
4445
4446
4447
4448
4449
4450
4451
4452
4453
4454
4455
4456
4457
4458
4459
4460
4461
4462
4463
4464
4465
4466
4467
4468
4469
4470
4471
4472
4473
4474
4475
4476
4477
4478
4479
4480
4481
4482
4483
4484
4485
4486
4487
4488
4489
4490
4491
4492
4493
4494
4495
4496
4497
4498
4499
4500
4501
4502
4503
4504
4505
4506
4507
4508
4509
4510
4511
4512
4513
4514
4515
4516
4517
4518
4519
4520
4521
4522
4523
4524
4525
4526
4527
4528
4529
4530
4531
4532
4533
4534
4535
4536
4537
4538
4539
4540
4541
4542
4543
4544
4545
4546
4547
4548
4549
4550
4551
4552
4553
4554
4555
4556
4557
4558
4559
4560
4561
4562
4563
4564
4565
4566
4567
4568
4569
4570
4571
4572
4573
4574
4575
4576
4577
4578
4579
4580
4581
4582
4583
4584
4585
4586
4587
4588
4589
4590
4591
4592
4593
4594
4595
4596
4597
4598
4599
4600
4601
4602
4603
4604
4605
4606
4607
4608
4609
4610
4611
4612
4613
4614
4615
4616
4617
4618
4619
4620
4621
4622
4623
4624
4625
4626
4627
4628
4629
4630
4631
4632
4633
4634
4635
4636
4637
4638
4639
4640
4641
4642
4643
4644
4645
4646
4647
4648
4649
4650
4651
4652
4653
4654
4655
4656
4657
4658
4659
4660
4661
4662
4663
4664
4665
4666
4667
4668
4669
4670
4671
4672
4673
4674
4675
4676
4677
4678
4679
4680
4681
4682
4683
4684
4685
4686
4687
4688
4689
4690
4691
4692
4693
4694
4695
4696
4697
4698
4699
4700
4701
4702
4703
4704
4705
4706
4707
4708
4709
4710
4711
4712
4713
4714
4715
4716
4717
4718
4719
4720
4721
4722
4723
4724
4725
4726
4727
4728
4729
4730
4731
4732
4733
4734
4735
4736
4737
4738
4739
4740
4741
4742
4743
4744
4745
4746
4747
4748
4749
4750
4751
4752
4753
4754
4755
4756
4757
4758
4759
4760
4761
4762
4763
4764
4765
4766
4767
4768
4769
4770
4771
4772
4773
4774
4775
4776
4777
4778
4779
4780
4781
4782
4783
4784
4785
4786
4787
4788
4789
4790
4791
4792
4793
4794
4795
4796
4797
4798
4799
4800
4801
4802
4803
4804
4805
4806
4807
4808
4809
4810
4811
4812
4813
4814
4815
4816
4817
4818
4819
4820
4821
4822
4823
4824
4825
4826
4827
4828
4829
4830
4831
4832
4833
4834
4835
4836
4837
4838
4839
4840
4841
4842
4843
4844
4845
4846
4847
4848
4849
4850
4851
4852
4853
4854
4855
4856
4857
4858
4859
4860
4861
4862
4863
4864
4865
4866
4867
4868
4869
4870
4871
4872
4873
4874
4875
4876
4877
4878
4879
4880
4881
4882
4883
4884
4885
4886
4887
4888
4889
4890
4891
4892
4893
4894
4895
4896
4897
4898
4899
4900
4901
4902
4903
4904
4905
4906
4907
4908
4909
4910
4911
4912
4913
4914
4915
4916




















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
/* miniz.c v1.15 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing
   See "unlicense" statement at the end of this file.
   Rich Geldreich <richgel99@gmail.com>, last updated Oct. 13, 2013
   Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt

   Most API's defined in miniz.c are optional. For example, to disable the archive related functions just define
   MINIZ_NO_ARCHIVE_APIS, or to get rid of all stdio usage define MINIZ_NO_STDIO (see the list below for more macros).

   * Change History
     10/13/13 v1.15 r4 - Interim bugfix release while I work on the next major release with Zip64 support (almost there!):
       - Critical fix for the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY bug (thanks kahmyong.moon@hp.com) which could cause locate files to not find files. This bug
        would only have occured in earlier versions if you explicitly used this flag, OR if you used mz_zip_extract_archive_file_to_heap() or mz_zip_add_mem_to_archive_file_in_place()
        (which used this flag). If you can't switch to v1.15 but want to fix this bug, just remove the uses of this flag from both helper funcs (and of course don't use the flag).
       - Bugfix in mz_zip_reader_extract_to_mem_no_alloc() from kymoon when pUser_read_buf is not NULL and compressed size is > uncompressed size
       - Fixing mz_zip_reader_extract_*() funcs so they don't try to extract compressed data from directory entries, to account for weird zipfiles which contain zero-size compressed data on dir entries.
         Hopefully this fix won't cause any issues on weird zip archives, because it assumes the low 16-bits of zip external attributes are DOS attributes (which I believe they always are in practice).
       - Fixing mz_zip_reader_is_file_a_directory() so it doesn't check the internal attributes, just the filename and external attributes
       - mz_zip_reader_init_file() - missing MZ_FCLOSE() call if the seek failed
       - Added cmake support for Linux builds which builds all the examples, tested with clang v3.3 and gcc v4.6.
       - Clang fix for tdefl_write_image_to_png_file_in_memory() from toffaletti
       - Merged MZ_FORCEINLINE fix from hdeanclark
       - Fix <time.h> include before config #ifdef, thanks emil.brink
       - Added tdefl_write_image_to_png_file_in_memory_ex(): supports Y flipping (super useful for OpenGL apps), and explicit control over the compression level (so you can
        set it to 1 for real-time compression).
       - Merged in some compiler fixes from paulharris's github repro.
       - Retested this build under Windows (VS 2010, including static analysis), tcc  0.9.26, gcc v4.6 and clang v3.3.
       - Added example6.c, which dumps an image of the mandelbrot set to a PNG file.
       - Modified example2 to help test the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY flag more.
       - In r3: Bugfix to mz_zip_writer_add_file() found during merge: Fix possible src file fclose() leak if alignment bytes+local header file write faiiled
       - In r4: Minor bugfix to mz_zip_writer_add_from_zip_reader(): Was pushing the wrong central dir header offset, appears harmless in this release, but it became a problem in the zip64 branch
     5/20/12 v1.14 - MinGW32/64 GCC 4.6.1 compiler fixes: added MZ_FORCEINLINE, #include <time.h> (thanks fermtect).
     5/19/12 v1.13 - From jason@cornsyrup.org and kelwert@mtu.edu - Fix mz_crc32() so it doesn't compute the wrong CRC-32's when mz_ulong is 64-bit.
       - Temporarily/locally slammed in "typedef unsigned long mz_ulong" and re-ran a randomized regression test on ~500k files.
       - Eliminated a bunch of warnings when compiling with GCC 32-bit/64.
       - Ran all examples, miniz.c, and tinfl.c through MSVC 2008's /analyze (static analysis) option and fixed all warnings (except for the silly
        "Use of the comma-operator in a tested expression.." analysis warning, which I purposely use to work around a MSVC compiler warning).
       - Created 32-bit and 64-bit Codeblocks projects/workspace. Built and tested Linux executables. The codeblocks workspace is compatible with Linux+Win32/x64.
       - Added miniz_tester solution/project, which is a useful little app derived from LZHAM's tester app that I use as part of the regression test.
       - Ran miniz.c and tinfl.c through another series of regression testing on ~500,000 files and archives.
       - Modified example5.c so it purposely disables a bunch of high-level functionality (MINIZ_NO_STDIO, etc.). (Thanks to corysama for the MINIZ_NO_STDIO bug report.)
       - Fix ftell() usage in examples so they exit with an error on files which are too large (a limitation of the examples, not miniz itself).
     4/12/12 v1.12 - More comments, added low-level example5.c, fixed a couple minor level_and_flags issues in the archive API's.
      level_and_flags can now be set to MZ_DEFAULT_COMPRESSION. Thanks to Bruce Dawson <bruced@valvesoftware.com> for the feedback/bug report.
     5/28/11 v1.11 - Added statement from unlicense.org
     5/27/11 v1.10 - Substantial compressor optimizations:
      - Level 1 is now ~4x faster than before. The L1 compressor's throughput now varies between 70-110MB/sec. on a
      - Core i7 (actual throughput varies depending on the type of data, and x64 vs. x86).
      - Improved baseline L2-L9 compression perf. Also, greatly improved compression perf. issues on some file types.
      - Refactored the compression code for better readability and maintainability.
      - Added level 10 compression level (L10 has slightly better ratio than level 9, but could have a potentially large
       drop in throughput on some files).
     5/15/11 v1.09 - Initial stable release.

   * Low-level Deflate/Inflate implementation notes:

     Compression: Use the "tdefl" API's. The compressor supports raw, static, and dynamic blocks, lazy or
     greedy parsing, match length filtering, RLE-only, and Huffman-only streams. It performs and compresses
     approximately as well as zlib.

     Decompression: Use the "tinfl" API's. The entire decompressor is implemented as a single function
     coroutine: see tinfl_decompress(). It supports decompression into a 32KB (or larger power of 2) wrapping buffer, or into a memory
     block large enough to hold the entire file.

     The low-level tdefl/tinfl API's do not make any use of dynamic memory allocation.

   * zlib-style API notes:

     miniz.c implements a fairly large subset of zlib. There's enough functionality present for it to be a drop-in
     zlib replacement in many apps:
        The z_stream struct, optional memory allocation callbacks
        deflateInit/deflateInit2/deflate/deflateReset/deflateEnd/deflateBound
        inflateInit/inflateInit2/inflate/inflateEnd
        compress, compress2, compressBound, uncompress
        CRC-32, Adler-32 - Using modern, minimal code size, CPU cache friendly routines.
        Supports raw deflate streams or standard zlib streams with adler-32 checking.

     Limitations:
      The callback API's are not implemented yet. No support for gzip headers or zlib static dictionaries.
      I've tried to closely emulate zlib's various flavors of stream flushing and return status codes, but
      there are no guarantees that miniz.c pulls this off perfectly.

   * PNG writing: See the tdefl_write_image_to_png_file_in_memory() function, originally written by
     Alex Evans. Supports 1-4 bytes/pixel images.

   * ZIP archive API notes:

     The ZIP archive API's where designed with simplicity and efficiency in mind, with just enough abstraction to
     get the job done with minimal fuss. There are simple API's to retrieve file information, read files from
     existing archives, create new archives, append new files to existing archives, or clone archive data from
     one archive to another. It supports archives located in memory or the heap, on disk (using stdio.h),
     or you can specify custom file read/write callbacks.

     - Archive reading: Just call this function to read a single file from a disk archive:

      void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name,
        size_t *pSize, mz_uint zip_flags);

     For more complex cases, use the "mz_zip_reader" functions. Upon opening an archive, the entire central
     directory is located and read as-is into memory, and subsequent file access only occurs when reading individual files.

     - Archives file scanning: The simple way is to use this function to scan a loaded archive for a specific file:

     int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags);

     The locate operation can optionally check file comments too, which (as one example) can be used to identify
     multiple versions of the same file in an archive. This function uses a simple linear search through the central
     directory, so it's not very fast.

     Alternately, you can iterate through all the files in an archive (using mz_zip_reader_get_num_files()) and
     retrieve detailed info on each file by calling mz_zip_reader_file_stat().

     - Archive creation: Use the "mz_zip_writer" functions. The ZIP writer immediately writes compressed file data
     to disk and builds an exact image of the central directory in memory. The central directory image is written
     all at once at the end of the archive file when the archive is finalized.

     The archive writer can optionally align each file's local header and file data to any power of 2 alignment,
     which can be useful when the archive will be read from optical media. Also, the writer supports placing
     arbitrary data blobs at the very beginning of ZIP archives. Archives written using either feature are still
     readable by any ZIP tool.

     - Archive appending: The simple way to add a single file to an archive is to call this function:

      mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name,
        const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags);

     The archive will be created if it doesn't already exist, otherwise it'll be appended to.
     Note the appending is done in-place and is not an atomic operation, so if something goes wrong
     during the operation it's possible the archive could be left without a central directory (although the local
     file headers and file data will be fine, so the archive will be recoverable).

     For more complex archive modification scenarios:
     1. The safest way is to use a mz_zip_reader to read the existing archive, cloning only those bits you want to
     preserve into a new archive using using the mz_zip_writer_add_from_zip_reader() function (which compiles the
     compressed file data as-is). When you're done, delete the old archive and rename the newly written archive, and
     you're done. This is safe but requires a bunch of temporary disk space or heap memory.

     2. Or, you can convert an mz_zip_reader in-place to an mz_zip_writer using mz_zip_writer_init_from_reader(),
     append new files as needed, then finalize the archive which will write an updated central directory to the
     original archive. (This is basically what mz_zip_add_mem_to_archive_file_in_place() does.) There's a
     possibility that the archive's central directory could be lost with this method if anything goes wrong, though.

     - ZIP archive support limitations:
     No zip64 or spanning support. Extraction functions can only handle unencrypted, stored or deflated files.
     Requires streams capable of seeking.

   * This is a header file library, like stb_image.c. To get only a header file, either cut and paste the
     below header, or create miniz.h, #define MINIZ_HEADER_FILE_ONLY, and then include miniz.c from it.

   * Important: For best perf. be sure to customize the below macros for your target platform:
     #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1
     #define MINIZ_LITTLE_ENDIAN 1
     #define MINIZ_HAS_64BIT_REGISTERS 1

   * On platforms using glibc, Be sure to "#define _LARGEFILE64_SOURCE 1" before including miniz.c to ensure miniz
     uses the 64-bit variants: fopen64(), stat64(), etc. Otherwise you won't be able to process large files
     (i.e. 32-bit stat() fails for me on files > 0x7FFFFFFF bytes).
*/

#ifndef MINIZ_HEADER_INCLUDED
#define MINIZ_HEADER_INCLUDED

#include <stdlib.h>

// Defines to completely disable specific portions of miniz.c:
// If all macros here are defined the only functionality remaining will be CRC-32, adler-32, tinfl, and tdefl.

// Define MINIZ_NO_STDIO to disable all usage and any functions which rely on stdio for file I/O.
//#define MINIZ_NO_STDIO

// If MINIZ_NO_TIME is specified then the ZIP archive functions will not be able to get the current time, or
// get/set file times, and the C run-time funcs that get/set times won't be called.
// The current downside is the times written to your archives will be from 1979.
//#define MINIZ_NO_TIME

// Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's.
//#define MINIZ_NO_ARCHIVE_APIS

// Define MINIZ_NO_ARCHIVE_APIS to disable all writing related ZIP archive API's.
//#define MINIZ_NO_ARCHIVE_WRITING_APIS

// Define MINIZ_NO_ZLIB_APIS to remove all ZLIB-style compression/decompression API's.
//#define MINIZ_NO_ZLIB_APIS

// Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent conflicts against stock zlib.
//#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES

// Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc.
// Note if MINIZ_NO_MALLOC is defined then the user must always provide custom user alloc/free/realloc
// callbacks to the zlib and archive API's, and a few stand-alone helper API's which don't provide custom user
// functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work.
//#define MINIZ_NO_MALLOC

#if defined(__TINYC__) && (defined(__linux) || defined(__linux__))
  // TODO: Work around "error: include file 'sys\utime.h' when compiling with tcc on Linux
  #define MINIZ_NO_TIME
#endif

#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_ARCHIVE_APIS)
  #include <time.h>
#endif

#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__i386) || defined(__i486__) || defined(__i486) || defined(i386) || defined(__ia64__) || defined(__x86_64__)
// MINIZ_X86_OR_X64_CPU is only used to help set the below macros.
#define MINIZ_X86_OR_X64_CPU 1
#endif

#if (__BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU
// Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian.
#define MINIZ_LITTLE_ENDIAN 1
#endif

#if MINIZ_X86_OR_X64_CPU
// Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses.
#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1
#endif

#if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) || defined(__ia64__) || defined(__x86_64__)
// Set MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are reasonably fast (and don't involve compiler generated calls to helper functions).
#define MINIZ_HAS_64BIT_REGISTERS 1
#endif

#ifdef __cplusplus
extern "C" {
#endif

// ------------------- zlib-style API Definitions.

// For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. Beware: mz_ulong can be either 32 or 64-bits!
typedef unsigned long mz_ulong;

// mz_free() internally uses the MZ_FREE() macro (which by default calls free() unless you've modified the MZ_MALLOC macro) to release a block allocated from the heap.
void mz_free(void *p);

#define MZ_ADLER32_INIT (1)
// mz_adler32() returns the initial adler-32 value to use when called with ptr==NULL.
mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len);

#define MZ_CRC32_INIT (0)
// mz_crc32() returns the initial CRC-32 value to use when called with ptr==NULL.
mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len);

// Compression strategies.
enum { MZ_DEFAULT_STRATEGY = 0, MZ_FILTERED = 1, MZ_HUFFMAN_ONLY = 2, MZ_RLE = 3, MZ_FIXED = 4 };

// Method
#define MZ_DEFLATED 8

#ifndef MINIZ_NO_ZLIB_APIS

// Heap allocation callbacks.
// Note that mz_alloc_func parameter types purpsosely differ from zlib's: items/size is size_t, not unsigned long.
typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size);
typedef void (*mz_free_func)(void *opaque, void *address);
typedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, size_t size);

#define MZ_VERSION          "9.1.15"
#define MZ_VERNUM           0x91F0
#define MZ_VER_MAJOR        9
#define MZ_VER_MINOR        1
#define MZ_VER_REVISION     15
#define MZ_VER_SUBREVISION  0

// Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The other values are for advanced use (refer to the zlib docs).
enum { MZ_NO_FLUSH = 0, MZ_PARTIAL_FLUSH = 1, MZ_SYNC_FLUSH = 2, MZ_FULL_FLUSH = 3, MZ_FINISH = 4, MZ_BLOCK = 5 };

// Return status codes. MZ_PARAM_ERROR is non-standard.
enum { MZ_OK = 0, MZ_STREAM_END = 1, MZ_NEED_DICT = 2, MZ_ERRNO = -1, MZ_STREAM_ERROR = -2, MZ_DATA_ERROR = -3, MZ_MEM_ERROR = -4, MZ_BUF_ERROR = -5, MZ_VERSION_ERROR = -6, MZ_PARAM_ERROR = -10000 };

// Compression levels: 0-9 are the standard zlib-style levels, 10 is best possible compression (not zlib compatible, and may be very slow), MZ_DEFAULT_COMPRESSION=MZ_DEFAULT_LEVEL.
enum { MZ_NO_COMPRESSION = 0, MZ_BEST_SPEED = 1, MZ_BEST_COMPRESSION = 9, MZ_UBER_COMPRESSION = 10, MZ_DEFAULT_LEVEL = 6, MZ_DEFAULT_COMPRESSION = -1 };

// Window bits
#define MZ_DEFAULT_WINDOW_BITS 15

struct mz_internal_state;

// Compression/decompression stream struct.
typedef struct mz_stream_s
{
  const unsigned char *next_in;     // pointer to next byte to read
  unsigned int avail_in;            // number of bytes available at next_in
  mz_ulong total_in;                // total number of bytes consumed so far

  unsigned char *next_out;          // pointer to next byte to write
  unsigned int avail_out;           // number of bytes that can be written to next_out
  mz_ulong total_out;               // total number of bytes produced so far

  char *msg;                        // error msg (unused)
  struct mz_internal_state *state;  // internal state, allocated by zalloc/zfree

  mz_alloc_func zalloc;             // optional heap allocation function (defaults to malloc)
  mz_free_func zfree;               // optional heap free function (defaults to free)
  void *opaque;                     // heap alloc function user pointer

  int data_type;                    // data_type (unused)
  mz_ulong adler;                   // adler32 of the source or uncompressed data
  mz_ulong reserved;                // not used
} mz_stream;

typedef mz_stream *mz_streamp;

// Returns the version string of miniz.c.
const char *mz_version(void);

// mz_deflateInit() initializes a compressor with default options:
// Parameters:
//  pStream must point to an initialized mz_stream struct.
//  level must be between [MZ_NO_COMPRESSION, MZ_BEST_COMPRESSION].
//  level 1 enables a specially optimized compression function that's been optimized purely for performance, not ratio.
//  (This special func. is currently only enabled when MINIZ_USE_UNALIGNED_LOADS_AND_STORES and MINIZ_LITTLE_ENDIAN are defined.)
// Return values:
//  MZ_OK on success.
//  MZ_STREAM_ERROR if the stream is bogus.
//  MZ_PARAM_ERROR if the input parameters are bogus.
//  MZ_MEM_ERROR on out of memory.
int mz_deflateInit(mz_streamp pStream, int level);

// mz_deflateInit2() is like mz_deflate(), except with more control:
// Additional parameters:
//   method must be MZ_DEFLATED
//   window_bits must be MZ_DEFAULT_WINDOW_BITS (to wrap the deflate stream with zlib header/adler-32 footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate/no header or footer)
//   mem_level must be between [1, 9] (it's checked but ignored by miniz.c)
int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy);

// Quickly resets a compressor without having to reallocate anything. Same as calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2().
int mz_deflateReset(mz_streamp pStream);

// mz_deflate() compresses the input to output, consuming as much of the input and producing as much output as possible.
// Parameters:
//   pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members.
//   flush may be MZ_NO_FLUSH, MZ_PARTIAL_FLUSH/MZ_SYNC_FLUSH, MZ_FULL_FLUSH, or MZ_FINISH.
// Return values:
//   MZ_OK on success (when flushing, or if more input is needed but not available, and/or there's more output to be written but the output buffer is full).
//   MZ_STREAM_END if all input has been consumed and all output bytes have been written. Don't call mz_deflate() on the stream anymore.
//   MZ_STREAM_ERROR if the stream is bogus.
//   MZ_PARAM_ERROR if one of the parameters is invalid.
//   MZ_BUF_ERROR if no forward progress is possible because the input and/or output buffers are empty. (Fill up the input buffer or free up some output space and try again.)
int mz_deflate(mz_streamp pStream, int flush);

// mz_deflateEnd() deinitializes a compressor:
// Return values:
//  MZ_OK on success.
//  MZ_STREAM_ERROR if the stream is bogus.
int mz_deflateEnd(mz_streamp pStream);

// mz_deflateBound() returns a (very) conservative upper bound on the amount of data that could be generated by deflate(), assuming flush is set to only MZ_NO_FLUSH or MZ_FINISH.
mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len);

// Single-call compression functions mz_compress() and mz_compress2():
// Returns MZ_OK on success, or one of the error codes from mz_deflate() on failure.
int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len);
int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level);

// mz_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling mz_compress().
mz_ulong mz_compressBound(mz_ulong source_len);

// Initializes a decompressor.
int mz_inflateInit(mz_streamp pStream);

// mz_inflateInit2() is like mz_inflateInit() with an additional option that controls the window size and whether or not the stream has been wrapped with a zlib header/footer:
// window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse zlib header/footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate).
int mz_inflateInit2(mz_streamp pStream, int window_bits);

// Decompresses the input stream to the output, consuming only as much of the input as needed, and writing as much to the output as possible.
// Parameters:
//   pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members.
//   flush may be MZ_NO_FLUSH, MZ_SYNC_FLUSH, or MZ_FINISH.
//   On the first call, if flush is MZ_FINISH it's assumed the input and output buffers are both sized large enough to decompress the entire stream in a single call (this is slightly faster).
//   MZ_FINISH implies that there are no more source bytes available beside what's already in the input buffer, and that the output buffer is large enough to hold the rest of the decompressed data.
// Return values:
//   MZ_OK on success. Either more input is needed but not available, and/or there's more output to be written but the output buffer is full.
//   MZ_STREAM_END if all needed input has been consumed and all output bytes have been written. For zlib streams, the adler-32 of the decompressed data has also been verified.
//   MZ_STREAM_ERROR if the stream is bogus.
//   MZ_DATA_ERROR if the deflate stream is invalid.
//   MZ_PARAM_ERROR if one of the parameters is invalid.
//   MZ_BUF_ERROR if no forward progress is possible because the input buffer is empty but the inflater needs more input to continue, or if the output buffer is not large enough. Call mz_inflate() again
//   with more input data, or with more room in the output buffer (except when using single call decompression, described above).
int mz_inflate(mz_streamp pStream, int flush);

// Deinitializes a decompressor.
int mz_inflateEnd(mz_streamp pStream);

// Single-call decompression.
// Returns MZ_OK on success, or one of the error codes from mz_inflate() on failure.
int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len);

// Returns a string description of the specified error code, or NULL if the error code is invalid.
const char *mz_error(int err);

// Redefine zlib-compatible names to miniz equivalents, so miniz.c can be used as a drop-in replacement for the subset of zlib that miniz.c supports.
// Define MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib in the same project.
#ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES
  typedef unsigned char Byte;
  typedef unsigned int uInt;
  typedef mz_ulong uLong;
  typedef Byte Bytef;
  typedef uInt uIntf;
  typedef char charf;
  typedef int intf;
  typedef void *voidpf;
  typedef uLong uLongf;
  typedef void *voidp;
  typedef void *const voidpc;
  #define Z_NULL                0
  #define Z_NO_FLUSH            MZ_NO_FLUSH
  #define Z_PARTIAL_FLUSH       MZ_PARTIAL_FLUSH
  #define Z_SYNC_FLUSH          MZ_SYNC_FLUSH
  #define Z_FULL_FLUSH          MZ_FULL_FLUSH
  #define Z_FINISH              MZ_FINISH
  #define Z_BLOCK               MZ_BLOCK
  #define Z_OK                  MZ_OK
  #define Z_STREAM_END          MZ_STREAM_END
  #define Z_NEED_DICT           MZ_NEED_DICT
  #define Z_ERRNO               MZ_ERRNO
  #define Z_STREAM_ERROR        MZ_STREAM_ERROR
  #define Z_DATA_ERROR          MZ_DATA_ERROR
  #define Z_MEM_ERROR           MZ_MEM_ERROR
  #define Z_BUF_ERROR           MZ_BUF_ERROR
  #define Z_VERSION_ERROR       MZ_VERSION_ERROR
  #define Z_PARAM_ERROR         MZ_PARAM_ERROR
  #define Z_NO_COMPRESSION      MZ_NO_COMPRESSION
  #define Z_BEST_SPEED          MZ_BEST_SPEED
  #define Z_BEST_COMPRESSION    MZ_BEST_COMPRESSION
  #define Z_DEFAULT_COMPRESSION MZ_DEFAULT_COMPRESSION
  #define Z_DEFAULT_STRATEGY    MZ_DEFAULT_STRATEGY
  #define Z_FILTERED            MZ_FILTERED
  #define Z_HUFFMAN_ONLY        MZ_HUFFMAN_ONLY
  #define Z_RLE                 MZ_RLE
  #define Z_FIXED               MZ_FIXED
  #define Z_DEFLATED            MZ_DEFLATED
  #define Z_DEFAULT_WINDOW_BITS MZ_DEFAULT_WINDOW_BITS
  #define alloc_func            mz_alloc_func
  #define free_func             mz_free_func
  #define internal_state        mz_internal_state
  #define z_stream              mz_stream
  #define deflateInit           mz_deflateInit
  #define deflateInit2          mz_deflateInit2
  #define deflateReset          mz_deflateReset
  #define deflate               mz_deflate
  #define deflateEnd            mz_deflateEnd
  #define deflateBound          mz_deflateBound
  #define compress              mz_compress
  #define compress2             mz_compress2
  #define compressBound         mz_compressBound
  #define inflateInit           mz_inflateInit
  #define inflateInit2          mz_inflateInit2
  #define inflate               mz_inflate
  #define inflateEnd            mz_inflateEnd
  #define uncompress            mz_uncompress
  #define crc32                 mz_crc32
  #define adler32               mz_adler32
  #define MAX_WBITS             15
  #define MAX_MEM_LEVEL         9
  #define zError                mz_error
  #define ZLIB_VERSION          MZ_VERSION
  #define ZLIB_VERNUM           MZ_VERNUM
  #define ZLIB_VER_MAJOR        MZ_VER_MAJOR
  #define ZLIB_VER_MINOR        MZ_VER_MINOR
  #define ZLIB_VER_REVISION     MZ_VER_REVISION
  #define ZLIB_VER_SUBREVISION  MZ_VER_SUBREVISION
  #define zlibVersion           mz_version
  #define zlib_version          mz_version()
#endif // #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES

#endif // MINIZ_NO_ZLIB_APIS

// ------------------- Types and macros

typedef unsigned char mz_uint8;
typedef signed short mz_int16;
typedef unsigned short mz_uint16;
typedef unsigned int mz_uint32;
typedef unsigned int mz_uint;
typedef long long mz_int64;
typedef unsigned long long mz_uint64;
typedef int mz_bool;

#define MZ_FALSE (0)
#define MZ_TRUE (1)

// An attempt to work around MSVC's spammy "warning C4127: conditional expression is constant" message.
#ifdef _MSC_VER
   #define MZ_MACRO_END while (0, 0)
#else
   #define MZ_MACRO_END while (0)
#endif

// ------------------- ZIP archive reading/writing

#ifndef MINIZ_NO_ARCHIVE_APIS

enum
{
  MZ_ZIP_MAX_IO_BUF_SIZE = 64*1024,
  MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 260,
  MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 256
};

typedef struct
{
  mz_uint32 m_file_index;
  mz_uint32 m_central_dir_ofs;
  mz_uint16 m_version_made_by;
  mz_uint16 m_version_needed;
  mz_uint16 m_bit_flag;
  mz_uint16 m_method;
#ifndef MINIZ_NO_TIME
  time_t m_time;
#endif
  mz_uint32 m_crc32;
  mz_uint64 m_comp_size;
  mz_uint64 m_uncomp_size;
  mz_uint16 m_internal_attr;
  mz_uint32 m_external_attr;
  mz_uint64 m_local_header_ofs;
  mz_uint32 m_comment_size;
  char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE];
  char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE];
} mz_zip_archive_file_stat;

typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n);
typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n);

struct mz_zip_internal_state_tag;
typedef struct mz_zip_internal_state_tag mz_zip_internal_state;

typedef enum
{
  MZ_ZIP_MODE_INVALID = 0,
  MZ_ZIP_MODE_READING = 1,
  MZ_ZIP_MODE_WRITING = 2,
  MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3
} mz_zip_mode;

typedef struct mz_zip_archive_tag
{
  mz_uint64 m_archive_size;
  mz_uint64 m_central_directory_file_ofs;
  mz_uint m_total_files;
  mz_zip_mode m_zip_mode;

  mz_uint m_file_offset_alignment;

  mz_alloc_func m_pAlloc;
  mz_free_func m_pFree;
  mz_realloc_func m_pRealloc;
  void *m_pAlloc_opaque;

  mz_file_read_func m_pRead;
  mz_file_write_func m_pWrite;
  void *m_pIO_opaque;

  mz_zip_internal_state *m_pState;

} mz_zip_archive;

typedef enum
{
  MZ_ZIP_FLAG_CASE_SENSITIVE                = 0x0100,
  MZ_ZIP_FLAG_IGNORE_PATH                   = 0x0200,
  MZ_ZIP_FLAG_COMPRESSED_DATA               = 0x0400,
  MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800
} mz_zip_flags;

// ZIP archive reading

// Inits a ZIP archive reader.
// These functions read and validate the archive's central directory.
mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint32 flags);
mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint32 flags);

#ifndef MINIZ_NO_STDIO
mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags);
#endif

// Returns the total number of files in the archive.
mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip);

// Returns detailed information about an archive file entry.
mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat);

// Determines if an archive file entry is a directory entry.
mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index);
mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index);

// Retrieves the filename of an archive file entry.
// Returns the number of bytes written to pFilename, or if filename_buf_size is 0 this function returns the number of bytes needed to fully store the filename.
mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size);

// Attempts to locates a file in the archive's central directory.
// Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH
// Returns -1 if the file cannot be found.
int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags);

// Extracts a archive file to a memory buffer using no memory allocation.
mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size);
mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size);

// Extracts a archive file to a memory buffer.
mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags);
mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags);

// Extracts a archive file to a dynamically allocated heap buffer.
void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags);
void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags);

// Extracts a archive file using a callback function to output the file's data.
mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags);
mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags);

#ifndef MINIZ_NO_STDIO
// Extracts a archive file to a disk file and sets its last accessed and modified times.
// This function only extracts files, not archive directory records.
mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags);
mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags);
#endif

// Ends archive reading, freeing all allocations, and closing the input archive file if mz_zip_reader_init_file() was used.
mz_bool mz_zip_reader_end(mz_zip_archive *pZip);

// ZIP archive writing

#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS

// Inits a ZIP archive writer.
mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size);
mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size);

#ifndef MINIZ_NO_STDIO
mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning);
#endif

// Converts a ZIP archive reader object into a writer object, to allow efficient in-place file appends to occur on an existing archive.
// For archives opened using mz_zip_reader_init_file, pFilename must be the archive's filename so it can be reopened for writing. If the file can't be reopened, mz_zip_reader_end() will be called.
// For archives opened using mz_zip_reader_init_mem, the memory block must be growable using the realloc callback (which defaults to realloc unless you've overridden it).
// Finally, for archives opened using mz_zip_reader_init, the mz_zip_archive's user provided m_pWrite function cannot be NULL.
// Note: In-place archive modification is not recommended unless you know what you're doing, because if execution stops or something goes wrong before
// the archive is finalized the file's central directory will be hosed.
mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename);

// Adds the contents of a memory buffer to an archive. These functions record the current local time into the archive.
// To add a directory entry, call this method with an archive name ending in a forwardslash with empty buffer.
// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION.
mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags);
mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32);

#ifndef MINIZ_NO_STDIO
// Adds the contents of a disk file to an archive. This function also records the disk file's modified time into the archive.
// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION.
mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags);
#endif

// Adds a file to an archive by fully cloning the data from another archive.
// This function fully clones the source file's compressed data (no recompression), along with its full filename, extra data, and comment fields.
mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint file_index);

// Finalizes the archive by writing the central directory records followed by the end of central directory record.
// After an archive is finalized, the only valid call on the mz_zip_archive struct is mz_zip_writer_end().
// An archive must be manually finalized by calling this function for it to be valid.
mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip);
mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **pBuf, size_t *pSize);

// Ends archive writing, freeing all allocations, and closing the output file if mz_zip_writer_init_file() was used.
// Note for the archive to be valid, it must have been finalized before ending.
mz_bool mz_zip_writer_end(mz_zip_archive *pZip);

// Misc. high-level helper functions:

// mz_zip_add_mem_to_archive_file_in_place() efficiently (but not atomically) appends a memory blob to a ZIP archive.
// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION.
mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags);

// Reads a single file from an archive into a heap block.
// Returns NULL on failure.
void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint zip_flags);

#endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS

#endif // #ifndef MINIZ_NO_ARCHIVE_APIS

// ------------------- Low-level Decompression API Definitions

// Decompression flags used by tinfl_decompress().
// TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream.
// TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input.
// TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB).
// TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the decompressed bytes.
enum
{
  TINFL_FLAG_PARSE_ZLIB_HEADER = 1,
  TINFL_FLAG_HAS_MORE_INPUT = 2,
  TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4,
  TINFL_FLAG_COMPUTE_ADLER32 = 8
};

// High level decompression functions:
// tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block allocated via malloc().
// On entry:
//  pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data to decompress.
// On return:
//  Function returns a pointer to the decompressed data, or NULL on failure.
//  *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data.
//  The caller must call mz_free() on the returned block when it's no longer needed.
void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags);

// tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory.
// Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success.
#define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1))
size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags);

// tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer.
// Returns 1 on success or 0 on failure.
typedef int (*tinfl_put_buf_func_ptr)(const void* pBuf, int len, void *pUser);
int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags);

struct tinfl_decompressor_tag; typedef struct tinfl_decompressor_tag tinfl_decompressor;

// Max size of LZ dictionary.
#define TINFL_LZ_DICT_SIZE 32768

// Return status.
typedef enum
{
  TINFL_STATUS_BAD_PARAM = -3,
  TINFL_STATUS_ADLER32_MISMATCH = -2,
  TINFL_STATUS_FAILED = -1,
  TINFL_STATUS_DONE = 0,
  TINFL_STATUS_NEEDS_MORE_INPUT = 1,
  TINFL_STATUS_HAS_MORE_OUTPUT = 2
} tinfl_status;

// Initializes the decompressor to its initial state.
#define tinfl_init(r) do { (r)->m_state = 0; } MZ_MACRO_END
#define tinfl_get_adler32(r) (r)->m_check_adler32

// Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability.
// This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output.
tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags);

// Internal/private bits follow.
enum
{
  TINFL_MAX_HUFF_TABLES = 3, TINFL_MAX_HUFF_SYMBOLS_0 = 288, TINFL_MAX_HUFF_SYMBOLS_1 = 32, TINFL_MAX_HUFF_SYMBOLS_2 = 19,
  TINFL_FAST_LOOKUP_BITS = 10, TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS
};

typedef struct
{
  mz_uint8 m_code_size[TINFL_MAX_HUFF_SYMBOLS_0];
  mz_int16 m_look_up[TINFL_FAST_LOOKUP_SIZE], m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2];
} tinfl_huff_table;

#if MINIZ_HAS_64BIT_REGISTERS
  #define TINFL_USE_64BIT_BITBUF 1
#endif

#if TINFL_USE_64BIT_BITBUF
  typedef mz_uint64 tinfl_bit_buf_t;
  #define TINFL_BITBUF_SIZE (64)
#else
  typedef mz_uint32 tinfl_bit_buf_t;
  #define TINFL_BITBUF_SIZE (32)
#endif

struct tinfl_decompressor_tag
{
  mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES];
  tinfl_bit_buf_t m_bit_buf;
  size_t m_dist_from_out_buf_start;
  tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES];
  mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137];
};

// ------------------- Low-level Compression API Definitions

// Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly slower, and raw/dynamic blocks will be output more frequently).
#define TDEFL_LESS_MEMORY 0

// tdefl_init() compression flags logically OR'd together (low 12 bits contain the max. number of probes per dictionary search):
// TDEFL_DEFAULT_MAX_PROBES: The compressor defaults to 128 dictionary probes per dictionary search. 0=Huffman only, 1=Huffman+LZ (fastest/crap compression), 4095=Huffman+LZ (slowest/best compression).
enum
{
  TDEFL_HUFFMAN_ONLY = 0, TDEFL_DEFAULT_MAX_PROBES = 128, TDEFL_MAX_PROBES_MASK = 0xFFF
};

// TDEFL_WRITE_ZLIB_HEADER: If set, the compressor outputs a zlib header before the deflate data, and the Adler-32 of the source data at the end. Otherwise, you'll get raw deflate data.
// TDEFL_COMPUTE_ADLER32: Always compute the adler-32 of the input data (even when not writing zlib headers).
// TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more efficient lazy parsing.
// TDEFL_NONDETERMINISTIC_PARSING_FLAG: Enable to decrease the compressor's initialization time to the minimum, but the output may vary from run to run given the same input (depending on the contents of memory).
// TDEFL_RLE_MATCHES: Only look for RLE matches (matches with a distance of 1)
// TDEFL_FILTER_MATCHES: Discards matches <= 5 chars if enabled.
// TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables.
// TDEFL_FORCE_ALL_RAW_BLOCKS: Only use raw (uncompressed) deflate blocks.
// The low 12 bits are reserved to control the max # of hash probes per dictionary lookup (see TDEFL_MAX_PROBES_MASK).
enum
{
  TDEFL_WRITE_ZLIB_HEADER             = 0x01000,
  TDEFL_COMPUTE_ADLER32               = 0x02000,
  TDEFL_GREEDY_PARSING_FLAG           = 0x04000,
  TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000,
  TDEFL_RLE_MATCHES                   = 0x10000,
  TDEFL_FILTER_MATCHES                = 0x20000,
  TDEFL_FORCE_ALL_STATIC_BLOCKS       = 0x40000,
  TDEFL_FORCE_ALL_RAW_BLOCKS          = 0x80000
};

// High level compression functions:
// tdefl_compress_mem_to_heap() compresses a block in memory to a heap block allocated via malloc().
// On entry:
//  pSrc_buf, src_buf_len: Pointer and size of source block to compress.
//  flags: The max match finder probes (default is 128) logically OR'd against the above flags. Higher probes are slower but improve compression.
// On return:
//  Function returns a pointer to the compressed data, or NULL on failure.
//  *pOut_len will be set to the compressed data's size, which could be larger than src_buf_len on uncompressible data.
//  The caller must free() the returned block when it's no longer needed.
void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags);

// tdefl_compress_mem_to_mem() compresses a block in memory to another block in memory.
// Returns 0 on failure.
size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags);

// Compresses an image to a compressed PNG file in memory.
// On entry:
//  pImage, w, h, and num_chans describe the image to compress. num_chans may be 1, 2, 3, or 4.
//  The image pitch in bytes per scanline will be w*num_chans. The leftmost pixel on the top scanline is stored first in memory.
//  level may range from [0,10], use MZ_NO_COMPRESSION, MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc. or a decent default is MZ_DEFAULT_LEVEL
//  If flip is true, the image will be flipped on the Y axis (useful for OpenGL apps).
// On return:
//  Function returns a pointer to the compressed data, or NULL on failure.
//  *pLen_out will be set to the size of the PNG image file.
//  The caller must mz_free() the returned heap block (which will typically be larger than *pLen_out) when it's no longer needed.
void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip);
void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out);

// Output stream interface. The compressor uses this interface to write compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time.
typedef mz_bool (*tdefl_put_buf_func_ptr)(const void* pBuf, int len, void *pUser);

// tdefl_compress_mem_to_output() compresses a block to an output stream. The above helpers use this function internally.
mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags);

enum { TDEFL_MAX_HUFF_TABLES = 3, TDEFL_MAX_HUFF_SYMBOLS_0 = 288, TDEFL_MAX_HUFF_SYMBOLS_1 = 32, TDEFL_MAX_HUFF_SYMBOLS_2 = 19, TDEFL_LZ_DICT_SIZE = 32768, TDEFL_LZ_DICT_SIZE_MASK = TDEFL_LZ_DICT_SIZE - 1, TDEFL_MIN_MATCH_LEN = 3, TDEFL_MAX_MATCH_LEN = 258 };

// TDEFL_OUT_BUF_SIZE MUST be large enough to hold a single entire compressed output block (using static/fixed Huffman codes).
#if TDEFL_LESS_MEMORY
enum { TDEFL_LZ_CODE_BUF_SIZE = 24 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13 ) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 12, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS };
#else
enum { TDEFL_LZ_CODE_BUF_SIZE = 64 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13 ) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 15, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS };
#endif

// The low-level tdefl functions below may be used directly if the above helper functions aren't flexible enough. The low-level functions don't make any heap allocations, unlike the above helper functions.
typedef enum
{
  TDEFL_STATUS_BAD_PARAM = -2,
  TDEFL_STATUS_PUT_BUF_FAILED = -1,
  TDEFL_STATUS_OKAY = 0,
  TDEFL_STATUS_DONE = 1,
} tdefl_status;

// Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums
typedef enum
{
  TDEFL_NO_FLUSH = 0,
  TDEFL_SYNC_FLUSH = 2,
  TDEFL_FULL_FLUSH = 3,
  TDEFL_FINISH = 4
} tdefl_flush;

// tdefl's compression state structure.
typedef struct
{
  tdefl_put_buf_func_ptr m_pPut_buf_func;
  void *m_pPut_buf_user;
  mz_uint m_flags, m_max_probes[2];
  int m_greedy_parsing;
  mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size;
  mz_uint8 *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end;
  mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, m_bit_buffer;
  mz_uint m_saved_match_dist, m_saved_match_len, m_saved_lit, m_output_flush_ofs, m_output_flush_remaining, m_finished, m_block_index, m_wants_to_finish;
  tdefl_status m_prev_return_status;
  const void *m_pIn_buf;
  void *m_pOut_buf;
  size_t *m_pIn_buf_size, *m_pOut_buf_size;
  tdefl_flush m_flush;
  const mz_uint8 *m_pSrc;
  size_t m_src_buf_left, m_out_buf_ofs;
  mz_uint8 m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1];
  mz_uint16 m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS];
  mz_uint16 m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS];
  mz_uint8 m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS];
  mz_uint8 m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE];
  mz_uint16 m_next[TDEFL_LZ_DICT_SIZE];
  mz_uint16 m_hash[TDEFL_LZ_HASH_SIZE];
  mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE];
} tdefl_compressor;

// Initializes the compressor.
// There is no corresponding deinit() function because the tdefl API's do not dynamically allocate memory.
// pBut_buf_func: If NULL, output data will be supplied to the specified callback. In this case, the user should call the tdefl_compress_buffer() API for compression.
// If pBut_buf_func is NULL the user should always call the tdefl_compress() API.
// flags: See the above enums (TDEFL_HUFFMAN_ONLY, TDEFL_WRITE_ZLIB_HEADER, etc.)
tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags);

// Compresses a block of data, consuming as much of the specified input buffer as possible, and writing as much compressed data to the specified output buffer as possible.
tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush);

// tdefl_compress_buffer() is only usable when the tdefl_init() is called with a non-NULL tdefl_put_buf_func_ptr.
// tdefl_compress_buffer() always consumes the entire input buffer.
tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush);

tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d);
mz_uint32 tdefl_get_adler32(tdefl_compressor *d);

// Can't use tdefl_create_comp_flags_from_zip_params if MINIZ_NO_ZLIB_APIS isn't defined, because it uses some of its macros.
#ifndef MINIZ_NO_ZLIB_APIS
// Create tdefl_compress() flags given zlib-style compression parameters.
// level may range from [0,10] (where 10 is absolute max compression, but may be much slower on some files)
// window_bits may be -15 (raw deflate) or 15 (zlib)
// strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, MZ_RLE, or MZ_FIXED
mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy);
#endif // #ifndef MINIZ_NO_ZLIB_APIS

#ifdef __cplusplus
}
#endif

#endif // MINIZ_HEADER_INCLUDED

// ------------------- End of Header: Implementation follows. (If you only want the header, define MINIZ_HEADER_FILE_ONLY.)

#ifndef MINIZ_HEADER_FILE_ONLY

typedef unsigned char mz_validate_uint16[sizeof(mz_uint16)==2 ? 1 : -1];
typedef unsigned char mz_validate_uint32[sizeof(mz_uint32)==4 ? 1 : -1];
typedef unsigned char mz_validate_uint64[sizeof(mz_uint64)==8 ? 1 : -1];

#include <string.h>
#include <assert.h>

#define MZ_ASSERT(x) assert(x)

#ifdef MINIZ_NO_MALLOC
  #define MZ_MALLOC(x) NULL
  #define MZ_FREE(x) (void)x, ((void)0)
  #define MZ_REALLOC(p, x) NULL
#else
  #define MZ_MALLOC(x) malloc(x)
  #define MZ_FREE(x) free(x)
  #define MZ_REALLOC(p, x) realloc(p, x)
#endif

#define MZ_MAX(a,b) (((a)>(b))?(a):(b))
#define MZ_MIN(a,b) (((a)<(b))?(a):(b))
#define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj))

#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN
  #define MZ_READ_LE16(p) *((const mz_uint16 *)(p))
  #define MZ_READ_LE32(p) *((const mz_uint32 *)(p))
#else
  #define MZ_READ_LE16(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U))
  #define MZ_READ_LE32(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U))
#endif

#ifdef _MSC_VER
  #define MZ_FORCEINLINE __forceinline
#elif defined(__GNUC__)
  #define MZ_FORCEINLINE inline __attribute__((__always_inline__))
#else
  #define MZ_FORCEINLINE inline
#endif

#ifdef __cplusplus
  extern "C" {
#endif

// ------------------- zlib-style API's

mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len)
{
  mz_uint32 i, s1 = (mz_uint32)(adler & 0xffff), s2 = (mz_uint32)(adler >> 16); size_t block_len = buf_len % 5552;
  if (!ptr) return MZ_ADLER32_INIT;
  while (buf_len) {
    for (i = 0; i + 7 < block_len; i += 8, ptr += 8) {
      s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1;
      s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1;
    }
    for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1;
    s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552;
  }
  return (s2 << 16) + s1;
}

// Karl Malbrain's compact CRC-32. See "A compact CCITT crc16 and crc32 C implementation that balances processor cache usage against speed": http://www.geocities.com/malbrain/
mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len)
{
  static const mz_uint32 s_crc32[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
    0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c };
  mz_uint32 crcu32 = (mz_uint32)crc;
  if (!ptr) return MZ_CRC32_INIT;
  crcu32 = ~crcu32; while (buf_len--) { mz_uint8 b = *ptr++; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)]; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)]; }
  return ~crcu32;
}

void mz_free(void *p)
{
  MZ_FREE(p);
}

#ifndef MINIZ_NO_ZLIB_APIS

static void *def_alloc_func(void *opaque, size_t items, size_t size) { (void)opaque, (void)items, (void)size; return MZ_MALLOC(items * size); }
static void def_free_func(void *opaque, void *address) { (void)opaque, (void)address; MZ_FREE(address); }
static void *def_realloc_func(void *opaque, void *address, size_t items, size_t size) { (void)opaque, (void)address, (void)items, (void)size; return MZ_REALLOC(address, items * size); }

const char *mz_version(void)
{
  return MZ_VERSION;
}

int mz_deflateInit(mz_streamp pStream, int level)
{
  return mz_deflateInit2(pStream, level, MZ_DEFLATED, MZ_DEFAULT_WINDOW_BITS, 9, MZ_DEFAULT_STRATEGY);
}

int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy)
{
  tdefl_compressor *pComp;
  mz_uint comp_flags = TDEFL_COMPUTE_ADLER32 | tdefl_create_comp_flags_from_zip_params(level, window_bits, strategy);

  if (!pStream) return MZ_STREAM_ERROR;
  if ((method != MZ_DEFLATED) || ((mem_level < 1) || (mem_level > 9)) || ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS))) return MZ_PARAM_ERROR;

  pStream->data_type = 0;
  pStream->adler = MZ_ADLER32_INIT;
  pStream->msg = NULL;
  pStream->reserved = 0;
  pStream->total_in = 0;
  pStream->total_out = 0;
  if (!pStream->zalloc) pStream->zalloc = def_alloc_func;
  if (!pStream->zfree) pStream->zfree = def_free_func;

  pComp = (tdefl_compressor *)pStream->zalloc(pStream->opaque, 1, sizeof(tdefl_compressor));
  if (!pComp)
    return MZ_MEM_ERROR;

  pStream->state = (struct mz_internal_state *)pComp;

  if (tdefl_init(pComp, NULL, NULL, comp_flags) != TDEFL_STATUS_OKAY)
  {
    mz_deflateEnd(pStream);
    return MZ_PARAM_ERROR;
  }

  return MZ_OK;
}

int mz_deflateReset(mz_streamp pStream)
{
  if ((!pStream) || (!pStream->state) || (!pStream->zalloc) || (!pStream->zfree)) return MZ_STREAM_ERROR;
  pStream->total_in = pStream->total_out = 0;
  tdefl_init((tdefl_compressor*)pStream->state, NULL, NULL, ((tdefl_compressor*)pStream->state)->m_flags);
  return MZ_OK;
}

int mz_deflate(mz_streamp pStream, int flush)
{
  size_t in_bytes, out_bytes;
  mz_ulong orig_total_in, orig_total_out;
  int mz_status = MZ_OK;

  if ((!pStream) || (!pStream->state) || (flush < 0) || (flush > MZ_FINISH) || (!pStream->next_out)) return MZ_STREAM_ERROR;
  if (!pStream->avail_out) return MZ_BUF_ERROR;

  if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH;

  if (((tdefl_compressor*)pStream->state)->m_prev_return_status == TDEFL_STATUS_DONE)
    return (flush == MZ_FINISH) ? MZ_STREAM_END : MZ_BUF_ERROR;

  orig_total_in = pStream->total_in; orig_total_out = pStream->total_out;
  for ( ; ; )
  {
    tdefl_status defl_status;
    in_bytes = pStream->avail_in; out_bytes = pStream->avail_out;

    defl_status = tdefl_compress((tdefl_compressor*)pStream->state, pStream->next_in, &in_bytes, pStream->next_out, &out_bytes, (tdefl_flush)flush);
    pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes;
    pStream->total_in += (mz_uint)in_bytes; pStream->adler = tdefl_get_adler32((tdefl_compressor*)pStream->state);

    pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes;
    pStream->total_out += (mz_uint)out_bytes;

    if (defl_status < 0)
    {
      mz_status = MZ_STREAM_ERROR;
      break;
    }
    else if (defl_status == TDEFL_STATUS_DONE)
    {
      mz_status = MZ_STREAM_END;
      break;
    }
    else if (!pStream->avail_out)
      break;
    else if ((!pStream->avail_in) && (flush != MZ_FINISH))
    {
      if ((flush) || (pStream->total_in != orig_total_in) || (pStream->total_out != orig_total_out))
        break;
      return MZ_BUF_ERROR; // Can't make forward progress without some input.
    }
  }
  return mz_status;
}

int mz_deflateEnd(mz_streamp pStream)
{
  if (!pStream) return MZ_STREAM_ERROR;
  if (pStream->state)
  {
    pStream->zfree(pStream->opaque, pStream->state);
    pStream->state = NULL;
  }
  return MZ_OK;
}

mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len)
{
  (void)pStream;
  // This is really over conservative. (And lame, but it's actually pretty tricky to compute a true upper bound given the way tdefl's blocking works.)
  return MZ_MAX(128 + (source_len * 110) / 100, 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5);
}

int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level)
{
  int status;
  mz_stream stream;
  memset(&stream, 0, sizeof(stream));

  // In case mz_ulong is 64-bits (argh I hate longs).
  if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR;

  stream.next_in = pSource;
  stream.avail_in = (mz_uint32)source_len;
  stream.next_out = pDest;
  stream.avail_out = (mz_uint32)*pDest_len;

  status = mz_deflateInit(&stream, level);
  if (status != MZ_OK) return status;

  status = mz_deflate(&stream, MZ_FINISH);
  if (status != MZ_STREAM_END)
  {
    mz_deflateEnd(&stream);
    return (status == MZ_OK) ? MZ_BUF_ERROR : status;
  }

  *pDest_len = stream.total_out;
  return mz_deflateEnd(&stream);
}

int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len)
{
  return mz_compress2(pDest, pDest_len, pSource, source_len, MZ_DEFAULT_COMPRESSION);
}

mz_ulong mz_compressBound(mz_ulong source_len)
{
  return mz_deflateBound(NULL, source_len);
}

typedef struct
{
  tinfl_decompressor m_decomp;
  mz_uint m_dict_ofs, m_dict_avail, m_first_call, m_has_flushed; int m_window_bits;
  mz_uint8 m_dict[TINFL_LZ_DICT_SIZE];
  tinfl_status m_last_status;
} inflate_state;

int mz_inflateInit2(mz_streamp pStream, int window_bits)
{
  inflate_state *pDecomp;
  if (!pStream) return MZ_STREAM_ERROR;
  if ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS)) return MZ_PARAM_ERROR;

  pStream->data_type = 0;
  pStream->adler = 0;
  pStream->msg = NULL;
  pStream->total_in = 0;
  pStream->total_out = 0;
  pStream->reserved = 0;
  if (!pStream->zalloc) pStream->zalloc = def_alloc_func;
  if (!pStream->zfree) pStream->zfree = def_free_func;

  pDecomp = (inflate_state*)pStream->zalloc(pStream->opaque, 1, sizeof(inflate_state));
  if (!pDecomp) return MZ_MEM_ERROR;

  pStream->state = (struct mz_internal_state *)pDecomp;

  tinfl_init(&pDecomp->m_decomp);
  pDecomp->m_dict_ofs = 0;
  pDecomp->m_dict_avail = 0;
  pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT;
  pDecomp->m_first_call = 1;
  pDecomp->m_has_flushed = 0;
  pDecomp->m_window_bits = window_bits;

  return MZ_OK;
}

int mz_inflateInit(mz_streamp pStream)
{
   return mz_inflateInit2(pStream, MZ_DEFAULT_WINDOW_BITS);
}

int mz_inflate(mz_streamp pStream, int flush)
{
  inflate_state* pState;
  mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32;
  size_t in_bytes, out_bytes, orig_avail_in;
  tinfl_status status;

  if ((!pStream) || (!pStream->state)) return MZ_STREAM_ERROR;
  if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH;
  if ((flush) && (flush != MZ_SYNC_FLUSH) && (flush != MZ_FINISH)) return MZ_STREAM_ERROR;

  pState = (inflate_state*)pStream->state;
  if (pState->m_window_bits > 0) decomp_flags |= TINFL_FLAG_PARSE_ZLIB_HEADER;
  orig_avail_in = pStream->avail_in;

  first_call = pState->m_first_call; pState->m_first_call = 0;
  if (pState->m_last_status < 0) return MZ_DATA_ERROR;

  if (pState->m_has_flushed && (flush != MZ_FINISH)) return MZ_STREAM_ERROR;
  pState->m_has_flushed |= (flush == MZ_FINISH);

  if ((flush == MZ_FINISH) && (first_call))
  {
    // MZ_FINISH on the first call implies that the input and output buffers are large enough to hold the entire compressed/decompressed file.
    decomp_flags |= TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF;
    in_bytes = pStream->avail_in; out_bytes = pStream->avail_out;
    status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pStream->next_out, pStream->next_out, &out_bytes, decomp_flags);
    pState->m_last_status = status;
    pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; pStream->total_in += (mz_uint)in_bytes;
    pStream->adler = tinfl_get_adler32(&pState->m_decomp);
    pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes; pStream->total_out += (mz_uint)out_bytes;

    if (status < 0)
      return MZ_DATA_ERROR;
    else if (status != TINFL_STATUS_DONE)
    {
      pState->m_last_status = TINFL_STATUS_FAILED;
      return MZ_BUF_ERROR;
    }
    return MZ_STREAM_END;
  }
  // flush != MZ_FINISH then we must assume there's more input.
  if (flush != MZ_FINISH) decomp_flags |= TINFL_FLAG_HAS_MORE_INPUT;

  if (pState->m_dict_avail)
  {
    n = MZ_MIN(pState->m_dict_avail, pStream->avail_out);
    memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n);
    pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n;
    pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1);
    return ((pState->m_last_status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK;
  }

  for ( ; ; )
  {
    in_bytes = pStream->avail_in;
    out_bytes = TINFL_LZ_DICT_SIZE - pState->m_dict_ofs;

    status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pState->m_dict, pState->m_dict + pState->m_dict_ofs, &out_bytes, decomp_flags);
    pState->m_last_status = status;

    pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes;
    pStream->total_in += (mz_uint)in_bytes; pStream->adler = tinfl_get_adler32(&pState->m_decomp);

    pState->m_dict_avail = (mz_uint)out_bytes;

    n = MZ_MIN(pState->m_dict_avail, pStream->avail_out);
    memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n);
    pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n;
    pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1);

    if (status < 0)
       return MZ_DATA_ERROR; // Stream is corrupted (there could be some uncompressed data left in the output dictionary - oh well).
    else if ((status == TINFL_STATUS_NEEDS_MORE_INPUT) && (!orig_avail_in))
      return MZ_BUF_ERROR; // Signal caller that we can't make forward progress without supplying more input or by setting flush to MZ_FINISH.
    else if (flush == MZ_FINISH)
    {
       // The output buffer MUST be large to hold the remaining uncompressed data when flush==MZ_FINISH.
       if (status == TINFL_STATUS_DONE)
          return pState->m_dict_avail ? MZ_BUF_ERROR : MZ_STREAM_END;
       // status here must be TINFL_STATUS_HAS_MORE_OUTPUT, which means there's at least 1 more byte on the way. If there's no more room left in the output buffer then something is wrong.
       else if (!pStream->avail_out)
          return MZ_BUF_ERROR;
    }
    else if ((status == TINFL_STATUS_DONE) || (!pStream->avail_in) || (!pStream->avail_out) || (pState->m_dict_avail))
      break;
  }

  return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK;
}

int mz_inflateEnd(mz_streamp pStream)
{
  if (!pStream)
    return MZ_STREAM_ERROR;
  if (pStream->state)
  {
    pStream->zfree(pStream->opaque, pStream->state);
    pStream->state = NULL;
  }
  return MZ_OK;
}

int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len)
{
  mz_stream stream;
  int status;
  memset(&stream, 0, sizeof(stream));

  // In case mz_ulong is 64-bits (argh I hate longs).
  if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR;

  stream.next_in = pSource;
  stream.avail_in = (mz_uint32)source_len;
  stream.next_out = pDest;
  stream.avail_out = (mz_uint32)*pDest_len;

  status = mz_inflateInit(&stream);
  if (status != MZ_OK)
    return status;

  status = mz_inflate(&stream, MZ_FINISH);
  if (status != MZ_STREAM_END)
  {
    mz_inflateEnd(&stream);
    return ((status == MZ_BUF_ERROR) && (!stream.avail_in)) ? MZ_DATA_ERROR : status;
  }
  *pDest_len = stream.total_out;

  return mz_inflateEnd(&stream);
}

const char *mz_error(int err)
{
  static struct { int m_err; const char *m_pDesc; } s_error_descs[] =
  {
    { MZ_OK, "" }, { MZ_STREAM_END, "stream end" }, { MZ_NEED_DICT, "need dictionary" }, { MZ_ERRNO, "file error" }, { MZ_STREAM_ERROR, "stream error" },
    { MZ_DATA_ERROR, "data error" }, { MZ_MEM_ERROR, "out of memory" }, { MZ_BUF_ERROR, "buf error" }, { MZ_VERSION_ERROR, "version error" }, { MZ_PARAM_ERROR, "parameter error" }
  };
  mz_uint i; for (i = 0; i < sizeof(s_error_descs) / sizeof(s_error_descs[0]); ++i) if (s_error_descs[i].m_err == err) return s_error_descs[i].m_pDesc;
  return NULL;
}

#endif //MINIZ_NO_ZLIB_APIS

// ------------------- Low-level Decompression (completely independent from all compression API's)

#define TINFL_MEMCPY(d, s, l) memcpy(d, s, l)
#define TINFL_MEMSET(p, c, l) memset(p, c, l)

#define TINFL_CR_BEGIN switch(r->m_state) { case 0:
#define TINFL_CR_RETURN(state_index, result) do { status = result; r->m_state = state_index; goto common_exit; case state_index:; } MZ_MACRO_END
#define TINFL_CR_RETURN_FOREVER(state_index, result) do { for ( ; ; ) { TINFL_CR_RETURN(state_index, result); } } MZ_MACRO_END
#define TINFL_CR_FINISH }

// TODO: If the caller has indicated that there's no more input, and we attempt to read beyond the input buf, then something is wrong with the input because the inflator never
// reads ahead more than it needs to. Currently TINFL_GET_BYTE() pads the end of the stream with 0's in this scenario.
#define TINFL_GET_BYTE(state_index, c) do { \
  if (pIn_buf_cur >= pIn_buf_end) { \
    for ( ; ; ) { \
      if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) { \
        TINFL_CR_RETURN(state_index, TINFL_STATUS_NEEDS_MORE_INPUT); \
        if (pIn_buf_cur < pIn_buf_end) { \
          c = *pIn_buf_cur++; \
          break; \
        } \
      } else { \
        c = 0; \
        break; \
      } \
    } \
  } else c = *pIn_buf_cur++; } MZ_MACRO_END

#define TINFL_NEED_BITS(state_index, n) do { mz_uint c; TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; } while (num_bits < (mz_uint)(n))
#define TINFL_SKIP_BITS(state_index, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END
#define TINFL_GET_BITS(state_index, b, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } b = bit_buf & ((1 << (n)) - 1); bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END

// TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes remaining in the input buffer falls below 2.
// It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a
// Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the
// bit buffer contains >=15 bits (deflate's max. Huffman code size).
#define TINFL_HUFF_BITBUF_FILL(state_index, pHuff) \
  do { \
    temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \
    if (temp >= 0) { \
      code_len = temp >> 9; \
      if ((code_len) && (num_bits >= code_len)) \
      break; \
    } else if (num_bits > TINFL_FAST_LOOKUP_BITS) { \
       code_len = TINFL_FAST_LOOKUP_BITS; \
       do { \
          temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \
       } while ((temp < 0) && (num_bits >= (code_len + 1))); if (temp >= 0) break; \
    } TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; \
  } while (num_bits < 15);

// TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex than you would initially expect because the zlib API expects the decompressor to never read
// beyond the final byte of the deflate stream. (In other words, when this macro wants to read another byte from the input, it REALLY needs another byte in order to fully
// decode the next Huffman code.) Handling this properly is particularly important on raw deflate (non-zlib) streams, which aren't followed by a byte aligned adler-32.
// The slow path is only executed at the very end of the input buffer.
#define TINFL_HUFF_DECODE(state_index, sym, pHuff) do { \
  int temp; mz_uint code_len, c; \
  if (num_bits < 15) { \
    if ((pIn_buf_end - pIn_buf_cur) < 2) { \
       TINFL_HUFF_BITBUF_FILL(state_index, pHuff); \
    } else { \
       bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); pIn_buf_cur += 2; num_bits += 16; \
    } \
  } \
  if ((temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \
    code_len = temp >> 9, temp &= 511; \
  else { \
    code_len = TINFL_FAST_LOOKUP_BITS; do { temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; } while (temp < 0); \
  } sym = temp; bit_buf >>= code_len; num_bits -= code_len; } MZ_MACRO_END

tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags)
{
  static const int s_length_base[31] = { 3,4,5,6,7,8,9,10,11,13, 15,17,19,23,27,31,35,43,51,59, 67,83,99,115,131,163,195,227,258,0,0 };
  static const int s_length_extra[31]= { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 };
  static const int s_dist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, 257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0};
  static const int s_dist_extra[32] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13};
  static const mz_uint8 s_length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 };
  static const int s_min_table_sizes[3] = { 257, 1, 4 };

  tinfl_status status = TINFL_STATUS_FAILED; mz_uint32 num_bits, dist, counter, num_extra; tinfl_bit_buf_t bit_buf;
  const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size;
  mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next + *pOut_buf_size;
  size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start;

  // Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter).
  if (((out_buf_size_mask + 1) & out_buf_size_mask) || (pOut_buf_next < pOut_buf_start)) { *pIn_buf_size = *pOut_buf_size = 0; return TINFL_STATUS_BAD_PARAM; }

  num_bits = r->m_num_bits; bit_buf = r->m_bit_buf; dist = r->m_dist; counter = r->m_counter; num_extra = r->m_num_extra; dist_from_out_buf_start = r->m_dist_from_out_buf_start;
  TINFL_CR_BEGIN

  bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0; r->m_z_adler32 = r->m_check_adler32 = 1;
  if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER)
  {
    TINFL_GET_BYTE(1, r->m_zhdr0); TINFL_GET_BYTE(2, r->m_zhdr1);
    counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8));
    if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)(1U << (8U + (r->m_zhdr0 >> 4)))));
    if (counter) { TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); }
  }

  do
  {
    TINFL_GET_BITS(3, r->m_final, 3); r->m_type = r->m_final >> 1;
    if (r->m_type == 0)
    {
      TINFL_SKIP_BITS(5, num_bits & 7);
      for (counter = 0; counter < 4; ++counter) { if (num_bits) TINFL_GET_BITS(6, r->m_raw_header[counter], 8); else TINFL_GET_BYTE(7, r->m_raw_header[counter]); }
      if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != (mz_uint)(0xFFFF ^ (r->m_raw_header[2] | (r->m_raw_header[3] << 8)))) { TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED); }
      while ((counter) && (num_bits))
      {
        TINFL_GET_BITS(51, dist, 8);
        while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT); }
        *pOut_buf_cur++ = (mz_uint8)dist;
        counter--;
      }
      while (counter)
      {
        size_t n; while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT); }
        while (pIn_buf_cur >= pIn_buf_end)
        {
          if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT)
          {
            TINFL_CR_RETURN(38, TINFL_STATUS_NEEDS_MORE_INPUT);
          }
          else
          {
            TINFL_CR_RETURN_FOREVER(40, TINFL_STATUS_FAILED);
          }
        }
        n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter);
        TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n); pIn_buf_cur += n; pOut_buf_cur += n; counter -= (mz_uint)n;
      }
    }
    else if (r->m_type == 3)
    {
      TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED);
    }
    else
    {
      if (r->m_type == 1)
      {
        mz_uint8 *p = r->m_tables[0].m_code_size; mz_uint i;
        r->m_table_sizes[0] = 288; r->m_table_sizes[1] = 32; TINFL_MEMSET(r->m_tables[1].m_code_size, 5, 32);
        for ( i = 0; i <= 143; ++i) *p++ = 8; for ( ; i <= 255; ++i) *p++ = 9; for ( ; i <= 279; ++i) *p++ = 7; for ( ; i <= 287; ++i) *p++ = 8;
      }
      else
      {
        for (counter = 0; counter < 3; counter++) { TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); r->m_table_sizes[counter] += s_min_table_sizes[counter]; }
        MZ_CLEAR_OBJ(r->m_tables[2].m_code_size); for (counter = 0; counter < r->m_table_sizes[2]; counter++) { mz_uint s; TINFL_GET_BITS(14, s, 3); r->m_tables[2].m_code_size[s_length_dezigzag[counter]] = (mz_uint8)s; }
        r->m_table_sizes[2] = 19;
      }
      for ( ; (int)r->m_type >= 0; r->m_type--)
      {
        int tree_next, tree_cur; tinfl_huff_table *pTable;
        mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16]; pTable = &r->m_tables[r->m_type]; MZ_CLEAR_OBJ(total_syms); MZ_CLEAR_OBJ(pTable->m_look_up); MZ_CLEAR_OBJ(pTable->m_tree);
        for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) total_syms[pTable->m_code_size[i]]++;
        used_syms = 0, total = 0; next_code[0] = next_code[1] = 0;
        for (i = 1; i <= 15; ++i) { used_syms += total_syms[i]; next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); }
        if ((65536 != total) && (used_syms > 1))
        {
          TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED);
        }
        for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index)
        {
          mz_uint rev_code = 0, l, cur_code, code_size = pTable->m_code_size[sym_index]; if (!code_size) continue;
          cur_code = next_code[code_size]++; for (l = code_size; l > 0; l--, cur_code >>= 1) rev_code = (rev_code << 1) | (cur_code & 1);
          if (code_size <= TINFL_FAST_LOOKUP_BITS) { mz_int16 k = (mz_int16)((code_size << 9) | sym_index); while (rev_code < TINFL_FAST_LOOKUP_SIZE) { pTable->m_look_up[rev_code] = k; rev_code += (1 << code_size); } continue; }
          if (0 == (tree_cur = pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)])) { pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; }
          rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1);
          for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--)
          {
            tree_cur -= ((rev_code >>= 1) & 1);
            if (!pTable->m_tree[-tree_cur - 1]) { pTable->m_tree[-tree_cur - 1] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } else tree_cur = pTable->m_tree[-tree_cur - 1];
          }
          tree_cur -= ((rev_code >>= 1) & 1); pTable->m_tree[-tree_cur - 1] = (mz_int16)sym_index;
        }
        if (r->m_type == 2)
        {
          for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]); )
          {
            mz_uint s; TINFL_HUFF_DECODE(16, dist, &r->m_tables[2]); if (dist < 16) { r->m_len_codes[counter++] = (mz_uint8)dist; continue; }
            if ((dist == 16) && (!counter))
            {
              TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED);
            }
            num_extra = "\02\03\07"[dist - 16]; TINFL_GET_BITS(18, s, num_extra); s += "\03\03\013"[dist - 16];
            TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s); counter += s;
          }
          if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter)
          {
            TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED);
          }
          TINFL_MEMCPY(r->m_tables[0].m_code_size, r->m_len_codes, r->m_table_sizes[0]); TINFL_MEMCPY(r->m_tables[1].m_code_size, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]);
        }
      }
      for ( ; ; )
      {
        mz_uint8 *pSrc;
        for ( ; ; )
        {
          if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2))
          {
            TINFL_HUFF_DECODE(23, counter, &r->m_tables[0]);
            if (counter >= 256)
              break;
            while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT); }
            *pOut_buf_cur++ = (mz_uint8)counter;
          }
          else
          {
            int sym2; mz_uint code_len;
#if TINFL_USE_64BIT_BITBUF
            if (num_bits < 30) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits); pIn_buf_cur += 4; num_bits += 32; }
#else
            if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; }
#endif
            if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0)
              code_len = sym2 >> 9;
            else
            {
              code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0);
            }
            counter = sym2; bit_buf >>= code_len; num_bits -= code_len;
            if (counter & 256)
              break;

#if !TINFL_USE_64BIT_BITBUF
            if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; }
#endif
            if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0)
              code_len = sym2 >> 9;
            else
            {
              code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0);
            }
            bit_buf >>= code_len; num_bits -= code_len;

            pOut_buf_cur[0] = (mz_uint8)counter;
            if (sym2 & 256)
            {
              pOut_buf_cur++;
              counter = sym2;
              break;
            }
            pOut_buf_cur[1] = (mz_uint8)sym2;
            pOut_buf_cur += 2;
          }
        }
        if ((counter &= 511) == 256) break;

        num_extra = s_length_extra[counter - 257]; counter = s_length_base[counter - 257];
        if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(25, extra_bits, num_extra); counter += extra_bits; }

        TINFL_HUFF_DECODE(26, dist, &r->m_tables[1]);
        num_extra = s_dist_extra[dist]; dist = s_dist_base[dist];
        if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(27, extra_bits, num_extra); dist += extra_bits; }

        dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start;
        if ((dist > dist_from_out_buf_start) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))
        {
          TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED);
        }

        pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask);

        if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end)
        {
          while (counter--)
          {
            while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT); }
            *pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask];
          }
          continue;
        }
#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES
        else if ((counter >= 9) && (counter <= dist))
        {
          const mz_uint8 *pSrc_end = pSrc + (counter & ~7);
          do
          {
            ((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0];
            ((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1];
            pOut_buf_cur += 8;
          } while ((pSrc += 8) < pSrc_end);
          if ((counter &= 7) < 3)
          {
            if (counter)
            {
              pOut_buf_cur[0] = pSrc[0];
              if (counter > 1)
                pOut_buf_cur[1] = pSrc[1];
              pOut_buf_cur += counter;
            }
            continue;
          }
        }
#endif
        do
        {
          pOut_buf_cur[0] = pSrc[0];
          pOut_buf_cur[1] = pSrc[1];
          pOut_buf_cur[2] = pSrc[2];
          pOut_buf_cur += 3; pSrc += 3;
        } while ((int)(counter -= 3) > 2);
        if ((int)counter > 0)
        {
          pOut_buf_cur[0] = pSrc[0];
          if ((int)counter > 1)
            pOut_buf_cur[1] = pSrc[1];
          pOut_buf_cur += counter;
        }
      }
    }
  } while (!(r->m_final & 1));
  if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER)
  {
    TINFL_SKIP_BITS(32, num_bits & 7); for (counter = 0; counter < 4; ++counter) { mz_uint s; if (num_bits) TINFL_GET_BITS(41, s, 8); else TINFL_GET_BYTE(42, s); r->m_z_adler32 = (r->m_z_adler32 << 8) | s; }
  }
  TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE);
  TINFL_CR_FINISH

common_exit:
  r->m_num_bits = num_bits; r->m_bit_buf = bit_buf; r->m_dist = dist; r->m_counter = counter; r->m_num_extra = num_extra; r->m_dist_from_out_buf_start = dist_from_out_buf_start;
  *pIn_buf_size = pIn_buf_cur - pIn_buf_next; *pOut_buf_size = pOut_buf_cur - pOut_buf_next;
  if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0))
  {
    const mz_uint8 *ptr = pOut_buf_next; size_t buf_len = *pOut_buf_size;
    mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16; size_t block_len = buf_len % 5552;
    while (buf_len)
    {
      for (i = 0; i + 7 < block_len; i += 8, ptr += 8)
      {
        s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1;
        s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1;
      }
      for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1;
      s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552;
    }
    r->m_check_adler32 = (s2 << 16) + s1; if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32)) status = TINFL_STATUS_ADLER32_MISMATCH;
  }
  return status;
}

// Higher level helper functions.
void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags)
{
  tinfl_decompressor decomp; void *pBuf = NULL, *pNew_buf; size_t src_buf_ofs = 0, out_buf_capacity = 0;
  *pOut_len = 0;
  tinfl_init(&decomp);
  for ( ; ; )
  {
    size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity;
    tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8*)pSrc_buf + src_buf_ofs, &src_buf_size, (mz_uint8*)pBuf, pBuf ? (mz_uint8*)pBuf + *pOut_len : NULL, &dst_buf_size,
      (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF);
    if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT))
    {
      MZ_FREE(pBuf); *pOut_len = 0; return NULL;
    }
    src_buf_ofs += src_buf_size;
    *pOut_len += dst_buf_size;
    if (status == TINFL_STATUS_DONE) break;
    new_out_buf_capacity = out_buf_capacity * 2; if (new_out_buf_capacity < 128) new_out_buf_capacity = 128;
    pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity);
    if (!pNew_buf)
    {
      MZ_FREE(pBuf); *pOut_len = 0; return NULL;
    }
    pBuf = pNew_buf; out_buf_capacity = new_out_buf_capacity;
  }
  return pBuf;
}

size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags)
{
  tinfl_decompressor decomp; tinfl_status status; tinfl_init(&decomp);
  status = tinfl_decompress(&decomp, (const mz_uint8*)pSrc_buf, &src_buf_len, (mz_uint8*)pOut_buf, (mz_uint8*)pOut_buf, &out_buf_len, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF);
  return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len;
}

int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags)
{
  int result = 0;
  tinfl_decompressor decomp;
  mz_uint8 *pDict = (mz_uint8*)MZ_MALLOC(TINFL_LZ_DICT_SIZE); size_t in_buf_ofs = 0, dict_ofs = 0;
  if (!pDict)
    return TINFL_STATUS_FAILED;
  tinfl_init(&decomp);
  for ( ; ; )
  {
    size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs;
    tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8*)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size,
      (flags & ~(TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)));
    in_buf_ofs += in_buf_size;
    if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user)))
      break;
    if (status != TINFL_STATUS_HAS_MORE_OUTPUT)
    {
      result = (status == TINFL_STATUS_DONE);
      break;
    }
    dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1);
  }
  MZ_FREE(pDict);
  *pIn_buf_size = in_buf_ofs;
  return result;
}

// ------------------- Low-level Compression (independent from all decompression API's)

// Purposely making these tables static for faster init and thread safety.
static const mz_uint16 s_tdefl_len_sym[256] = {
  257,258,259,260,261,262,263,264,265,265,266,266,267,267,268,268,269,269,269,269,270,270,270,270,271,271,271,271,272,272,272,272,
  273,273,273,273,273,273,273,273,274,274,274,274,274,274,274,274,275,275,275,275,275,275,275,275,276,276,276,276,276,276,276,276,
  277,277,277,277,277,277,277,277,277,277,277,277,277,277,277,277,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,
  279,279,279,279,279,279,279,279,279,279,279,279,279,279,279,279,280,280,280,280,280,280,280,280,280,280,280,280,280,280,280,280,
  281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,
  282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,
  283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,
  284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,285 };

static const mz_uint8 s_tdefl_len_extra[256] = {
  0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
  4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
  5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
  5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,0 };

static const mz_uint8 s_tdefl_small_dist_sym[512] = {
  0,1,2,3,4,4,5,5,6,6,6,6,7,7,7,7,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,11,11,11,11,11,11,
  11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13,
  13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14,14,14,14,14,
  14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,
  14,14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,
  15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,16,16,16,16,16,16,16,16,16,16,16,16,16,
  16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
  16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
  16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,17,17,17,17,17,17,17,17,17,17,17,17,17,17,
  17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,
  17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,
  17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17 };

static const mz_uint8 s_tdefl_small_dist_extra[512] = {
  0,0,0,0,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,
  5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
  6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
  6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
  7,7,7,7,7,7,7,7 };

static const mz_uint8 s_tdefl_large_dist_sym[128] = {
  0,0,18,19,20,20,21,21,22,22,22,22,23,23,23,23,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,26,26,26,26,
  26,26,26,26,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,
  28,28,28,28,28,28,28,28,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29 };

static const mz_uint8 s_tdefl_large_dist_extra[128] = {
  0,0,8,8,9,9,9,9,10,10,10,10,10,10,10,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,
  12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,
  13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13 };

// Radix sorts tdefl_sym_freq[] array by 16-bit key m_key. Returns ptr to sorted values.
typedef struct { mz_uint16 m_key, m_sym_index; } tdefl_sym_freq;
static tdefl_sym_freq* tdefl_radix_sort_syms(mz_uint num_syms, tdefl_sym_freq* pSyms0, tdefl_sym_freq* pSyms1)
{
  mz_uint32 total_passes = 2, pass_shift, pass, i, hist[256 * 2]; tdefl_sym_freq* pCur_syms = pSyms0, *pNew_syms = pSyms1; MZ_CLEAR_OBJ(hist);
  for (i = 0; i < num_syms; i++) { mz_uint freq = pSyms0[i].m_key; hist[freq & 0xFF]++; hist[256 + ((freq >> 8) & 0xFF)]++; }
  while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256])) total_passes--;
  for (pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8)
  {
    const mz_uint32* pHist = &hist[pass << 8];
    mz_uint offsets[256], cur_ofs = 0;
    for (i = 0; i < 256; i++) { offsets[i] = cur_ofs; cur_ofs += pHist[i]; }
    for (i = 0; i < num_syms; i++) pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = pCur_syms[i];
    { tdefl_sym_freq* t = pCur_syms; pCur_syms = pNew_syms; pNew_syms = t; }
  }
  return pCur_syms;
}

// tdefl_calculate_minimum_redundancy() originally written by: Alistair Moffat, alistair@cs.mu.oz.au, Jyrki Katajainen, jyrki@diku.dk, November 1996.
static void tdefl_calculate_minimum_redundancy(tdefl_sym_freq *A, int n)
{
  int root, leaf, next, avbl, used, dpth;
  if (n==0) return; else if (n==1) { A[0].m_key = 1; return; }
  A[0].m_key += A[1].m_key; root = 0; leaf = 2;
  for (next=1; next < n-1; next++)
  {
    if (leaf>=n || A[root].m_key<A[leaf].m_key) { A[next].m_key = A[root].m_key; A[root++].m_key = (mz_uint16)next; } else A[next].m_key = A[leaf++].m_key;
    if (leaf>=n || (root<next && A[root].m_key<A[leaf].m_key)) { A[next].m_key = (mz_uint16)(A[next].m_key + A[root].m_key); A[root++].m_key = (mz_uint16)next; } else A[next].m_key = (mz_uint16)(A[next].m_key + A[leaf++].m_key);
  }
  A[n-2].m_key = 0; for (next=n-3; next>=0; next--) A[next].m_key = A[A[next].m_key].m_key+1;
  avbl = 1; used = dpth = 0; root = n-2; next = n-1;
  while (avbl>0)
  {
    while (root>=0 && (int)A[root].m_key==dpth) { used++; root--; }
    while (avbl>used) { A[next--].m_key = (mz_uint16)(dpth); avbl--; }
    avbl = 2*used; dpth++; used = 0;
  }
}

// Limits canonical Huffman code table's max code size.
enum { TDEFL_MAX_SUPPORTED_HUFF_CODESIZE = 32 };
static void tdefl_huffman_enforce_max_code_size(int *pNum_codes, int code_list_len, int max_code_size)
{
  int i; mz_uint32 total = 0; if (code_list_len <= 1) return;
  for (i = max_code_size + 1; i <= TDEFL_MAX_SUPPORTED_HUFF_CODESIZE; i++) pNum_codes[max_code_size] += pNum_codes[i];
  for (i = max_code_size; i > 0; i--) total += (((mz_uint32)pNum_codes[i]) << (max_code_size - i));
  while (total != (1UL << max_code_size))
  {
    pNum_codes[max_code_size]--;
    for (i = max_code_size - 1; i > 0; i--) if (pNum_codes[i]) { pNum_codes[i]--; pNum_codes[i + 1] += 2; break; }
    total--;
  }
}

static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int table_len, int code_size_limit, int static_table)
{
  int i, j, l, num_codes[1 + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE]; mz_uint next_code[TDEFL_MAX_SUPPORTED_HUFF_CODESIZE + 1]; MZ_CLEAR_OBJ(num_codes);
  if (static_table)
  {
    for (i = 0; i < table_len; i++) num_codes[d->m_huff_code_sizes[table_num][i]]++;
  }
  else
  {
    tdefl_sym_freq syms0[TDEFL_MAX_HUFF_SYMBOLS], syms1[TDEFL_MAX_HUFF_SYMBOLS], *pSyms;
    int num_used_syms = 0;
    const mz_uint16 *pSym_count = &d->m_huff_count[table_num][0];
    for (i = 0; i < table_len; i++) if (pSym_count[i]) { syms0[num_used_syms].m_key = (mz_uint16)pSym_count[i]; syms0[num_used_syms++].m_sym_index = (mz_uint16)i; }

    pSyms = tdefl_radix_sort_syms(num_used_syms, syms0, syms1); tdefl_calculate_minimum_redundancy(pSyms, num_used_syms);

    for (i = 0; i < num_used_syms; i++) num_codes[pSyms[i].m_key]++;

    tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, code_size_limit);

    MZ_CLEAR_OBJ(d->m_huff_code_sizes[table_num]); MZ_CLEAR_OBJ(d->m_huff_codes[table_num]);
    for (i = 1, j = num_used_syms; i <= code_size_limit; i++)
      for (l = num_codes[i]; l > 0; l--) d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (mz_uint8)(i);
  }

  next_code[1] = 0; for (j = 0, i = 2; i <= code_size_limit; i++) next_code[i] = j = ((j + num_codes[i - 1]) << 1);

  for (i = 0; i < table_len; i++)
  {
    mz_uint rev_code = 0, code, code_size; if ((code_size = d->m_huff_code_sizes[table_num][i]) == 0) continue;
    code = next_code[code_size]++; for (l = code_size; l > 0; l--, code >>= 1) rev_code = (rev_code << 1) | (code & 1);
    d->m_huff_codes[table_num][i] = (mz_uint16)rev_code;
  }
}

#define TDEFL_PUT_BITS(b, l) do { \
  mz_uint bits = b; mz_uint len = l; MZ_ASSERT(bits <= ((1U << len) - 1U)); \
  d->m_bit_buffer |= (bits << d->m_bits_in); d->m_bits_in += len; \
  while (d->m_bits_in >= 8) { \
    if (d->m_pOutput_buf < d->m_pOutput_buf_end) \
      *d->m_pOutput_buf++ = (mz_uint8)(d->m_bit_buffer); \
      d->m_bit_buffer >>= 8; \
      d->m_bits_in -= 8; \
  } \
} MZ_MACRO_END

#define TDEFL_RLE_PREV_CODE_SIZE() { if (rle_repeat_count) { \
  if (rle_repeat_count < 3) { \
    d->m_huff_count[2][prev_code_size] = (mz_uint16)(d->m_huff_count[2][prev_code_size] + rle_repeat_count); \
    while (rle_repeat_count--) packed_code_sizes[num_packed_code_sizes++] = prev_code_size; \
  } else { \
    d->m_huff_count[2][16] = (mz_uint16)(d->m_huff_count[2][16] + 1); packed_code_sizes[num_packed_code_sizes++] = 16; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_repeat_count - 3); \
} rle_repeat_count = 0; } }

#define TDEFL_RLE_ZERO_CODE_SIZE() { if (rle_z_count) { \
  if (rle_z_count < 3) { \
    d->m_huff_count[2][0] = (mz_uint16)(d->m_huff_count[2][0] + rle_z_count); while (rle_z_count--) packed_code_sizes[num_packed_code_sizes++] = 0; \
  } else if (rle_z_count <= 10) { \
    d->m_huff_count[2][17] = (mz_uint16)(d->m_huff_count[2][17] + 1); packed_code_sizes[num_packed_code_sizes++] = 17; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 3); \
  } else { \
    d->m_huff_count[2][18] = (mz_uint16)(d->m_huff_count[2][18] + 1); packed_code_sizes[num_packed_code_sizes++] = 18; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 11); \
} rle_z_count = 0; } }

static mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };

static void tdefl_start_dynamic_block(tdefl_compressor *d)
{
  int num_lit_codes, num_dist_codes, num_bit_lengths; mz_uint i, total_code_sizes_to_pack, num_packed_code_sizes, rle_z_count, rle_repeat_count, packed_code_sizes_index;
  mz_uint8 code_sizes_to_pack[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], packed_code_sizes[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], prev_code_size = 0xFF;

  d->m_huff_count[0][256] = 1;

  tdefl_optimize_huffman_table(d, 0, TDEFL_MAX_HUFF_SYMBOLS_0, 15, MZ_FALSE);
  tdefl_optimize_huffman_table(d, 1, TDEFL_MAX_HUFF_SYMBOLS_1, 15, MZ_FALSE);

  for (num_lit_codes = 286; num_lit_codes > 257; num_lit_codes--) if (d->m_huff_code_sizes[0][num_lit_codes - 1]) break;
  for (num_dist_codes = 30; num_dist_codes > 1; num_dist_codes--) if (d->m_huff_code_sizes[1][num_dist_codes - 1]) break;

  memcpy(code_sizes_to_pack, &d->m_huff_code_sizes[0][0], num_lit_codes);
  memcpy(code_sizes_to_pack + num_lit_codes, &d->m_huff_code_sizes[1][0], num_dist_codes);
  total_code_sizes_to_pack = num_lit_codes + num_dist_codes; num_packed_code_sizes = 0; rle_z_count = 0; rle_repeat_count = 0;

  memset(&d->m_huff_count[2][0], 0, sizeof(d->m_huff_count[2][0]) * TDEFL_MAX_HUFF_SYMBOLS_2);
  for (i = 0; i < total_code_sizes_to_pack; i++)
  {
    mz_uint8 code_size = code_sizes_to_pack[i];
    if (!code_size)
    {
      TDEFL_RLE_PREV_CODE_SIZE();
      if (++rle_z_count == 138) { TDEFL_RLE_ZERO_CODE_SIZE(); }
    }
    else
    {
      TDEFL_RLE_ZERO_CODE_SIZE();
      if (code_size != prev_code_size)
      {
        TDEFL_RLE_PREV_CODE_SIZE();
        d->m_huff_count[2][code_size] = (mz_uint16)(d->m_huff_count[2][code_size] + 1); packed_code_sizes[num_packed_code_sizes++] = code_size;
      }
      else if (++rle_repeat_count == 6)
      {
        TDEFL_RLE_PREV_CODE_SIZE();
      }
    }
    prev_code_size = code_size;
  }
  if (rle_repeat_count) { TDEFL_RLE_PREV_CODE_SIZE(); } else { TDEFL_RLE_ZERO_CODE_SIZE(); }

  tdefl_optimize_huffman_table(d, 2, TDEFL_MAX_HUFF_SYMBOLS_2, 7, MZ_FALSE);

  TDEFL_PUT_BITS(2, 2);

  TDEFL_PUT_BITS(num_lit_codes - 257, 5);
  TDEFL_PUT_BITS(num_dist_codes - 1, 5);

  for (num_bit_lengths = 18; num_bit_lengths >= 0; num_bit_lengths--) if (d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[num_bit_lengths]]) break;
  num_bit_lengths = MZ_MAX(4, (num_bit_lengths + 1)); TDEFL_PUT_BITS(num_bit_lengths - 4, 4);
  for (i = 0; (int)i < num_bit_lengths; i++) TDEFL_PUT_BITS(d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[i]], 3);

  for (packed_code_sizes_index = 0; packed_code_sizes_index < num_packed_code_sizes; )
  {
    mz_uint code = packed_code_sizes[packed_code_sizes_index++]; MZ_ASSERT(code < TDEFL_MAX_HUFF_SYMBOLS_2);
    TDEFL_PUT_BITS(d->m_huff_codes[2][code], d->m_huff_code_sizes[2][code]);
    if (code >= 16) TDEFL_PUT_BITS(packed_code_sizes[packed_code_sizes_index++], "\02\03\07"[code - 16]);
  }
}

static void tdefl_start_static_block(tdefl_compressor *d)
{
  mz_uint i;
  mz_uint8 *p = &d->m_huff_code_sizes[0][0];

  for (i = 0; i <= 143; ++i) *p++ = 8;
  for ( ; i <= 255; ++i) *p++ = 9;
  for ( ; i <= 279; ++i) *p++ = 7;
  for ( ; i <= 287; ++i) *p++ = 8;

  memset(d->m_huff_code_sizes[1], 5, 32);

  tdefl_optimize_huffman_table(d, 0, 288, 15, MZ_TRUE);
  tdefl_optimize_huffman_table(d, 1, 32, 15, MZ_TRUE);

  TDEFL_PUT_BITS(1, 2);
}

static const mz_uint mz_bitmasks[17] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF };

#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS
static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d)
{
  mz_uint flags;
  mz_uint8 *pLZ_codes;
  mz_uint8 *pOutput_buf = d->m_pOutput_buf;
  mz_uint8 *pLZ_code_buf_end = d->m_pLZ_code_buf;
  mz_uint64 bit_buffer = d->m_bit_buffer;
  mz_uint bits_in = d->m_bits_in;

#define TDEFL_PUT_BITS_FAST(b, l) { bit_buffer |= (((mz_uint64)(b)) << bits_in); bits_in += (l); }

  flags = 1;
  for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < pLZ_code_buf_end; flags >>= 1)
  {
    if (flags == 1)
      flags = *pLZ_codes++ | 0x100;

    if (flags & 1)
    {
      mz_uint s0, s1, n0, n1, sym, num_extra_bits;
      mz_uint match_len = pLZ_codes[0], match_dist = *(const mz_uint16 *)(pLZ_codes + 1); pLZ_codes += 3;

      MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]);
      TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]);
      TDEFL_PUT_BITS_FAST(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]);

      // This sequence coaxes MSVC into using cmov's vs. jmp's.
      s0 = s_tdefl_small_dist_sym[match_dist & 511];
      n0 = s_tdefl_small_dist_extra[match_dist & 511];
      s1 = s_tdefl_large_dist_sym[match_dist >> 8];
      n1 = s_tdefl_large_dist_extra[match_dist >> 8];
      sym = (match_dist < 512) ? s0 : s1;
      num_extra_bits = (match_dist < 512) ? n0 : n1;

      MZ_ASSERT(d->m_huff_code_sizes[1][sym]);
      TDEFL_PUT_BITS_FAST(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]);
      TDEFL_PUT_BITS_FAST(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits);
    }
    else
    {
      mz_uint lit = *pLZ_codes++;
      MZ_ASSERT(d->m_huff_code_sizes[0][lit]);
      TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]);

      if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end))
      {
        flags >>= 1;
        lit = *pLZ_codes++;
        MZ_ASSERT(d->m_huff_code_sizes[0][lit]);
        TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]);

        if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end))
        {
          flags >>= 1;
          lit = *pLZ_codes++;
          MZ_ASSERT(d->m_huff_code_sizes[0][lit]);
          TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]);
        }
      }
    }

    if (pOutput_buf >= d->m_pOutput_buf_end)
      return MZ_FALSE;

    *(mz_uint64*)pOutput_buf = bit_buffer;
    pOutput_buf += (bits_in >> 3);
    bit_buffer >>= (bits_in & ~7);
    bits_in &= 7;
  }

#undef TDEFL_PUT_BITS_FAST

  d->m_pOutput_buf = pOutput_buf;
  d->m_bits_in = 0;
  d->m_bit_buffer = 0;

  while (bits_in)
  {
    mz_uint32 n = MZ_MIN(bits_in, 16);
    TDEFL_PUT_BITS((mz_uint)bit_buffer & mz_bitmasks[n], n);
    bit_buffer >>= n;
    bits_in -= n;
  }

  TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]);

  return (d->m_pOutput_buf < d->m_pOutput_buf_end);
}
#else
static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d)
{
  mz_uint flags;
  mz_uint8 *pLZ_codes;

  flags = 1;
  for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < d->m_pLZ_code_buf; flags >>= 1)
  {
    if (flags == 1)
      flags = *pLZ_codes++ | 0x100;
    if (flags & 1)
    {
      mz_uint sym, num_extra_bits;
      mz_uint match_len = pLZ_codes[0], match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); pLZ_codes += 3;

      MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]);
      TDEFL_PUT_BITS(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]);
      TDEFL_PUT_BITS(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]);

      if (match_dist < 512)
      {
        sym = s_tdefl_small_dist_sym[match_dist]; num_extra_bits = s_tdefl_small_dist_extra[match_dist];
      }
      else
      {
        sym = s_tdefl_large_dist_sym[match_dist >> 8]; num_extra_bits = s_tdefl_large_dist_extra[match_dist >> 8];
      }
      MZ_ASSERT(d->m_huff_code_sizes[1][sym]);
      TDEFL_PUT_BITS(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]);
      TDEFL_PUT_BITS(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits);
    }
    else
    {
      mz_uint lit = *pLZ_codes++;
      MZ_ASSERT(d->m_huff_code_sizes[0][lit]);
      TDEFL_PUT_BITS(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]);
    }
  }

  TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]);

  return (d->m_pOutput_buf < d->m_pOutput_buf_end);
}
#endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS

static mz_bool tdefl_compress_block(tdefl_compressor *d, mz_bool static_block)
{
  if (static_block)
    tdefl_start_static_block(d);
  else
    tdefl_start_dynamic_block(d);
  return tdefl_compress_lz_codes(d);
}

static int tdefl_flush_block(tdefl_compressor *d, int flush)
{
  mz_uint saved_bit_buf, saved_bits_in;
  mz_uint8 *pSaved_output_buf;
  mz_bool comp_block_succeeded = MZ_FALSE;
  int n, use_raw_block = ((d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS) != 0) && (d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size;
  mz_uint8 *pOutput_buf_start = ((d->m_pPut_buf_func == NULL) && ((*d->m_pOut_buf_size - d->m_out_buf_ofs) >= TDEFL_OUT_BUF_SIZE)) ? ((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs) : d->m_output_buf;

  d->m_pOutput_buf = pOutput_buf_start;
  d->m_pOutput_buf_end = d->m_pOutput_buf + TDEFL_OUT_BUF_SIZE - 16;

  MZ_ASSERT(!d->m_output_flush_remaining);
  d->m_output_flush_ofs = 0;
  d->m_output_flush_remaining = 0;

  *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> d->m_num_flags_left);
  d->m_pLZ_code_buf -= (d->m_num_flags_left == 8);

  if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index))
  {
    TDEFL_PUT_BITS(0x78, 8); TDEFL_PUT_BITS(0x01, 8);
  }

  TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1);

  pSaved_output_buf = d->m_pOutput_buf; saved_bit_buf = d->m_bit_buffer; saved_bits_in = d->m_bits_in;

  if (!use_raw_block)
    comp_block_succeeded = tdefl_compress_block(d, (d->m_flags & TDEFL_FORCE_ALL_STATIC_BLOCKS) || (d->m_total_lz_bytes < 48));

  // If the block gets expanded, forget the current contents of the output buffer and send a raw block instead.
  if ( ((use_raw_block) || ((d->m_total_lz_bytes) && ((d->m_pOutput_buf - pSaved_output_buf + 1U) >= d->m_total_lz_bytes))) &&
       ((d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size) )
  {
    mz_uint i; d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in;
    TDEFL_PUT_BITS(0, 2);
    if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); }
    for (i = 2; i; --i, d->m_total_lz_bytes ^= 0xFFFF)
    {
      TDEFL_PUT_BITS(d->m_total_lz_bytes & 0xFFFF, 16);
    }
    for (i = 0; i < d->m_total_lz_bytes; ++i)
    {
      TDEFL_PUT_BITS(d->m_dict[(d->m_lz_code_buf_dict_pos + i) & TDEFL_LZ_DICT_SIZE_MASK], 8);
    }
  }
  // Check for the extremely unlikely (if not impossible) case of the compressed block not fitting into the output buffer when using dynamic codes.
  else if (!comp_block_succeeded)
  {
    d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in;
    tdefl_compress_block(d, MZ_TRUE);
  }

  if (flush)
  {
    if (flush == TDEFL_FINISH)
    {
      if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); }
      if (d->m_flags & TDEFL_WRITE_ZLIB_HEADER) { mz_uint i, a = d->m_adler32; for (i = 0; i < 4; i++) { TDEFL_PUT_BITS((a >> 24) & 0xFF, 8); a <<= 8; } }
    }
    else
    {
      mz_uint i, z = 0; TDEFL_PUT_BITS(0, 3); if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } for (i = 2; i; --i, z ^= 0xFFFF) { TDEFL_PUT_BITS(z & 0xFFFF, 16); }
    }
  }

  MZ_ASSERT(d->m_pOutput_buf < d->m_pOutput_buf_end);

  memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0);
  memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1);

  d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; d->m_num_flags_left = 8; d->m_lz_code_buf_dict_pos += d->m_total_lz_bytes; d->m_total_lz_bytes = 0; d->m_block_index++;

  if ((n = (int)(d->m_pOutput_buf - pOutput_buf_start)) != 0)
  {
    if (d->m_pPut_buf_func)
    {
      *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf;
      if (!(*d->m_pPut_buf_func)(d->m_output_buf, n, d->m_pPut_buf_user))
        return (d->m_prev_return_status = TDEFL_STATUS_PUT_BUF_FAILED);
    }
    else if (pOutput_buf_start == d->m_output_buf)
    {
      int bytes_to_copy = (int)MZ_MIN((size_t)n, (size_t)(*d->m_pOut_buf_size - d->m_out_buf_ofs));
      memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf, bytes_to_copy);
      d->m_out_buf_ofs += bytes_to_copy;
      if ((n -= bytes_to_copy) != 0)
      {
        d->m_output_flush_ofs = bytes_to_copy;
        d->m_output_flush_remaining = n;
      }
    }
    else
    {
      d->m_out_buf_ofs += n;
    }
  }

  return d->m_output_flush_remaining;
}

#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES
#define TDEFL_READ_UNALIGNED_WORD(p) *(const mz_uint16*)(p)
static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len)
{
  mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len;
  mz_uint num_probes_left = d->m_max_probes[match_len >= 32];
  const mz_uint16 *s = (const mz_uint16*)(d->m_dict + pos), *p, *q;
  mz_uint16 c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]), s01 = TDEFL_READ_UNALIGNED_WORD(s);
  MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return;
  for ( ; ; )
  {
    for ( ; ; )
    {
      if (--num_probes_left == 0) return;
      #define TDEFL_PROBE \
        next_probe_pos = d->m_next[probe_pos]; \
        if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) return; \
        probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \
        if (TDEFL_READ_UNALIGNED_WORD(&d->m_dict[probe_pos + match_len - 1]) == c01) break;
      TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE;
    }
    if (!dist) break; q = (const mz_uint16*)(d->m_dict + probe_pos); if (TDEFL_READ_UNALIGNED_WORD(q) != s01) continue; p = s; probe_len = 32;
    do { } while ( (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) &&
                   (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (--probe_len > 0) );
    if (!probe_len)
    {
      *pMatch_dist = dist; *pMatch_len = MZ_MIN(max_match_len, TDEFL_MAX_MATCH_LEN); break;
    }
    else if ((probe_len = ((mz_uint)(p - s) * 2) + (mz_uint)(*(const mz_uint8*)p == *(const mz_uint8*)q)) > match_len)
    {
      *pMatch_dist = dist; if ((*pMatch_len = match_len = MZ_MIN(max_match_len, probe_len)) == max_match_len) break;
      c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]);
    }
  }
}
#else
static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len)
{
  mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len;
  mz_uint num_probes_left = d->m_max_probes[match_len >= 32];
  const mz_uint8 *s = d->m_dict + pos, *p, *q;
  mz_uint8 c0 = d->m_dict[pos + match_len], c1 = d->m_dict[pos + match_len - 1];
  MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return;
  for ( ; ; )
  {
    for ( ; ; )
    {
      if (--num_probes_left == 0) return;
      #define TDEFL_PROBE \
        next_probe_pos = d->m_next[probe_pos]; \
        if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) return; \
        probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \
        if ((d->m_dict[probe_pos + match_len] == c0) && (d->m_dict[probe_pos + match_len - 1] == c1)) break;
      TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE;
    }
    if (!dist) break; p = s; q = d->m_dict + probe_pos; for (probe_len = 0; probe_len < max_match_len; probe_len++) if (*p++ != *q++) break;
    if (probe_len > match_len)
    {
      *pMatch_dist = dist; if ((*pMatch_len = match_len = probe_len) == max_match_len) return;
      c0 = d->m_dict[pos + match_len]; c1 = d->m_dict[pos + match_len - 1];
    }
  }
}
#endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES

#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN
static mz_bool tdefl_compress_fast(tdefl_compressor *d)
{
  // Faster, minimally featured LZRW1-style match+parse loop with better register utilization. Intended for applications where raw throughput is valued more highly than ratio.
  mz_uint lookahead_pos = d->m_lookahead_pos, lookahead_size = d->m_lookahead_size, dict_size = d->m_dict_size, total_lz_bytes = d->m_total_lz_bytes, num_flags_left = d->m_num_flags_left;
  mz_uint8 *pLZ_code_buf = d->m_pLZ_code_buf, *pLZ_flags = d->m_pLZ_flags;
  mz_uint cur_pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK;

  while ((d->m_src_buf_left) || ((d->m_flush) && (lookahead_size)))
  {
    const mz_uint TDEFL_COMP_FAST_LOOKAHEAD_SIZE = 4096;
    mz_uint dst_pos = (lookahead_pos + lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK;
    mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(d->m_src_buf_left, TDEFL_COMP_FAST_LOOKAHEAD_SIZE - lookahead_size);
    d->m_src_buf_left -= num_bytes_to_process;
    lookahead_size += num_bytes_to_process;

    while (num_bytes_to_process)
    {
      mz_uint32 n = MZ_MIN(TDEFL_LZ_DICT_SIZE - dst_pos, num_bytes_to_process);
      memcpy(d->m_dict + dst_pos, d->m_pSrc, n);
      if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1))
        memcpy(d->m_dict + TDEFL_LZ_DICT_SIZE + dst_pos, d->m_pSrc, MZ_MIN(n, (TDEFL_MAX_MATCH_LEN - 1) - dst_pos));
      d->m_pSrc += n;
      dst_pos = (dst_pos + n) & TDEFL_LZ_DICT_SIZE_MASK;
      num_bytes_to_process -= n;
    }

    dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - lookahead_size, dict_size);
    if ((!d->m_flush) && (lookahead_size < TDEFL_COMP_FAST_LOOKAHEAD_SIZE)) break;

    while (lookahead_size >= 4)
    {
      mz_uint cur_match_dist, cur_match_len = 1;
      mz_uint8 *pCur_dict = d->m_dict + cur_pos;
      mz_uint first_trigram = (*(const mz_uint32 *)pCur_dict) & 0xFFFFFF;
      mz_uint hash = (first_trigram ^ (first_trigram >> (24 - (TDEFL_LZ_HASH_BITS - 8)))) & TDEFL_LEVEL1_HASH_SIZE_MASK;
      mz_uint probe_pos = d->m_hash[hash];
      d->m_hash[hash] = (mz_uint16)lookahead_pos;

      if (((cur_match_dist = (mz_uint16)(lookahead_pos - probe_pos)) <= dict_size) && ((*(const mz_uint32 *)(d->m_dict + (probe_pos &= TDEFL_LZ_DICT_SIZE_MASK)) & 0xFFFFFF) == first_trigram))
      {
        const mz_uint16 *p = (const mz_uint16 *)pCur_dict;
        const mz_uint16 *q = (const mz_uint16 *)(d->m_dict + probe_pos);
        mz_uint32 probe_len = 32;
        do { } while ( (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) &&
          (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (--probe_len > 0) );
        cur_match_len = ((mz_uint)(p - (const mz_uint16 *)pCur_dict) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q);
        if (!probe_len)
          cur_match_len = cur_match_dist ? TDEFL_MAX_MATCH_LEN : 0;

        if ((cur_match_len < TDEFL_MIN_MATCH_LEN) || ((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U*1024U)))
        {
          cur_match_len = 1;
          *pLZ_code_buf++ = (mz_uint8)first_trigram;
          *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1);
          d->m_huff_count[0][(mz_uint8)first_trigram]++;
        }
        else
        {
          mz_uint32 s0, s1;
          cur_match_len = MZ_MIN(cur_match_len, lookahead_size);

          MZ_ASSERT((cur_match_len >= TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 1) && (cur_match_dist <= TDEFL_LZ_DICT_SIZE));

          cur_match_dist--;

          pLZ_code_buf[0] = (mz_uint8)(cur_match_len - TDEFL_MIN_MATCH_LEN);
          *(mz_uint16 *)(&pLZ_code_buf[1]) = (mz_uint16)cur_match_dist;
          pLZ_code_buf += 3;
          *pLZ_flags = (mz_uint8)((*pLZ_flags >> 1) | 0x80);

          s0 = s_tdefl_small_dist_sym[cur_match_dist & 511];
          s1 = s_tdefl_large_dist_sym[cur_match_dist >> 8];
          d->m_huff_count[1][(cur_match_dist < 512) ? s0 : s1]++;

          d->m_huff_count[0][s_tdefl_len_sym[cur_match_len - TDEFL_MIN_MATCH_LEN]]++;
        }
      }
      else
      {
        *pLZ_code_buf++ = (mz_uint8)first_trigram;
        *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1);
        d->m_huff_count[0][(mz_uint8)first_trigram]++;
      }

      if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; }

      total_lz_bytes += cur_match_len;
      lookahead_pos += cur_match_len;
      dict_size = MZ_MIN(dict_size + cur_match_len, TDEFL_LZ_DICT_SIZE);
      cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK;
      MZ_ASSERT(lookahead_size >= cur_match_len);
      lookahead_size -= cur_match_len;

      if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8])
      {
        int n;
        d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size;
        d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left;
        if ((n = tdefl_flush_block(d, 0)) != 0)
          return (n < 0) ? MZ_FALSE : MZ_TRUE;
        total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left;
      }
    }

    while (lookahead_size)
    {
      mz_uint8 lit = d->m_dict[cur_pos];

      total_lz_bytes++;
      *pLZ_code_buf++ = lit;
      *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1);
      if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; }

      d->m_huff_count[0][lit]++;

      lookahead_pos++;
      dict_size = MZ_MIN(dict_size + 1, TDEFL_LZ_DICT_SIZE);
      cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK;
      lookahead_size--;

      if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8])
      {
        int n;
        d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size;
        d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left;
        if ((n = tdefl_flush_block(d, 0)) != 0)
          return (n < 0) ? MZ_FALSE : MZ_TRUE;
        total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left;
      }
    }
  }

  d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size;
  d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left;
  return MZ_TRUE;
}
#endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN

static MZ_FORCEINLINE void tdefl_record_literal(tdefl_compressor *d, mz_uint8 lit)
{
  d->m_total_lz_bytes++;
  *d->m_pLZ_code_buf++ = lit;
  *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> 1); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; }
  d->m_huff_count[0][lit]++;
}

static MZ_FORCEINLINE void tdefl_record_match(tdefl_compressor *d, mz_uint match_len, mz_uint match_dist)
{
  mz_uint32 s0, s1;

  MZ_ASSERT((match_len >= TDEFL_MIN_MATCH_LEN) && (match_dist >= 1) && (match_dist <= TDEFL_LZ_DICT_SIZE));

  d->m_total_lz_bytes += match_len;

  d->m_pLZ_code_buf[0] = (mz_uint8)(match_len - TDEFL_MIN_MATCH_LEN);

  match_dist -= 1;
  d->m_pLZ_code_buf[1] = (mz_uint8)(match_dist & 0xFF);
  d->m_pLZ_code_buf[2] = (mz_uint8)(match_dist >> 8); d->m_pLZ_code_buf += 3;

  *d->m_pLZ_flags = (mz_uint8)((*d->m_pLZ_flags >> 1) | 0x80); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; }

  s0 = s_tdefl_small_dist_sym[match_dist & 511]; s1 = s_tdefl_large_dist_sym[(match_dist >> 8) & 127];
  d->m_huff_count[1][(match_dist < 512) ? s0 : s1]++;

  if (match_len >= TDEFL_MIN_MATCH_LEN) d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++;
}

static mz_bool tdefl_compress_normal(tdefl_compressor *d)
{
  const mz_uint8 *pSrc = d->m_pSrc; size_t src_buf_left = d->m_src_buf_left;
  tdefl_flush flush = d->m_flush;

  while ((src_buf_left) || ((flush) && (d->m_lookahead_size)))
  {
    mz_uint len_to_move, cur_match_dist, cur_match_len, cur_pos;
    // Update dictionary and hash chains. Keeps the lookahead size equal to TDEFL_MAX_MATCH_LEN.
    if ((d->m_lookahead_size + d->m_dict_size) >= (TDEFL_MIN_MATCH_LEN - 1))
    {
      mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK, ins_pos = d->m_lookahead_pos + d->m_lookahead_size - 2;
      mz_uint hash = (d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK];
      mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size);
      const mz_uint8 *pSrc_end = pSrc + num_bytes_to_process;
      src_buf_left -= num_bytes_to_process;
      d->m_lookahead_size += num_bytes_to_process;
      while (pSrc != pSrc_end)
      {
        mz_uint8 c = *pSrc++; d->m_dict[dst_pos] = c; if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c;
        hash = ((hash << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1);
        d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos);
        dst_pos = (dst_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; ins_pos++;
      }
    }
    else
    {
      while ((src_buf_left) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN))
      {
        mz_uint8 c = *pSrc++;
        mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK;
        src_buf_left--;
        d->m_dict[dst_pos] = c;
        if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1))
          d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c;
        if ((++d->m_lookahead_size + d->m_dict_size) >= TDEFL_MIN_MATCH_LEN)
        {
          mz_uint ins_pos = d->m_lookahead_pos + (d->m_lookahead_size - 1) - 2;
          mz_uint hash = ((d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << (TDEFL_LZ_HASH_SHIFT * 2)) ^ (d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1);
          d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos);
        }
      }
    }
    d->m_dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - d->m_lookahead_size, d->m_dict_size);
    if ((!flush) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN))
      break;

    // Simple lazy/greedy parsing state machine.
    len_to_move = 1; cur_match_dist = 0; cur_match_len = d->m_saved_match_len ? d->m_saved_match_len : (TDEFL_MIN_MATCH_LEN - 1); cur_pos = d->m_lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK;
    if (d->m_flags & (TDEFL_RLE_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS))
    {
      if ((d->m_dict_size) && (!(d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS)))
      {
        mz_uint8 c = d->m_dict[(cur_pos - 1) & TDEFL_LZ_DICT_SIZE_MASK];
        cur_match_len = 0; while (cur_match_len < d->m_lookahead_size) { if (d->m_dict[cur_pos + cur_match_len] != c) break; cur_match_len++; }
        if (cur_match_len < TDEFL_MIN_MATCH_LEN) cur_match_len = 0; else cur_match_dist = 1;
      }
    }
    else
    {
      tdefl_find_match(d, d->m_lookahead_pos, d->m_dict_size, d->m_lookahead_size, &cur_match_dist, &cur_match_len);
    }
    if (((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U*1024U)) || (cur_pos == cur_match_dist) || ((d->m_flags & TDEFL_FILTER_MATCHES) && (cur_match_len <= 5)))
    {
      cur_match_dist = cur_match_len = 0;
    }
    if (d->m_saved_match_len)
    {
      if (cur_match_len > d->m_saved_match_len)
      {
        tdefl_record_literal(d, (mz_uint8)d->m_saved_lit);
        if (cur_match_len >= 128)
        {
          tdefl_record_match(d, cur_match_len, cur_match_dist);
          d->m_saved_match_len = 0; len_to_move = cur_match_len;
        }
        else
        {
          d->m_saved_lit = d->m_dict[cur_pos]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len;
        }
      }
      else
      {
        tdefl_record_match(d, d->m_saved_match_len, d->m_saved_match_dist);
        len_to_move = d->m_saved_match_len - 1; d->m_saved_match_len = 0;
      }
    }
    else if (!cur_match_dist)
      tdefl_record_literal(d, d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]);
    else if ((d->m_greedy_parsing) || (d->m_flags & TDEFL_RLE_MATCHES) || (cur_match_len >= 128))
    {
      tdefl_record_match(d, cur_match_len, cur_match_dist);
      len_to_move = cur_match_len;
    }
    else
    {
      d->m_saved_lit = d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len;
    }
    // Move the lookahead forward by len_to_move bytes.
    d->m_lookahead_pos += len_to_move;
    MZ_ASSERT(d->m_lookahead_size >= len_to_move);
    d->m_lookahead_size -= len_to_move;
    d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, TDEFL_LZ_DICT_SIZE);
    // Check if it's time to flush the current LZ codes to the internal output buffer.
    if ( (d->m_pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) ||
         ( (d->m_total_lz_bytes > 31*1024) && (((((mz_uint)(d->m_pLZ_code_buf - d->m_lz_code_buf) * 115) >> 7) >= d->m_total_lz_bytes) || (d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) )
    {
      int n;
      d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left;
      if ((n = tdefl_flush_block(d, 0)) != 0)
        return (n < 0) ? MZ_FALSE : MZ_TRUE;
    }
  }

  d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left;
  return MZ_TRUE;
}

static tdefl_status tdefl_flush_output_buffer(tdefl_compressor *d)
{
  if (d->m_pIn_buf_size)
  {
    *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf;
  }

  if (d->m_pOut_buf_size)
  {
    size_t n = MZ_MIN(*d->m_pOut_buf_size - d->m_out_buf_ofs, d->m_output_flush_remaining);
    memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf + d->m_output_flush_ofs, n);
    d->m_output_flush_ofs += (mz_uint)n;
    d->m_output_flush_remaining -= (mz_uint)n;
    d->m_out_buf_ofs += n;

    *d->m_pOut_buf_size = d->m_out_buf_ofs;
  }

  return (d->m_finished && !d->m_output_flush_remaining) ? TDEFL_STATUS_DONE : TDEFL_STATUS_OKAY;
}

tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush)
{
  if (!d)
  {
    if (pIn_buf_size) *pIn_buf_size = 0;
    if (pOut_buf_size) *pOut_buf_size = 0;
    return TDEFL_STATUS_BAD_PARAM;
  }

  d->m_pIn_buf = pIn_buf; d->m_pIn_buf_size = pIn_buf_size;
  d->m_pOut_buf = pOut_buf; d->m_pOut_buf_size = pOut_buf_size;
  d->m_pSrc = (const mz_uint8 *)(pIn_buf); d->m_src_buf_left = pIn_buf_size ? *pIn_buf_size : 0;
  d->m_out_buf_ofs = 0;
  d->m_flush = flush;

  if ( ((d->m_pPut_buf_func != NULL) == ((pOut_buf != NULL) || (pOut_buf_size != NULL))) || (d->m_prev_return_status != TDEFL_STATUS_OKAY) ||
        (d->m_wants_to_finish && (flush != TDEFL_FINISH)) || (pIn_buf_size && *pIn_buf_size && !pIn_buf) || (pOut_buf_size && *pOut_buf_size && !pOut_buf) )
  {
    if (pIn_buf_size) *pIn_buf_size = 0;
    if (pOut_buf_size) *pOut_buf_size = 0;
    return (d->m_prev_return_status = TDEFL_STATUS_BAD_PARAM);
  }
  d->m_wants_to_finish |= (flush == TDEFL_FINISH);

  if ((d->m_output_flush_remaining) || (d->m_finished))
    return (d->m_prev_return_status = tdefl_flush_output_buffer(d));

#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN
  if (((d->m_flags & TDEFL_MAX_PROBES_MASK) == 1) &&
      ((d->m_flags & TDEFL_GREEDY_PARSING_FLAG) != 0) &&
      ((d->m_flags & (TDEFL_FILTER_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS | TDEFL_RLE_MATCHES)) == 0))
  {
    if (!tdefl_compress_fast(d))
      return d->m_prev_return_status;
  }
  else
#endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN
  {
    if (!tdefl_compress_normal(d))
      return d->m_prev_return_status;
  }

  if ((d->m_flags & (TDEFL_WRITE_ZLIB_HEADER | TDEFL_COMPUTE_ADLER32)) && (pIn_buf))
    d->m_adler32 = (mz_uint32)mz_adler32(d->m_adler32, (const mz_uint8 *)pIn_buf, d->m_pSrc - (const mz_uint8 *)pIn_buf);

  if ((flush) && (!d->m_lookahead_size) && (!d->m_src_buf_left) && (!d->m_output_flush_remaining))
  {
    if (tdefl_flush_block(d, flush) < 0)
      return d->m_prev_return_status;
    d->m_finished = (flush == TDEFL_FINISH);
    if (flush == TDEFL_FULL_FLUSH) { MZ_CLEAR_OBJ(d->m_hash); MZ_CLEAR_OBJ(d->m_next); d->m_dict_size = 0; }
  }

  return (d->m_prev_return_status = tdefl_flush_output_buffer(d));
}

tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush)
{
  MZ_ASSERT(d->m_pPut_buf_func); return tdefl_compress(d, pIn_buf, &in_buf_size, NULL, NULL, flush);
}

tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags)
{
  d->m_pPut_buf_func = pPut_buf_func; d->m_pPut_buf_user = pPut_buf_user;
  d->m_flags = (mz_uint)(flags); d->m_max_probes[0] = 1 + ((flags & 0xFFF) + 2) / 3; d->m_greedy_parsing = (flags & TDEFL_GREEDY_PARSING_FLAG) != 0;
  d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3;
  if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) MZ_CLEAR_OBJ(d->m_hash);
  d->m_lookahead_pos = d->m_lookahead_size = d->m_dict_size = d->m_total_lz_bytes = d->m_lz_code_buf_dict_pos = d->m_bits_in = 0;
  d->m_output_flush_ofs = d->m_output_flush_remaining = d->m_finished = d->m_block_index = d->m_bit_buffer = d->m_wants_to_finish = 0;
  d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; d->m_num_flags_left = 8;
  d->m_pOutput_buf = d->m_output_buf; d->m_pOutput_buf_end = d->m_output_buf; d->m_prev_return_status = TDEFL_STATUS_OKAY;
  d->m_saved_match_dist = d->m_saved_match_len = d->m_saved_lit = 0; d->m_adler32 = 1;
  d->m_pIn_buf = NULL; d->m_pOut_buf = NULL;
  d->m_pIn_buf_size = NULL; d->m_pOut_buf_size = NULL;
  d->m_flush = TDEFL_NO_FLUSH; d->m_pSrc = NULL; d->m_src_buf_left = 0; d->m_out_buf_ofs = 0;
  memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0);
  memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1);
  return TDEFL_STATUS_OKAY;
}

tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d)
{
  return d->m_prev_return_status;
}

mz_uint32 tdefl_get_adler32(tdefl_compressor *d)
{
  return d->m_adler32;
}

mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags)
{
  tdefl_compressor *pComp; mz_bool succeeded; if (((buf_len) && (!pBuf)) || (!pPut_buf_func)) return MZ_FALSE;
  pComp = (tdefl_compressor*)MZ_MALLOC(sizeof(tdefl_compressor)); if (!pComp) return MZ_FALSE;
  succeeded = (tdefl_init(pComp, pPut_buf_func, pPut_buf_user, flags) == TDEFL_STATUS_OKAY);
  succeeded = succeeded && (tdefl_compress_buffer(pComp, pBuf, buf_len, TDEFL_FINISH) == TDEFL_STATUS_DONE);
  MZ_FREE(pComp); return succeeded;
}

typedef struct
{
  size_t m_size, m_capacity;
  mz_uint8 *m_pBuf;
  mz_bool m_expandable;
} tdefl_output_buffer;

static mz_bool tdefl_output_buffer_putter(const void *pBuf, int len, void *pUser)
{
  tdefl_output_buffer *p = (tdefl_output_buffer *)pUser;
  size_t new_size = p->m_size + len;
  if (new_size > p->m_capacity)
  {
    size_t new_capacity = p->m_capacity; mz_uint8 *pNew_buf; if (!p->m_expandable) return MZ_FALSE;
    do { new_capacity = MZ_MAX(128U, new_capacity << 1U); } while (new_size > new_capacity);
    pNew_buf = (mz_uint8*)MZ_REALLOC(p->m_pBuf, new_capacity); if (!pNew_buf) return MZ_FALSE;
    p->m_pBuf = pNew_buf; p->m_capacity = new_capacity;
  }
  memcpy((mz_uint8*)p->m_pBuf + p->m_size, pBuf, len); p->m_size = new_size;
  return MZ_TRUE;
}

void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags)
{
  tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf);
  if (!pOut_len) return MZ_FALSE; else *pOut_len = 0;
  out_buf.m_expandable = MZ_TRUE;
  if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return NULL;
  *pOut_len = out_buf.m_size; return out_buf.m_pBuf;
}

size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags)
{
  tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf);
  if (!pOut_buf) return 0;
  out_buf.m_pBuf = (mz_uint8*)pOut_buf; out_buf.m_capacity = out_buf_len;
  if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return 0;
  return out_buf.m_size;
}

#ifndef MINIZ_NO_ZLIB_APIS
static const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32,  16, 32, 128, 256,  512, 768, 1500 };

// level may actually range from [0,10] (10 is a "hidden" max level, where we want a bit more compression and it's fine if throughput to fall off a cliff on some files).
mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy)
{
  mz_uint comp_flags = s_tdefl_num_probes[(level >= 0) ? MZ_MIN(10, level) : MZ_DEFAULT_LEVEL] | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0);
  if (window_bits > 0) comp_flags |= TDEFL_WRITE_ZLIB_HEADER;

  if (!level) comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS;
  else if (strategy == MZ_FILTERED) comp_flags |= TDEFL_FILTER_MATCHES;
  else if (strategy == MZ_HUFFMAN_ONLY) comp_flags &= ~TDEFL_MAX_PROBES_MASK;
  else if (strategy == MZ_FIXED) comp_flags |= TDEFL_FORCE_ALL_STATIC_BLOCKS;
  else if (strategy == MZ_RLE) comp_flags |= TDEFL_RLE_MATCHES;

  return comp_flags;
}
#endif //MINIZ_NO_ZLIB_APIS

#ifdef _MSC_VER
#pragma warning (push)
#pragma warning (disable:4204) // nonstandard extension used : non-constant aggregate initializer (also supported by GNU C and C99, so no big deal)
#endif

// Simple PNG writer function by Alex Evans, 2011. Released into the public domain: https://gist.github.com/908299, more context at
// http://altdevblogaday.org/2011/04/06/a-smaller-jpg-encoder/.
// This is actually a modification of Alex's original code so PNG files generated by this function pass pngcheck.
void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip)
{
  // Using a local copy of this array here in case MINIZ_NO_ZLIB_APIS was defined.
  static const mz_uint s_tdefl_png_num_probes[11] = { 0, 1, 6, 32,  16, 32, 128, 256,  512, 768, 1500 };
  tdefl_compressor *pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); tdefl_output_buffer out_buf; int i, bpl = w * num_chans, y, z; mz_uint32 c; *pLen_out = 0;
  if (!pComp) return NULL;
  MZ_CLEAR_OBJ(out_buf); out_buf.m_expandable = MZ_TRUE; out_buf.m_capacity = 57+MZ_MAX(64, (1+bpl)*h); if (NULL == (out_buf.m_pBuf = (mz_uint8*)MZ_MALLOC(out_buf.m_capacity))) { MZ_FREE(pComp); return NULL; }
  // write dummy header
  for (z = 41; z; --z) tdefl_output_buffer_putter(&z, 1, &out_buf);
  // compress image data
  tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, s_tdefl_png_num_probes[MZ_MIN(10, level)] | TDEFL_WRITE_ZLIB_HEADER);
  for (y = 0; y < h; ++y) { tdefl_compress_buffer(pComp, &z, 1, TDEFL_NO_FLUSH); tdefl_compress_buffer(pComp, (mz_uint8*)pImage + (flip ? (h - 1 - y) : y) * bpl, bpl, TDEFL_NO_FLUSH); }
  if (tdefl_compress_buffer(pComp, NULL, 0, TDEFL_FINISH) != TDEFL_STATUS_DONE) { MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; }
  // write real header
  *pLen_out = out_buf.m_size-41;
  {
    static const mz_uint8 chans[] = {0x00, 0x00, 0x04, 0x02, 0x06};
    mz_uint8 pnghdr[41]={0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a,0x00,0x00,0x00,0x0d,0x49,0x48,0x44,0x52,
      0,0,(mz_uint8)(w>>8),(mz_uint8)w,0,0,(mz_uint8)(h>>8),(mz_uint8)h,8,chans[num_chans],0,0,0,0,0,0,0,
      (mz_uint8)(*pLen_out>>24),(mz_uint8)(*pLen_out>>16),(mz_uint8)(*pLen_out>>8),(mz_uint8)*pLen_out,0x49,0x44,0x41,0x54};
    c=(mz_uint32)mz_crc32(MZ_CRC32_INIT,pnghdr+12,17); for (i=0; i<4; ++i, c<<=8) ((mz_uint8*)(pnghdr+29))[i]=(mz_uint8)(c>>24);
    memcpy(out_buf.m_pBuf, pnghdr, 41);
  }
  // write footer (IDAT CRC-32, followed by IEND chunk)
  if (!tdefl_output_buffer_putter("\0\0\0\0\0\0\0\0\x49\x45\x4e\x44\xae\x42\x60\x82", 16, &out_buf)) { *pLen_out = 0; MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; }
  c = (mz_uint32)mz_crc32(MZ_CRC32_INIT,out_buf.m_pBuf+41-4, *pLen_out+4); for (i=0; i<4; ++i, c<<=8) (out_buf.m_pBuf+out_buf.m_size-16)[i] = (mz_uint8)(c >> 24);
  // compute final size of file, grab compressed data buffer and return
  *pLen_out += 57; MZ_FREE(pComp); return out_buf.m_pBuf;
}
void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out)
{
  // Level 6 corresponds to TDEFL_DEFAULT_MAX_PROBES or MZ_DEFAULT_LEVEL (but we can't depend on MZ_DEFAULT_LEVEL being available in case the zlib API's where #defined out)
  return tdefl_write_image_to_png_file_in_memory_ex(pImage, w, h, num_chans, pLen_out, 6, MZ_FALSE);
}

#ifdef _MSC_VER
#pragma warning (pop)
#endif

// ------------------- .ZIP archive reading

#ifndef MINIZ_NO_ARCHIVE_APIS

#ifdef MINIZ_NO_STDIO
  #define MZ_FILE void *
#else
  #include <stdio.h>
  #include <sys/stat.h>

  #if defined(_MSC_VER) || defined(__MINGW64__)
    static FILE *mz_fopen(const char *pFilename, const char *pMode)
    {
      FILE* pFile = NULL;
      fopen_s(&pFile, pFilename, pMode);
      return pFile;
    }
    static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream)
    {
      FILE* pFile = NULL;
      if (freopen_s(&pFile, pPath, pMode, pStream))
        return NULL;
      return pFile;
    }
    #ifndef MINIZ_NO_TIME
      #include <sys/utime.h>
    #endif
    #define MZ_FILE FILE
    #define MZ_FOPEN mz_fopen
    #define MZ_FCLOSE fclose
    #define MZ_FREAD fread
    #define MZ_FWRITE fwrite
    #define MZ_FTELL64 _ftelli64
    #define MZ_FSEEK64 _fseeki64
    #define MZ_FILE_STAT_STRUCT _stat
    #define MZ_FILE_STAT _stat
    #define MZ_FFLUSH fflush
    #define MZ_FREOPEN mz_freopen
    #define MZ_DELETE_FILE remove
  #elif defined(__MINGW32__)
    #ifndef MINIZ_NO_TIME
      #include <sys/utime.h>
    #endif
    #define MZ_FILE FILE
    #define MZ_FOPEN(f, m) fopen(f, m)
    #define MZ_FCLOSE fclose
    #define MZ_FREAD fread
    #define MZ_FWRITE fwrite
    #define MZ_FTELL64 ftello64
    #define MZ_FSEEK64 fseeko64
    #define MZ_FILE_STAT_STRUCT _stat
    #define MZ_FILE_STAT _stat
    #define MZ_FFLUSH fflush
    #define MZ_FREOPEN(f, m, s) freopen(f, m, s)
    #define MZ_DELETE_FILE remove
  #elif defined(__TINYC__)
    #ifndef MINIZ_NO_TIME
      #include <sys/utime.h>
    #endif
    #define MZ_FILE FILE
    #define MZ_FOPEN(f, m) fopen(f, m)
    #define MZ_FCLOSE fclose
    #define MZ_FREAD fread
    #define MZ_FWRITE fwrite
    #define MZ_FTELL64 ftell
    #define MZ_FSEEK64 fseek
    #define MZ_FILE_STAT_STRUCT stat
    #define MZ_FILE_STAT stat
    #define MZ_FFLUSH fflush
    #define MZ_FREOPEN(f, m, s) freopen(f, m, s)
    #define MZ_DELETE_FILE remove
  #elif defined(__GNUC__) && _LARGEFILE64_SOURCE
    #ifndef MINIZ_NO_TIME
      #include <utime.h>
    #endif
    #define MZ_FILE FILE
    #define MZ_FOPEN(f, m) fopen64(f, m)
    #define MZ_FCLOSE fclose
    #define MZ_FREAD fread
    #define MZ_FWRITE fwrite
    #define MZ_FTELL64 ftello64
    #define MZ_FSEEK64 fseeko64
    #define MZ_FILE_STAT_STRUCT stat64
    #define MZ_FILE_STAT stat64
    #define MZ_FFLUSH fflush
    #define MZ_FREOPEN(p, m, s) freopen64(p, m, s)
    #define MZ_DELETE_FILE remove
  #else
    #ifndef MINIZ_NO_TIME
      #include <utime.h>
    #endif
    #define MZ_FILE FILE
    #define MZ_FOPEN(f, m) fopen(f, m)
    #define MZ_FCLOSE fclose
    #define MZ_FREAD fread
    #define MZ_FWRITE fwrite
    #define MZ_FTELL64 ftello
    #define MZ_FSEEK64 fseeko
    #define MZ_FILE_STAT_STRUCT stat
    #define MZ_FILE_STAT stat
    #define MZ_FFLUSH fflush
    #define MZ_FREOPEN(f, m, s) freopen(f, m, s)
    #define MZ_DELETE_FILE remove
  #endif // #ifdef _MSC_VER
#endif // #ifdef MINIZ_NO_STDIO

#define MZ_TOLOWER(c) ((((c) >= 'A') && ((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c))

// Various ZIP archive enums. To completely avoid cross platform compiler alignment and platform endian issues, miniz.c doesn't use structs for any of this stuff.
enum
{
  // ZIP archive identifiers and record sizes
  MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06054b50, MZ_ZIP_CENTRAL_DIR_HEADER_SIG = 0x02014b50, MZ_ZIP_LOCAL_DIR_HEADER_SIG = 0x04034b50,
  MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22,
  // Central directory header record offsets
  MZ_ZIP_CDH_SIG_OFS = 0, MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4, MZ_ZIP_CDH_VERSION_NEEDED_OFS = 6, MZ_ZIP_CDH_BIT_FLAG_OFS = 8,
  MZ_ZIP_CDH_METHOD_OFS = 10, MZ_ZIP_CDH_FILE_TIME_OFS = 12, MZ_ZIP_CDH_FILE_DATE_OFS = 14, MZ_ZIP_CDH_CRC32_OFS = 16,
  MZ_ZIP_CDH_COMPRESSED_SIZE_OFS = 20, MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS = 24, MZ_ZIP_CDH_FILENAME_LEN_OFS = 28, MZ_ZIP_CDH_EXTRA_LEN_OFS = 30,
  MZ_ZIP_CDH_COMMENT_LEN_OFS = 32, MZ_ZIP_CDH_DISK_START_OFS = 34, MZ_ZIP_CDH_INTERNAL_ATTR_OFS = 36, MZ_ZIP_CDH_EXTERNAL_ATTR_OFS = 38, MZ_ZIP_CDH_LOCAL_HEADER_OFS = 42,
  // Local directory header offsets
  MZ_ZIP_LDH_SIG_OFS = 0, MZ_ZIP_LDH_VERSION_NEEDED_OFS = 4, MZ_ZIP_LDH_BIT_FLAG_OFS = 6, MZ_ZIP_LDH_METHOD_OFS = 8, MZ_ZIP_LDH_FILE_TIME_OFS = 10,
  MZ_ZIP_LDH_FILE_DATE_OFS = 12, MZ_ZIP_LDH_CRC32_OFS = 14, MZ_ZIP_LDH_COMPRESSED_SIZE_OFS = 18, MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS = 22,
  MZ_ZIP_LDH_FILENAME_LEN_OFS = 26, MZ_ZIP_LDH_EXTRA_LEN_OFS = 28,
  // End of central directory offsets
  MZ_ZIP_ECDH_SIG_OFS = 0, MZ_ZIP_ECDH_NUM_THIS_DISK_OFS = 4, MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS = 6, MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 8,
  MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS = 10, MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12, MZ_ZIP_ECDH_CDIR_OFS_OFS = 16, MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20,
};

typedef struct
{
  void *m_p;
  size_t m_size, m_capacity;
  mz_uint m_element_size;
} mz_zip_array;

struct mz_zip_internal_state_tag
{
  mz_zip_array m_central_dir;
  mz_zip_array m_central_dir_offsets;
  mz_zip_array m_sorted_central_dir_offsets;
  MZ_FILE *m_pFile;
  void *m_pMem;
  size_t m_mem_size;
  size_t m_mem_capacity;
};

#define MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(array_ptr, element_size) (array_ptr)->m_element_size = element_size
#define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[index]

static MZ_FORCEINLINE void mz_zip_array_clear(mz_zip_archive *pZip, mz_zip_array *pArray)
{
  pZip->m_pFree(pZip->m_pAlloc_opaque, pArray->m_p);
  memset(pArray, 0, sizeof(mz_zip_array));
}

static mz_bool mz_zip_array_ensure_capacity(mz_zip_archive *pZip, mz_zip_array *pArray, size_t min_new_capacity, mz_uint growing)
{
  void *pNew_p; size_t new_capacity = min_new_capacity; MZ_ASSERT(pArray->m_element_size); if (pArray->m_capacity >= min_new_capacity) return MZ_TRUE;
  if (growing) { new_capacity = MZ_MAX(1, pArray->m_capacity); while (new_capacity < min_new_capacity) new_capacity *= 2; }
  if (NULL == (pNew_p = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pArray->m_p, pArray->m_element_size, new_capacity))) return MZ_FALSE;
  pArray->m_p = pNew_p; pArray->m_capacity = new_capacity;
  return MZ_TRUE;
}

static MZ_FORCEINLINE mz_bool mz_zip_array_reserve(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_capacity, mz_uint growing)
{
  if (new_capacity > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_capacity, growing)) return MZ_FALSE; }
  return MZ_TRUE;
}

static MZ_FORCEINLINE mz_bool mz_zip_array_resize(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_size, mz_uint growing)
{
  if (new_size > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_size, growing)) return MZ_FALSE; }
  pArray->m_size = new_size;
  return MZ_TRUE;
}

static MZ_FORCEINLINE mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, mz_zip_array *pArray, size_t n)
{
  return mz_zip_array_reserve(pZip, pArray, pArray->m_size + n, MZ_TRUE);
}

static MZ_FORCEINLINE mz_bool mz_zip_array_push_back(mz_zip_archive *pZip, mz_zip_array *pArray, const void *pElements, size_t n)
{
  size_t orig_size = pArray->m_size; if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE)) return MZ_FALSE;
  memcpy((mz_uint8*)pArray->m_p + orig_size * pArray->m_element_size, pElements, n * pArray->m_element_size);
  return MZ_TRUE;
}

#ifndef MINIZ_NO_TIME
static time_t mz_zip_dos_to_time_t(int dos_time, int dos_date)
{
  struct tm tm;
  memset(&tm, 0, sizeof(tm)); tm.tm_isdst = -1;
  tm.tm_year = ((dos_date >> 9) & 127) + 1980 - 1900; tm.tm_mon = ((dos_date >> 5) & 15) - 1; tm.tm_mday = dos_date & 31;
  tm.tm_hour = (dos_time >> 11) & 31; tm.tm_min = (dos_time >> 5) & 63; tm.tm_sec = (dos_time << 1) & 62;
  return mktime(&tm);
}

static void mz_zip_time_to_dos_time(time_t time, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date)
{
#ifdef _MSC_VER
  struct tm tm_struct;
  struct tm *tm = &tm_struct;
  errno_t err = localtime_s(tm, &time);
  if (err)
  {
    *pDOS_date = 0; *pDOS_time = 0;
    return;
  }
#else
  struct tm *tm = localtime(&time);
#endif
  *pDOS_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + ((tm->tm_sec) >> 1));
  *pDOS_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + ((tm->tm_mon + 1) << 5) + tm->tm_mday);
}
#endif

#ifndef MINIZ_NO_STDIO
static mz_bool mz_zip_get_file_modified_time(const char *pFilename, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date)
{
#ifdef MINIZ_NO_TIME
  (void)pFilename; *pDOS_date = *pDOS_time = 0;
#else
  struct MZ_FILE_STAT_STRUCT file_stat;
  // On Linux with x86 glibc, this call will fail on large files (>= 0x80000000 bytes) unless you compiled with _LARGEFILE64_SOURCE. Argh.
  if (MZ_FILE_STAT(pFilename, &file_stat) != 0)
    return MZ_FALSE;
  mz_zip_time_to_dos_time(file_stat.st_mtime, pDOS_time, pDOS_date);
#endif // #ifdef MINIZ_NO_TIME
  return MZ_TRUE;
}

#ifndef MINIZ_NO_TIME
static mz_bool mz_zip_set_file_times(const char *pFilename, time_t access_time, time_t modified_time)
{
  struct utimbuf t; t.actime = access_time; t.modtime = modified_time;
  return !utime(pFilename, &t);
}
#endif // #ifndef MINIZ_NO_TIME
#endif // #ifndef MINIZ_NO_STDIO

static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip, mz_uint32 flags)
{
  (void)flags;
  if ((!pZip) || (pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID))
    return MZ_FALSE;

  if (!pZip->m_pAlloc) pZip->m_pAlloc = def_alloc_func;
  if (!pZip->m_pFree) pZip->m_pFree = def_free_func;
  if (!pZip->m_pRealloc) pZip->m_pRealloc = def_realloc_func;

  pZip->m_zip_mode = MZ_ZIP_MODE_READING;
  pZip->m_archive_size = 0;
  pZip->m_central_directory_file_ofs = 0;
  pZip->m_total_files = 0;

  if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state))))
    return MZ_FALSE;
  memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state));
  MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8));
  MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32));
  MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32));
  return MZ_TRUE;
}

static MZ_FORCEINLINE mz_bool mz_zip_reader_filename_less(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, mz_uint r_index)
{
  const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE;
  const mz_uint8 *pR = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, r_index));
  mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS), r_len = MZ_READ_LE16(pR + MZ_ZIP_CDH_FILENAME_LEN_OFS);
  mz_uint8 l = 0, r = 0;
  pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; pR += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE;
  pE = pL + MZ_MIN(l_len, r_len);
  while (pL < pE)
  {
    if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR)))
      break;
    pL++; pR++;
  }
  return (pL == pE) ? (l_len < r_len) : (l < r);
}

#define MZ_SWAP_UINT32(a, b) do { mz_uint32 t = a; a = b; b = t; } MZ_MACRO_END

// Heap sort of lowercased filenames, used to help accelerate plain central directory searches by mz_zip_reader_locate_file(). (Could also use qsort(), but it could allocate memory.)
static void mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive *pZip)
{
  mz_zip_internal_state *pState = pZip->m_pState;
  const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets;
  const mz_zip_array *pCentral_dir = &pState->m_central_dir;
  mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0);
  const int size = pZip->m_total_files;
  int start = (size - 2) >> 1, end;
  while (start >= 0)
  {
    int child, root = start;
    for ( ; ; )
    {
      if ((child = (root << 1) + 1) >= size)
        break;
      child += (((child + 1) < size) && (mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1])));
      if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child]))
        break;
      MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child;
    }
    start--;
  }

  end = size - 1;
  while (end > 0)
  {
    int child, root = 0;
    MZ_SWAP_UINT32(pIndices[end], pIndices[0]);
    for ( ; ; )
    {
      if ((child = (root << 1) + 1) >= end)
        break;
      child += (((child + 1) < end) && mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1]));
      if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child]))
        break;
      MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child;
    }
    end--;
  }
}

static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint32 flags)
{
  mz_uint cdir_size, num_this_disk, cdir_disk_index;
  mz_uint64 cdir_ofs;
  mz_int64 cur_file_ofs;
  const mz_uint8 *p;
  mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; mz_uint8 *pBuf = (mz_uint8 *)buf_u32;
  mz_bool sort_central_dir = ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0);
  // Basic sanity checks - reject files which are too small, and check the first 4 bytes of the file to make sure a local header is there.
  if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)
    return MZ_FALSE;
  // Find the end of central directory record by scanning the file from the end towards the beginning.
  cur_file_ofs = MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0);
  for ( ; ; )
  {
    int i, n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs);
    if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n)
      return MZ_FALSE;
    for (i = n - 4; i >= 0; --i)
      if (MZ_READ_LE32(pBuf + i) == MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG)
        break;
    if (i >= 0)
    {
      cur_file_ofs += i;
      break;
    }
    if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >= (0xFFFF + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)))
      return MZ_FALSE;
    cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0);
  }
  // Read and verify the end of central directory record.
  if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)
    return MZ_FALSE;
  if ((MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) ||
      ((pZip->m_total_files = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS)) != MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS)))
    return MZ_FALSE;

  num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS);
  cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS);
  if (((num_this_disk | cdir_disk_index) != 0) && ((num_this_disk != 1) || (cdir_disk_index != 1)))
    return MZ_FALSE;

  if ((cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS)) < pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)
    return MZ_FALSE;

  cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS);
  if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size)
    return MZ_FALSE;

  pZip->m_central_directory_file_ofs = cdir_ofs;

  if (pZip->m_total_files)
  {
     mz_uint i, n;

    // Read the entire central directory into a heap block, and allocate another heap block to hold the unsorted central dir file record offsets, and another to hold the sorted indices.
    if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size, MZ_FALSE)) ||
        (!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets, pZip->m_total_files, MZ_FALSE)))
      return MZ_FALSE;

    if (sort_central_dir)
    {
      if (!mz_zip_array_resize(pZip, &pZip->m_pState->m_sorted_central_dir_offsets, pZip->m_total_files, MZ_FALSE))
        return MZ_FALSE;
    }

    if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs, pZip->m_pState->m_central_dir.m_p, cdir_size) != cdir_size)
      return MZ_FALSE;

    // Now create an index into the central directory file records, do some basic sanity checking on each record, and check for zip64 entries (which are not yet supported).
    p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p;
    for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i)
    {
      mz_uint total_header_size, comp_size, decomp_size, disk_index;
      if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) || (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG))
        return MZ_FALSE;
      MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, i) = (mz_uint32)(p - (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p);
      if (sort_central_dir)
        MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets, mz_uint32, i) = i;
      comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS);
      decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS);
      if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) && (decomp_size != comp_size)) || (decomp_size && !comp_size) || (decomp_size == 0xFFFFFFFF) || (comp_size == 0xFFFFFFFF))
        return MZ_FALSE;
      disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS);
      if ((disk_index != num_this_disk) && (disk_index != 1))
        return MZ_FALSE;
      if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size)
        return MZ_FALSE;
      if ((total_header_size = MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS)) > n)
        return MZ_FALSE;
      n -= total_header_size; p += total_header_size;
    }
  }

  if (sort_central_dir)
    mz_zip_reader_sort_central_dir_offsets_by_filename(pZip);

  return MZ_TRUE;
}

mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint32 flags)
{
  if ((!pZip) || (!pZip->m_pRead))
    return MZ_FALSE;
  if (!mz_zip_reader_init_internal(pZip, flags))
    return MZ_FALSE;
  pZip->m_archive_size = size;
  if (!mz_zip_reader_read_central_dir(pZip, flags))
  {
    mz_zip_reader_end(pZip);
    return MZ_FALSE;
  }
  return MZ_TRUE;
}

static size_t mz_zip_mem_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n)
{
  mz_zip_archive *pZip = (mz_zip_archive *)pOpaque;
  size_t s = (file_ofs >= pZip->m_archive_size) ? 0 : (size_t)MZ_MIN(pZip->m_archive_size - file_ofs, n);
  memcpy(pBuf, (const mz_uint8 *)pZip->m_pState->m_pMem + file_ofs, s);
  return s;
}

mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint32 flags)
{
  if (!mz_zip_reader_init_internal(pZip, flags))
    return MZ_FALSE;
  pZip->m_archive_size = size;
  pZip->m_pRead = mz_zip_mem_read_func;
  pZip->m_pIO_opaque = pZip;
#ifdef __cplusplus
  pZip->m_pState->m_pMem = const_cast<void *>(pMem);
#else
  pZip->m_pState->m_pMem = (void *)pMem;
#endif
  pZip->m_pState->m_mem_size = size;
  if (!mz_zip_reader_read_central_dir(pZip, flags))
  {
    mz_zip_reader_end(pZip);
    return MZ_FALSE;
  }
  return MZ_TRUE;
}

#ifndef MINIZ_NO_STDIO
static size_t mz_zip_file_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n)
{
  mz_zip_archive *pZip = (mz_zip_archive *)pOpaque;
  mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile);
  if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET))))
    return 0;
  return MZ_FREAD(pBuf, 1, n, pZip->m_pState->m_pFile);
}

mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags)
{
  mz_uint64 file_size;
  MZ_FILE *pFile = MZ_FOPEN(pFilename, "rb");
  if (!pFile)
    return MZ_FALSE;
  if (MZ_FSEEK64(pFile, 0, SEEK_END))
  {
    MZ_FCLOSE(pFile);
    return MZ_FALSE;
  }
  file_size = MZ_FTELL64(pFile);
  if (!mz_zip_reader_init_internal(pZip, flags))
  {
    MZ_FCLOSE(pFile);
    return MZ_FALSE;
  }
  pZip->m_pRead = mz_zip_file_read_func;
  pZip->m_pIO_opaque = pZip;
  pZip->m_pState->m_pFile = pFile;
  pZip->m_archive_size = file_size;
  if (!mz_zip_reader_read_central_dir(pZip, flags))
  {
    mz_zip_reader_end(pZip);
    return MZ_FALSE;
  }
  return MZ_TRUE;
}
#endif // #ifndef MINIZ_NO_STDIO

mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip)
{
  return pZip ? pZip->m_total_files : 0;
}

static MZ_FORCEINLINE const mz_uint8 *mz_zip_reader_get_cdh(mz_zip_archive *pZip, mz_uint file_index)
{
  if ((!pZip) || (!pZip->m_pState) || (file_index >= pZip->m_total_files) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING))
    return NULL;
  return &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index));
}

mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index)
{
  mz_uint m_bit_flag;
  const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index);
  if (!p)
    return MZ_FALSE;
  m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS);
  return (m_bit_flag & 1);
}

mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index)
{
  mz_uint filename_len, external_attr;
  const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index);
  if (!p)
    return MZ_FALSE;

  // First see if the filename ends with a '/' character.
  filename_len = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS);
  if (filename_len)
  {
    if (*(p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_len - 1) == '/')
      return MZ_TRUE;
  }

  // Bugfix: This code was also checking if the internal attribute was non-zero, which wasn't correct.
  // Most/all zip writers (hopefully) set DOS file/directory attributes in the low 16-bits, so check for the DOS directory flag and ignore the source OS ID in the created by field.
  // FIXME: Remove this check? Is it necessary - we already check the filename.
  external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS);
  if ((external_attr & 0x10) != 0)
    return MZ_TRUE;

  return MZ_FALSE;
}

mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat)
{
  mz_uint n;
  const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index);
  if ((!p) || (!pStat))
    return MZ_FALSE;

  // Unpack the central directory record.
  pStat->m_file_index = file_index;
  pStat->m_central_dir_ofs = MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index);
  pStat->m_version_made_by = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS);
  pStat->m_version_needed = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_NEEDED_OFS);
  pStat->m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS);
  pStat->m_method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS);
#ifndef MINIZ_NO_TIME
  pStat->m_time = mz_zip_dos_to_time_t(MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_TIME_OFS), MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_DATE_OFS));
#endif
  pStat->m_crc32 = MZ_READ_LE32(p + MZ_ZIP_CDH_CRC32_OFS);
  pStat->m_comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS);
  pStat->m_uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS);
  pStat->m_internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS);
  pStat->m_external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS);
  pStat->m_local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS);

  // Copy as much of the filename and comment as possible.
  n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - 1);
  memcpy(pStat->m_filename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); pStat->m_filename[n] = '\0';

  n = MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE - 1);
  pStat->m_comment_size = n;
  memcpy(pStat->m_comment, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS), n); pStat->m_comment[n] = '\0';

  return MZ_TRUE;
}

mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size)
{
  mz_uint n;
  const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index);
  if (!p) { if (filename_buf_size) pFilename[0] = '\0'; return 0; }
  n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS);
  if (filename_buf_size)
  {
    n = MZ_MIN(n, filename_buf_size - 1);
    memcpy(pFilename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n);
    pFilename[n] = '\0';
  }
  return n + 1;
}

static MZ_FORCEINLINE mz_bool mz_zip_reader_string_equal(const char *pA, const char *pB, mz_uint len, mz_uint flags)
{
  mz_uint i;
  if (flags & MZ_ZIP_FLAG_CASE_SENSITIVE)
    return 0 == memcmp(pA, pB, len);
  for (i = 0; i < len; ++i)
    if (MZ_TOLOWER(pA[i]) != MZ_TOLOWER(pB[i]))
      return MZ_FALSE;
  return MZ_TRUE;
}

static MZ_FORCEINLINE int mz_zip_reader_filename_compare(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, const char *pR, mz_uint r_len)
{
  const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE;
  mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS);
  mz_uint8 l = 0, r = 0;
  pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE;
  pE = pL + MZ_MIN(l_len, r_len);
  while (pL < pE)
  {
    if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR)))
      break;
    pL++; pR++;
  }
  return (pL == pE) ? (int)(l_len - r_len) : (l - r);
}

static int mz_zip_reader_locate_file_binary_search(mz_zip_archive *pZip, const char *pFilename)
{
  mz_zip_internal_state *pState = pZip->m_pState;
  const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets;
  const mz_zip_array *pCentral_dir = &pState->m_central_dir;
  mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0);
  const int size = pZip->m_total_files;
  const mz_uint filename_len = (mz_uint)strlen(pFilename);
  int l = 0, h = size - 1;
  while (l <= h)
  {
    int m = (l + h) >> 1, file_index = pIndices[m], comp = mz_zip_reader_filename_compare(pCentral_dir, pCentral_dir_offsets, file_index, pFilename, filename_len);
    if (!comp)
      return file_index;
    else if (comp < 0)
      l = m + 1;
    else
      h = m - 1;
  }
  return -1;
}

int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags)
{
  mz_uint file_index; size_t name_len, comment_len;
  if ((!pZip) || (!pZip->m_pState) || (!pName) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING))
    return -1;
  if (((flags & (MZ_ZIP_FLAG_IGNORE_PATH | MZ_ZIP_FLAG_CASE_SENSITIVE)) == 0) && (!pComment) && (pZip->m_pState->m_sorted_central_dir_offsets.m_size))
    return mz_zip_reader_locate_file_binary_search(pZip, pName);
  name_len = strlen(pName); if (name_len > 0xFFFF) return -1;
  comment_len = pComment ? strlen(pComment) : 0; if (comment_len > 0xFFFF) return -1;
  for (file_index = 0; file_index < pZip->m_total_files; file_index++)
  {
    const mz_uint8 *pHeader = &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index));
    mz_uint filename_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS);
    const char *pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE;
    if (filename_len < name_len)
      continue;
    if (comment_len)
    {
      mz_uint file_extra_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_EXTRA_LEN_OFS), file_comment_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_COMMENT_LEN_OFS);
      const char *pFile_comment = pFilename + filename_len + file_extra_len;
      if ((file_comment_len != comment_len) || (!mz_zip_reader_string_equal(pComment, pFile_comment, file_comment_len, flags)))
        continue;
    }
    if ((flags & MZ_ZIP_FLAG_IGNORE_PATH) && (filename_len))
    {
      int ofs = filename_len - 1;
      do
      {
        if ((pFilename[ofs] == '/') || (pFilename[ofs] == '\\') || (pFilename[ofs] == ':'))
          break;
      } while (--ofs >= 0);
      ofs++;
      pFilename += ofs; filename_len -= ofs;
    }
    if ((filename_len == name_len) && (mz_zip_reader_string_equal(pName, pFilename, filename_len, flags)))
      return file_index;
  }
  return -1;
}

mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size)
{
  int status = TINFL_STATUS_DONE;
  mz_uint64 needed_size, cur_file_ofs, comp_remaining, out_buf_ofs = 0, read_buf_size, read_buf_ofs = 0, read_buf_avail;
  mz_zip_archive_file_stat file_stat;
  void *pRead_buf;
  mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32;
  tinfl_decompressor inflator;

  if ((buf_size) && (!pBuf))
    return MZ_FALSE;

  if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat))
    return MZ_FALSE;

  // Empty file, or a directory (but not always a directory - I've seen odd zips with directories that have compressed data which inflates to 0 bytes)
  if (!file_stat.m_comp_size)
    return MZ_TRUE;

  // Entry is a subdirectory (I've seen old zips with dir entries which have compressed deflate data which inflates to 0 bytes, but these entries claim to uncompress to 512 bytes in the headers).
  // I'm torn how to handle this case - should it fail instead?
  if (mz_zip_reader_is_file_a_directory(pZip, file_index))
    return MZ_TRUE;

  // Encryption and patch files are not supported.
  if (file_stat.m_bit_flag & (1 | 32))
    return MZ_FALSE;

  // This function only supports stored and deflate.
  if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED))
    return MZ_FALSE;

  // Ensure supplied output buffer is large enough.
  needed_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size;
  if (buf_size < needed_size)
    return MZ_FALSE;

  // Read and parse the local directory entry.
  cur_file_ofs = file_stat.m_local_header_ofs;
  if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE)
    return MZ_FALSE;
  if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG)
    return MZ_FALSE;

  cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS);
  if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size)
    return MZ_FALSE;

  if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method))
  {
    // The file is stored or the caller has requested the compressed data.
    if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, (size_t)needed_size) != needed_size)
      return MZ_FALSE;
    return ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) != 0) || (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) == file_stat.m_crc32);
  }

  // Decompress the file either directly from memory or from a file input buffer.
  tinfl_init(&inflator);

  if (pZip->m_pState->m_pMem)
  {
    // Read directly from the archive in memory.
    pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs;
    read_buf_size = read_buf_avail = file_stat.m_comp_size;
    comp_remaining = 0;
  }
  else if (pUser_read_buf)
  {
    // Use a user provided read buffer.
    if (!user_read_buf_size)
      return MZ_FALSE;
    pRead_buf = (mz_uint8 *)pUser_read_buf;
    read_buf_size = user_read_buf_size;
    read_buf_avail = 0;
    comp_remaining = file_stat.m_comp_size;
  }
  else
  {
    // Temporarily allocate a read buffer.
    read_buf_size = MZ_MIN(file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE);
#ifdef _MSC_VER
    if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF))
#else
    if (((sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF))
#endif
      return MZ_FALSE;
    if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size)))
      return MZ_FALSE;
    read_buf_avail = 0;
    comp_remaining = file_stat.m_comp_size;
  }

  do
  {
    size_t in_buf_size, out_buf_size = (size_t)(file_stat.m_uncomp_size - out_buf_ofs);
    if ((!read_buf_avail) && (!pZip->m_pState->m_pMem))
    {
      read_buf_avail = MZ_MIN(read_buf_size, comp_remaining);
      if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail)
      {
        status = TINFL_STATUS_FAILED;
        break;
      }
      cur_file_ofs += read_buf_avail;
      comp_remaining -= read_buf_avail;
      read_buf_ofs = 0;
    }
    in_buf_size = (size_t)read_buf_avail;
    status = tinfl_decompress(&inflator, (mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pBuf, (mz_uint8 *)pBuf + out_buf_ofs, &out_buf_size, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | (comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0));
    read_buf_avail -= in_buf_size;
    read_buf_ofs += in_buf_size;
    out_buf_ofs += out_buf_size;
  } while (status == TINFL_STATUS_NEEDS_MORE_INPUT);

  if (status == TINFL_STATUS_DONE)
  {
    // Make sure the entire file was decompressed, and check its CRC.
    if ((out_buf_ofs != file_stat.m_uncomp_size) || (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32))
      status = TINFL_STATUS_FAILED;
  }

  if ((!pZip->m_pState->m_pMem) && (!pUser_read_buf))
    pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);

  return status == TINFL_STATUS_DONE;
}

mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size)
{
  int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags);
  if (file_index < 0)
    return MZ_FALSE;
  return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size);
}

mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags)
{
  return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, NULL, 0);
}

mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags)
{
  return mz_zip_reader_extract_file_to_mem_no_alloc(pZip, pFilename, pBuf, buf_size, flags, NULL, 0);
}

void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags)
{
  mz_uint64 comp_size, uncomp_size, alloc_size;
  const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index);
  void *pBuf;

  if (pSize)
    *pSize = 0;
  if (!p)
    return NULL;

  comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS);
  uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS);

  alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? comp_size : uncomp_size;
#ifdef _MSC_VER
  if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF))
#else
  if (((sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF))
#endif
    return NULL;
  if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)alloc_size)))
    return NULL;

  if (!mz_zip_reader_extract_to_mem(pZip, file_index, pBuf, (size_t)alloc_size, flags))
  {
    pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
    return NULL;
  }

  if (pSize) *pSize = (size_t)alloc_size;
  return pBuf;
}

void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags)
{
  int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags);
  if (file_index < 0)
  {
    if (pSize) *pSize = 0;
    return MZ_FALSE;
  }
  return mz_zip_reader_extract_to_heap(pZip, file_index, pSize, flags);
}

mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags)
{
  int status = TINFL_STATUS_DONE; mz_uint file_crc32 = MZ_CRC32_INIT;
  mz_uint64 read_buf_size, read_buf_ofs = 0, read_buf_avail, comp_remaining, out_buf_ofs = 0, cur_file_ofs;
  mz_zip_archive_file_stat file_stat;
  void *pRead_buf = NULL; void *pWrite_buf = NULL;
  mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32;

  if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat))
    return MZ_FALSE;

  // Empty file, or a directory (but not always a directory - I've seen odd zips with directories that have compressed data which inflates to 0 bytes)
  if (!file_stat.m_comp_size)
    return MZ_TRUE;

  // Entry is a subdirectory (I've seen old zips with dir entries which have compressed deflate data which inflates to 0 bytes, but these entries claim to uncompress to 512 bytes in the headers).
  // I'm torn how to handle this case - should it fail instead?
  if (mz_zip_reader_is_file_a_directory(pZip, file_index))
    return MZ_TRUE;

  // Encryption and patch files are not supported.
  if (file_stat.m_bit_flag & (1 | 32))
    return MZ_FALSE;

  // This function only supports stored and deflate.
  if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED))
    return MZ_FALSE;

  // Read and parse the local directory entry.
  cur_file_ofs = file_stat.m_local_header_ofs;
  if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE)
    return MZ_FALSE;
  if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG)
    return MZ_FALSE;

  cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS);
  if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size)
    return MZ_FALSE;

  // Decompress the file either directly from memory or from a file input buffer.
  if (pZip->m_pState->m_pMem)
  {
    pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs;
    read_buf_size = read_buf_avail = file_stat.m_comp_size;
    comp_remaining = 0;
  }
  else
  {
    read_buf_size = MZ_MIN(file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE);
    if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size)))
      return MZ_FALSE;
    read_buf_avail = 0;
    comp_remaining = file_stat.m_comp_size;
  }

  if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method))
  {
    // The file is stored or the caller has requested the compressed data.
    if (pZip->m_pState->m_pMem)
    {
#ifdef _MSC_VER
      if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > 0xFFFFFFFF))
#else
      if (((sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > 0xFFFFFFFF))
#endif
        return MZ_FALSE;
      if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)file_stat.m_comp_size) != file_stat.m_comp_size)
        status = TINFL_STATUS_FAILED;
      else if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))
        file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)file_stat.m_comp_size);
      cur_file_ofs += file_stat.m_comp_size;
      out_buf_ofs += file_stat.m_comp_size;
      comp_remaining = 0;
    }
    else
    {
      while (comp_remaining)
      {
        read_buf_avail = MZ_MIN(read_buf_size, comp_remaining);
        if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail)
        {
          status = TINFL_STATUS_FAILED;
          break;
        }

        if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))
          file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)read_buf_avail);

        if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail)
        {
          status = TINFL_STATUS_FAILED;
          break;
        }
        cur_file_ofs += read_buf_avail;
        out_buf_ofs += read_buf_avail;
        comp_remaining -= read_buf_avail;
      }
    }
  }
  else
  {
    tinfl_decompressor inflator;
    tinfl_init(&inflator);

    if (NULL == (pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE)))
      status = TINFL_STATUS_FAILED;
    else
    {
      do
      {
        mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pWrite_buf + (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1));
        size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1));
        if ((!read_buf_avail) && (!pZip->m_pState->m_pMem))
        {
          read_buf_avail = MZ_MIN(read_buf_size, comp_remaining);
          if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail)
          {
            status = TINFL_STATUS_FAILED;
            break;
          }
          cur_file_ofs += read_buf_avail;
          comp_remaining -= read_buf_avail;
          read_buf_ofs = 0;
        }

        in_buf_size = (size_t)read_buf_avail;
        status = tinfl_decompress(&inflator, (const mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pWrite_buf, pWrite_buf_cur, &out_buf_size, comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0);
        read_buf_avail -= in_buf_size;
        read_buf_ofs += in_buf_size;

        if (out_buf_size)
        {
          if (pCallback(pOpaque, out_buf_ofs, pWrite_buf_cur, out_buf_size) != out_buf_size)
          {
            status = TINFL_STATUS_FAILED;
            break;
          }
          file_crc32 = (mz_uint32)mz_crc32(file_crc32, pWrite_buf_cur, out_buf_size);
          if ((out_buf_ofs += out_buf_size) > file_stat.m_uncomp_size)
          {
            status = TINFL_STATUS_FAILED;
            break;
          }
        }
      } while ((status == TINFL_STATUS_NEEDS_MORE_INPUT) || (status == TINFL_STATUS_HAS_MORE_OUTPUT));
    }
  }

  if ((status == TINFL_STATUS_DONE) && (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)))
  {
    // Make sure the entire file was decompressed, and check its CRC.
    if ((out_buf_ofs != file_stat.m_uncomp_size) || (file_crc32 != file_stat.m_crc32))
      status = TINFL_STATUS_FAILED;
  }

  if (!pZip->m_pState->m_pMem)
    pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
  if (pWrite_buf)
    pZip->m_pFree(pZip->m_pAlloc_opaque, pWrite_buf);

  return status == TINFL_STATUS_DONE;
}

mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags)
{
  int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags);
  if (file_index < 0)
    return MZ_FALSE;
  return mz_zip_reader_extract_to_callback(pZip, file_index, pCallback, pOpaque, flags);
}

#ifndef MINIZ_NO_STDIO
static size_t mz_zip_file_write_callback(void *pOpaque, mz_uint64 ofs, const void *pBuf, size_t n)
{
  (void)ofs; return MZ_FWRITE(pBuf, 1, n, (MZ_FILE*)pOpaque);
}

mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags)
{
  mz_bool status;
  mz_zip_archive_file_stat file_stat;
  MZ_FILE *pFile;
  if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat))
    return MZ_FALSE;
  pFile = MZ_FOPEN(pDst_filename, "wb");
  if (!pFile)
    return MZ_FALSE;
  status = mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags);
  if (MZ_FCLOSE(pFile) == EOF)
    return MZ_FALSE;
#ifndef MINIZ_NO_TIME
  if (status)
    mz_zip_set_file_times(pDst_filename, file_stat.m_time, file_stat.m_time);
#endif
  return status;
}
#endif // #ifndef MINIZ_NO_STDIO

mz_bool mz_zip_reader_end(mz_zip_archive *pZip)
{
  if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING))
    return MZ_FALSE;

  if (pZip->m_pState)
  {
    mz_zip_internal_state *pState = pZip->m_pState; pZip->m_pState = NULL;
    mz_zip_array_clear(pZip, &pState->m_central_dir);
    mz_zip_array_clear(pZip, &pState->m_central_dir_offsets);
    mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets);

#ifndef MINIZ_NO_STDIO
    if (pState->m_pFile)
    {
      MZ_FCLOSE(pState->m_pFile);
      pState->m_pFile = NULL;
    }
#endif // #ifndef MINIZ_NO_STDIO

    pZip->m_pFree(pZip->m_pAlloc_opaque, pState);
  }
  pZip->m_zip_mode = MZ_ZIP_MODE_INVALID;

  return MZ_TRUE;
}

#ifndef MINIZ_NO_STDIO
mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags)
{
  int file_index = mz_zip_reader_locate_file(pZip, pArchive_filename, NULL, flags);
  if (file_index < 0)
    return MZ_FALSE;
  return mz_zip_reader_extract_to_file(pZip, file_index, pDst_filename, flags);
}
#endif

// ------------------- .ZIP archive writing

#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS

static void mz_write_le16(mz_uint8 *p, mz_uint16 v) { p[0] = (mz_uint8)v; p[1] = (mz_uint8)(v >> 8); }
static void mz_write_le32(mz_uint8 *p, mz_uint32 v) { p[0] = (mz_uint8)v; p[1] = (mz_uint8)(v >> 8); p[2] = (mz_uint8)(v >> 16); p[3] = (mz_uint8)(v >> 24); }
#define MZ_WRITE_LE16(p, v) mz_write_le16((mz_uint8 *)(p), (mz_uint16)(v))
#define MZ_WRITE_LE32(p, v) mz_write_le32((mz_uint8 *)(p), (mz_uint32)(v))

mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size)
{
  if ((!pZip) || (pZip->m_pState) || (!pZip->m_pWrite) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID))
    return MZ_FALSE;

  if (pZip->m_file_offset_alignment)
  {
    // Ensure user specified file offset alignment is a power of 2.
    if (pZip->m_file_offset_alignment & (pZip->m_file_offset_alignment - 1))
      return MZ_FALSE;
  }

  if (!pZip->m_pAlloc) pZip->m_pAlloc = def_alloc_func;
  if (!pZip->m_pFree) pZip->m_pFree = def_free_func;
  if (!pZip->m_pRealloc) pZip->m_pRealloc = def_realloc_func;

  pZip->m_zip_mode = MZ_ZIP_MODE_WRITING;
  pZip->m_archive_size = existing_size;
  pZip->m_central_directory_file_ofs = 0;
  pZip->m_total_files = 0;

  if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state))))
    return MZ_FALSE;
  memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state));
  MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8));
  MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32));
  MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32));
  return MZ_TRUE;
}

static size_t mz_zip_heap_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n)
{
  mz_zip_archive *pZip = (mz_zip_archive *)pOpaque;
  mz_zip_internal_state *pState = pZip->m_pState;
  mz_uint64 new_size = MZ_MAX(file_ofs + n, pState->m_mem_size);
#ifdef _MSC_VER
  if ((!n) || ((0, sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF)))
#else
  if ((!n) || ((sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF)))
#endif
    return 0;
  if (new_size > pState->m_mem_capacity)
  {
    void *pNew_block;
    size_t new_capacity = MZ_MAX(64, pState->m_mem_capacity); while (new_capacity < new_size) new_capacity *= 2;
    if (NULL == (pNew_block = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pState->m_pMem, 1, new_capacity)))
      return 0;
    pState->m_pMem = pNew_block; pState->m_mem_capacity = new_capacity;
  }
  memcpy((mz_uint8 *)pState->m_pMem + file_ofs, pBuf, n);
  pState->m_mem_size = (size_t)new_size;
  return n;
}

mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size)
{
  pZip->m_pWrite = mz_zip_heap_write_func;
  pZip->m_pIO_opaque = pZip;
  if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning))
    return MZ_FALSE;
  if (0 != (initial_allocation_size = MZ_MAX(initial_allocation_size, size_to_reserve_at_beginning)))
  {
    if (NULL == (pZip->m_pState->m_pMem = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, initial_allocation_size)))
    {
      mz_zip_writer_end(pZip);
      return MZ_FALSE;
    }
    pZip->m_pState->m_mem_capacity = initial_allocation_size;
  }
  return MZ_TRUE;
}

#ifndef MINIZ_NO_STDIO
static size_t mz_zip_file_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n)
{
  mz_zip_archive *pZip = (mz_zip_archive *)pOpaque;
  mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile);
  if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET))))
    return 0;
  return MZ_FWRITE(pBuf, 1, n, pZip->m_pState->m_pFile);
}

mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning)
{
  MZ_FILE *pFile;
  pZip->m_pWrite = mz_zip_file_write_func;
  pZip->m_pIO_opaque = pZip;
  if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning))
    return MZ_FALSE;
  if (NULL == (pFile = MZ_FOPEN(pFilename, "wb")))
  {
    mz_zip_writer_end(pZip);
    return MZ_FALSE;
  }
  pZip->m_pState->m_pFile = pFile;
  if (size_to_reserve_at_beginning)
  {
    mz_uint64 cur_ofs = 0; char buf[4096]; MZ_CLEAR_OBJ(buf);
    do
    {
      size_t n = (size_t)MZ_MIN(sizeof(buf), size_to_reserve_at_beginning);
      if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_ofs, buf, n) != n)
      {
        mz_zip_writer_end(pZip);
        return MZ_FALSE;
      }
      cur_ofs += n; size_to_reserve_at_beginning -= n;
    } while (size_to_reserve_at_beginning);
  }
  return MZ_TRUE;
}
#endif // #ifndef MINIZ_NO_STDIO

mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename)
{
  mz_zip_internal_state *pState;
  if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING))
    return MZ_FALSE;
  // No sense in trying to write to an archive that's already at the support max size
  if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > 0xFFFFFFFF))
    return MZ_FALSE;

  pState = pZip->m_pState;

  if (pState->m_pFile)
  {
#ifdef MINIZ_NO_STDIO
    pFilename; return MZ_FALSE;
#else
    // Archive is being read from stdio - try to reopen as writable.
    if (pZip->m_pIO_opaque != pZip)
      return MZ_FALSE;
    if (!pFilename)
      return MZ_FALSE;
    pZip->m_pWrite = mz_zip_file_write_func;
    if (NULL == (pState->m_pFile = MZ_FREOPEN(pFilename, "r+b", pState->m_pFile)))
    {
      // The mz_zip_archive is now in a bogus state because pState->m_pFile is NULL, so just close it.
      mz_zip_reader_end(pZip);
      return MZ_FALSE;
    }
#endif // #ifdef MINIZ_NO_STDIO
  }
  else if (pState->m_pMem)
  {
    // Archive lives in a memory block. Assume it's from the heap that we can resize using the realloc callback.
    if (pZip->m_pIO_opaque != pZip)
      return MZ_FALSE;
    pState->m_mem_capacity = pState->m_mem_size;
    pZip->m_pWrite = mz_zip_heap_write_func;
  }
  // Archive is being read via a user provided read function - make sure the user has specified a write function too.
  else if (!pZip->m_pWrite)
    return MZ_FALSE;

  // Start writing new files at the archive's current central directory location.
  pZip->m_archive_size = pZip->m_central_directory_file_ofs;
  pZip->m_zip_mode = MZ_ZIP_MODE_WRITING;
  pZip->m_central_directory_file_ofs = 0;

  return MZ_TRUE;
}

mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags)
{
  return mz_zip_writer_add_mem_ex(pZip, pArchive_name, pBuf, buf_size, NULL, 0, level_and_flags, 0, 0);
}

typedef struct
{
  mz_zip_archive *m_pZip;
  mz_uint64 m_cur_archive_file_ofs;
  mz_uint64 m_comp_size;
} mz_zip_writer_add_state;

static mz_bool mz_zip_writer_add_put_buf_callback(const void* pBuf, int len, void *pUser)
{
  mz_zip_writer_add_state *pState = (mz_zip_writer_add_state *)pUser;
  if ((int)pState->m_pZip->m_pWrite(pState->m_pZip->m_pIO_opaque, pState->m_cur_archive_file_ofs, pBuf, len) != len)
    return MZ_FALSE;
  pState->m_cur_archive_file_ofs += len;
  pState->m_comp_size += len;
  return MZ_TRUE;
}

static mz_bool mz_zip_writer_create_local_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date)
{
  (void)pZip;
  memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE);
  MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG);
  MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, method ? 20 : 0);
  MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags);
  MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, method);
  MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, dos_time);
  MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, dos_date);
  MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, uncomp_crc32);
  MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, comp_size);
  MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, uncomp_size);
  MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size);
  MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size);
  return MZ_TRUE;
}

static mz_bool mz_zip_writer_create_central_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, mz_uint32 ext_attributes)
{
  (void)pZip;
  memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE);
  MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG);
  MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, method ? 20 : 0);
  MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags);
  MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, method);
  MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_TIME_OFS, dos_time);
  MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_DATE_OFS, dos_date);
  MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_CRC32_OFS, uncomp_crc32);
  MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, comp_size);
  MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, uncomp_size);
  MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILENAME_LEN_OFS, filename_size);
  MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_EXTRA_LEN_OFS, extra_size);
  MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_COMMENT_LEN_OFS, comment_size);
  MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS, ext_attributes);
  MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_header_ofs);
  return MZ_TRUE;
}

static mz_bool mz_zip_writer_add_to_central_dir(mz_zip_archive *pZip, const char *pFilename, mz_uint16 filename_size, const void *pExtra, mz_uint16 extra_size, const void *pComment, mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, mz_uint32 ext_attributes)
{
  mz_zip_internal_state *pState = pZip->m_pState;
  mz_uint32 central_dir_ofs = (mz_uint32)pState->m_central_dir.m_size;
  size_t orig_central_dir_size = pState->m_central_dir.m_size;
  mz_uint8 central_dir_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE];

  // No zip64 support yet
  if ((local_header_ofs > 0xFFFFFFFF) || (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + extra_size + comment_size) > 0xFFFFFFFF))
    return MZ_FALSE;

  if (!mz_zip_writer_create_central_dir_header(pZip, central_dir_header, filename_size, extra_size, comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_header_ofs, ext_attributes))
    return MZ_FALSE;

  if ((!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_dir_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) ||
      (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pFilename, filename_size)) ||
      (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pExtra, extra_size)) ||
      (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pComment, comment_size)) ||
      (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &central_dir_ofs, 1)))
  {
    // Try to push the central directory array back into its original state.
    mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE);
    return MZ_FALSE;
  }

  return MZ_TRUE;
}

static mz_bool mz_zip_writer_validate_archive_name(const char *pArchive_name)
{
  // Basic ZIP archive filename validity checks: Valid filenames cannot start with a forward slash, cannot contain a drive letter, and cannot use DOS-style backward slashes.
  if (*pArchive_name == '/')
    return MZ_FALSE;
  while (*pArchive_name)
  {
    if ((*pArchive_name == '\\') || (*pArchive_name == ':'))
      return MZ_FALSE;
    pArchive_name++;
  }
  return MZ_TRUE;
}

static mz_uint mz_zip_writer_compute_padding_needed_for_file_alignment(mz_zip_archive *pZip)
{
  mz_uint32 n;
  if (!pZip->m_file_offset_alignment)
    return 0;
  n = (mz_uint32)(pZip->m_archive_size & (pZip->m_file_offset_alignment - 1));
  return (pZip->m_file_offset_alignment - n) & (pZip->m_file_offset_alignment - 1);
}

static mz_bool mz_zip_writer_write_zeros(mz_zip_archive *pZip, mz_uint64 cur_file_ofs, mz_uint32 n)
{
  char buf[4096];
  memset(buf, 0, MZ_MIN(sizeof(buf), n));
  while (n)
  {
    mz_uint32 s = MZ_MIN(sizeof(buf), n);
    if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_file_ofs, buf, s) != s)
      return MZ_FALSE;
    cur_file_ofs += s; n -= s;
  }
  return MZ_TRUE;
}

mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32)
{
  mz_uint16 method = 0, dos_time = 0, dos_date = 0;
  mz_uint level, ext_attributes = 0, num_alignment_padding_bytes;
  mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, comp_size = 0;
  size_t archive_name_size;
  mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE];
  tdefl_compressor *pComp = NULL;
  mz_bool store_data_uncompressed;
  mz_zip_internal_state *pState;

  if ((int)level_and_flags < 0)
    level_and_flags = MZ_DEFAULT_LEVEL;
  level = level_and_flags & 0xF;
  store_data_uncompressed = ((!level) || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA));

  if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || ((buf_size) && (!pBuf)) || (!pArchive_name) || ((comment_size) && (!pComment)) || (pZip->m_total_files == 0xFFFF) || (level > MZ_UBER_COMPRESSION))
    return MZ_FALSE;

  pState = pZip->m_pState;

  if ((!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (uncomp_size))
    return MZ_FALSE;
  // No zip64 support yet
  if ((buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF))
    return MZ_FALSE;
  if (!mz_zip_writer_validate_archive_name(pArchive_name))
    return MZ_FALSE;

#ifndef MINIZ_NO_TIME
  {
    time_t cur_time; time(&cur_time);
    mz_zip_time_to_dos_time(cur_time, &dos_time, &dos_date);
  }
#endif // #ifndef MINIZ_NO_TIME

  archive_name_size = strlen(pArchive_name);
  if (archive_name_size > 0xFFFF)
    return MZ_FALSE;

  num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip);

  // no zip64 support yet
  if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + comment_size + archive_name_size) > 0xFFFFFFFF))
    return MZ_FALSE;

  if ((archive_name_size) && (pArchive_name[archive_name_size - 1] == '/'))
  {
    // Set DOS Subdirectory attribute bit.
    ext_attributes |= 0x10;
    // Subdirectories cannot contain data.
    if ((buf_size) || (uncomp_size))
      return MZ_FALSE;
  }

  // Try to do any allocations before writing to the archive, so if an allocation fails the file remains unmodified. (A good idea if we're doing an in-place modification.)
  if ((!mz_zip_array_ensure_room(pZip, &pState->m_central_dir, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size)) || (!mz_zip_array_ensure_room(pZip, &pState->m_central_dir_offsets, 1)))
    return MZ_FALSE;

  if ((!store_data_uncompressed) && (buf_size))
  {
    if (NULL == (pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor))))
      return MZ_FALSE;
  }

  if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes + sizeof(local_dir_header)))
  {
    pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
    return MZ_FALSE;
  }
  local_dir_header_ofs += num_alignment_padding_bytes;
  if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); }
  cur_archive_file_ofs += num_alignment_padding_bytes + sizeof(local_dir_header);

  MZ_CLEAR_OBJ(local_dir_header);
  if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size)
  {
    pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
    return MZ_FALSE;
  }
  cur_archive_file_ofs += archive_name_size;

  if (!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA))
  {
    uncomp_crc32 = (mz_uint32)mz_crc32(MZ_CRC32_INIT, (const mz_uint8*)pBuf, buf_size);
    uncomp_size = buf_size;
    if (uncomp_size <= 3)
    {
      level = 0;
      store_data_uncompressed = MZ_TRUE;
    }
  }

  if (store_data_uncompressed)
  {
    if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pBuf, buf_size) != buf_size)
    {
      pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
      return MZ_FALSE;
    }

    cur_archive_file_ofs += buf_size;
    comp_size = buf_size;

    if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)
      method = MZ_DEFLATED;
  }
  else if (buf_size)
  {
    mz_zip_writer_add_state state;

    state.m_pZip = pZip;
    state.m_cur_archive_file_ofs = cur_archive_file_ofs;
    state.m_comp_size = 0;

    if ((tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) ||
        (tdefl_compress_buffer(pComp, pBuf, buf_size, TDEFL_FINISH) != TDEFL_STATUS_DONE))
    {
      pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
      return MZ_FALSE;
    }

    comp_size = state.m_comp_size;
    cur_archive_file_ofs = state.m_cur_archive_file_ofs;

    method = MZ_DEFLATED;
  }

  pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
  pComp = NULL;

  // no zip64 support yet
  if ((comp_size > 0xFFFFFFFF) || (cur_archive_file_ofs > 0xFFFFFFFF))
    return MZ_FALSE;

  if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, 0, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date))
    return MZ_FALSE;

  if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header))
    return MZ_FALSE;

  if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, NULL, 0, pComment, comment_size, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date, local_dir_header_ofs, ext_attributes))
    return MZ_FALSE;

  pZip->m_total_files++;
  pZip->m_archive_size = cur_archive_file_ofs;

  return MZ_TRUE;
}

#ifndef MINIZ_NO_STDIO
mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags)
{
  mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes;
  mz_uint16 method = 0, dos_time = 0, dos_date = 0, ext_attributes = 0;
  mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, uncomp_size = 0, comp_size = 0;
  size_t archive_name_size;
  mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE];
  MZ_FILE *pSrc_file = NULL;

  if ((int)level_and_flags < 0)
    level_and_flags = MZ_DEFAULT_LEVEL;
  level = level_and_flags & 0xF;

  if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION))
    return MZ_FALSE;
  if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)
    return MZ_FALSE;
  if (!mz_zip_writer_validate_archive_name(pArchive_name))
    return MZ_FALSE;

  archive_name_size = strlen(pArchive_name);
  if (archive_name_size > 0xFFFF)
    return MZ_FALSE;

  num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip);

  // no zip64 support yet
  if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + comment_size + archive_name_size) > 0xFFFFFFFF))
    return MZ_FALSE;

  if (!mz_zip_get_file_modified_time(pSrc_filename, &dos_time, &dos_date))
    return MZ_FALSE;

  pSrc_file = MZ_FOPEN(pSrc_filename, "rb");
  if (!pSrc_file)
    return MZ_FALSE;
  MZ_FSEEK64(pSrc_file, 0, SEEK_END);
  uncomp_size = MZ_FTELL64(pSrc_file);
  MZ_FSEEK64(pSrc_file, 0, SEEK_SET);

  if (uncomp_size > 0xFFFFFFFF)
  {
    // No zip64 support yet
    MZ_FCLOSE(pSrc_file);
    return MZ_FALSE;
  }
  if (uncomp_size <= 3)
    level = 0;

  if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes + sizeof(local_dir_header)))
  {
    MZ_FCLOSE(pSrc_file);
    return MZ_FALSE;
  }
  local_dir_header_ofs += num_alignment_padding_bytes;
  if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); }
  cur_archive_file_ofs += num_alignment_padding_bytes + sizeof(local_dir_header);

  MZ_CLEAR_OBJ(local_dir_header);
  if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size)
  {
    MZ_FCLOSE(pSrc_file);
    return MZ_FALSE;
  }
  cur_archive_file_ofs += archive_name_size;

  if (uncomp_size)
  {
    mz_uint64 uncomp_remaining = uncomp_size;
    void *pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, MZ_ZIP_MAX_IO_BUF_SIZE);
    if (!pRead_buf)
    {
      MZ_FCLOSE(pSrc_file);
      return MZ_FALSE;
    }

    if (!level)
    {
      while (uncomp_remaining)
      {
        mz_uint n = (mz_uint)MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, uncomp_remaining);
        if ((MZ_FREAD(pRead_buf, 1, n, pSrc_file) != n) || (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pRead_buf, n) != n))
        {
          pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
          MZ_FCLOSE(pSrc_file);
          return MZ_FALSE;
        }
        uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n);
        uncomp_remaining -= n;
        cur_archive_file_ofs += n;
      }
      comp_size = uncomp_size;
    }
    else
    {
      mz_bool result = MZ_FALSE;
      mz_zip_writer_add_state state;
      tdefl_compressor *pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor));
      if (!pComp)
      {
        pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
        MZ_FCLOSE(pSrc_file);
        return MZ_FALSE;
      }

      state.m_pZip = pZip;
      state.m_cur_archive_file_ofs = cur_archive_file_ofs;
      state.m_comp_size = 0;

      if (tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY)
      {
        pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
        pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
        MZ_FCLOSE(pSrc_file);
        return MZ_FALSE;
      }

      for ( ; ; )
      {
        size_t in_buf_size = (mz_uint32)MZ_MIN(uncomp_remaining, MZ_ZIP_MAX_IO_BUF_SIZE);
        tdefl_status status;

        if (MZ_FREAD(pRead_buf, 1, in_buf_size, pSrc_file) != in_buf_size)
          break;

        uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, in_buf_size);
        uncomp_remaining -= in_buf_size;

        status = tdefl_compress_buffer(pComp, pRead_buf, in_buf_size, uncomp_remaining ? TDEFL_NO_FLUSH : TDEFL_FINISH);
        if (status == TDEFL_STATUS_DONE)
        {
          result = MZ_TRUE;
          break;
        }
        else if (status != TDEFL_STATUS_OKAY)
          break;
      }

      pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);

      if (!result)
      {
        pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
        MZ_FCLOSE(pSrc_file);
        return MZ_FALSE;
      }

      comp_size = state.m_comp_size;
      cur_archive_file_ofs = state.m_cur_archive_file_ofs;

      method = MZ_DEFLATED;
    }

    pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
  }

  MZ_FCLOSE(pSrc_file); pSrc_file = NULL;

  // no zip64 support yet
  if ((comp_size > 0xFFFFFFFF) || (cur_archive_file_ofs > 0xFFFFFFFF))
    return MZ_FALSE;

  if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, 0, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date))
    return MZ_FALSE;

  if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header))
    return MZ_FALSE;

  if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, NULL, 0, pComment, comment_size, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date, local_dir_header_ofs, ext_attributes))
    return MZ_FALSE;

  pZip->m_total_files++;
  pZip->m_archive_size = cur_archive_file_ofs;

  return MZ_TRUE;
}
#endif // #ifndef MINIZ_NO_STDIO

mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint file_index)
{
  mz_uint n, bit_flags, num_alignment_padding_bytes;
  mz_uint64 comp_bytes_remaining, local_dir_header_ofs;
  mz_uint64 cur_src_file_ofs, cur_dst_file_ofs;
  mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32;
  mz_uint8 central_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE];
  size_t orig_central_dir_size;
  mz_zip_internal_state *pState;
  void *pBuf; const mz_uint8 *pSrc_central_header;

  if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING))
    return MZ_FALSE;
  if (NULL == (pSrc_central_header = mz_zip_reader_get_cdh(pSource_zip, file_index)))
    return MZ_FALSE;
  pState = pZip->m_pState;

  num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip);

  // no zip64 support yet
  if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) > 0xFFFFFFFF))
    return MZ_FALSE;

  cur_src_file_ofs = MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS);
  cur_dst_file_ofs = pZip->m_archive_size;

  if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE)
    return MZ_FALSE;
  if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG)
    return MZ_FALSE;
  cur_src_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE;

  if (!mz_zip_writer_write_zeros(pZip, cur_dst_file_ofs, num_alignment_padding_bytes))
    return MZ_FALSE;
  cur_dst_file_ofs += num_alignment_padding_bytes;
  local_dir_header_ofs = cur_dst_file_ofs;
  if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); }

  if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE)
    return MZ_FALSE;
  cur_dst_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE;

  n = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS);
  comp_bytes_remaining = n + MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS);

  if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)MZ_MAX(sizeof(mz_uint32) * 4, MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, comp_bytes_remaining)))))
    return MZ_FALSE;

  while (comp_bytes_remaining)
  {
    n = (mz_uint)MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, comp_bytes_remaining);
    if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, n) != n)
    {
      pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
      return MZ_FALSE;
    }
    cur_src_file_ofs += n;

    if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n)
    {
      pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
      return MZ_FALSE;
    }
    cur_dst_file_ofs += n;

    comp_bytes_remaining -= n;
  }

  bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS);
  if (bit_flags & 8)
  {
    // Copy data descriptor
    if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, sizeof(mz_uint32) * 4) != sizeof(mz_uint32) * 4)
    {
      pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
      return MZ_FALSE;
    }

    n = sizeof(mz_uint32) * ((MZ_READ_LE32(pBuf) == 0x08074b50) ? 4 : 3);
    if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n)
    {
      pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
      return MZ_FALSE;
    }

    cur_src_file_ofs += n;
    cur_dst_file_ofs += n;
  }
  pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);

  // no zip64 support yet
  if (cur_dst_file_ofs > 0xFFFFFFFF)
    return MZ_FALSE;

  orig_central_dir_size = pState->m_central_dir.m_size;

  memcpy(central_header, pSrc_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE);
  MZ_WRITE_LE32(central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_dir_header_ofs);
  if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE))
    return MZ_FALSE;

  n = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_COMMENT_LEN_OFS);
  if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n))
  {
    mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE);
    return MZ_FALSE;
  }

  if (pState->m_central_dir.m_size > 0xFFFFFFFF)
    return MZ_FALSE;
  n = (mz_uint32)orig_central_dir_size;
  if (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &n, 1))
  {
    mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE);
    return MZ_FALSE;
  }

  pZip->m_total_files++;
  pZip->m_archive_size = cur_dst_file_ofs;

  return MZ_TRUE;
}

mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip)
{
  mz_zip_internal_state *pState;
  mz_uint64 central_dir_ofs, central_dir_size;
  mz_uint8 hdr[MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE];

  if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING))
    return MZ_FALSE;

  pState = pZip->m_pState;

  // no zip64 support yet
  if ((pZip->m_total_files > 0xFFFF) || ((pZip->m_archive_size + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) > 0xFFFFFFFF))
    return MZ_FALSE;

  central_dir_ofs = 0;
  central_dir_size = 0;
  if (pZip->m_total_files)
  {
    // Write central directory
    central_dir_ofs = pZip->m_archive_size;
    central_dir_size = pState->m_central_dir.m_size;
    pZip->m_central_directory_file_ofs = central_dir_ofs;
    if (pZip->m_pWrite(pZip->m_pIO_opaque, central_dir_ofs, pState->m_central_dir.m_p, (size_t)central_dir_size) != central_dir_size)
      return MZ_FALSE;
    pZip->m_archive_size += central_dir_size;
  }

  // Write end of central directory record
  MZ_CLEAR_OBJ(hdr);
  MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_SIG_OFS, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG);
  MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, pZip->m_total_files);
  MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS, pZip->m_total_files);
  MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_SIZE_OFS, central_dir_size);
  MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_OFS_OFS, central_dir_ofs);

  if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, sizeof(hdr)) != sizeof(hdr))
    return MZ_FALSE;
#ifndef MINIZ_NO_STDIO
  if ((pState->m_pFile) && (MZ_FFLUSH(pState->m_pFile) == EOF))
    return MZ_FALSE;
#endif // #ifndef MINIZ_NO_STDIO

  pZip->m_archive_size += sizeof(hdr);

  pZip->m_zip_mode = MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED;
  return MZ_TRUE;
}

mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **pBuf, size_t *pSize)
{
  if ((!pZip) || (!pZip->m_pState) || (!pBuf) || (!pSize))
    return MZ_FALSE;
  if (pZip->m_pWrite != mz_zip_heap_write_func)
    return MZ_FALSE;
  if (!mz_zip_writer_finalize_archive(pZip))
    return MZ_FALSE;

  *pBuf = pZip->m_pState->m_pMem;
  *pSize = pZip->m_pState->m_mem_size;
  pZip->m_pState->m_pMem = NULL;
  pZip->m_pState->m_mem_size = pZip->m_pState->m_mem_capacity = 0;
  return MZ_TRUE;
}

mz_bool mz_zip_writer_end(mz_zip_archive *pZip)
{
  mz_zip_internal_state *pState;
  mz_bool status = MZ_TRUE;
  if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || ((pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) && (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED)))
    return MZ_FALSE;

  pState = pZip->m_pState;
  pZip->m_pState = NULL;
  mz_zip_array_clear(pZip, &pState->m_central_dir);
  mz_zip_array_clear(pZip, &pState->m_central_dir_offsets);
  mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets);

#ifndef MINIZ_NO_STDIO
  if (pState->m_pFile)
  {
    MZ_FCLOSE(pState->m_pFile);
    pState->m_pFile = NULL;
  }
#endif // #ifndef MINIZ_NO_STDIO

  if ((pZip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem))
  {
    pZip->m_pFree(pZip->m_pAlloc_opaque, pState->m_pMem);
    pState->m_pMem = NULL;
  }

  pZip->m_pFree(pZip->m_pAlloc_opaque, pState);
  pZip->m_zip_mode = MZ_ZIP_MODE_INVALID;
  return status;
}

#ifndef MINIZ_NO_STDIO
mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags)
{
  mz_bool status, created_new_archive = MZ_FALSE;
  mz_zip_archive zip_archive;
  struct MZ_FILE_STAT_STRUCT file_stat;
  MZ_CLEAR_OBJ(zip_archive);
  if ((int)level_and_flags < 0)
     level_and_flags = MZ_DEFAULT_LEVEL;
  if ((!pZip_filename) || (!pArchive_name) || ((buf_size) && (!pBuf)) || ((comment_size) && (!pComment)) || ((level_and_flags & 0xF) > MZ_UBER_COMPRESSION))
    return MZ_FALSE;
  if (!mz_zip_writer_validate_archive_name(pArchive_name))
    return MZ_FALSE;
  if (MZ_FILE_STAT(pZip_filename, &file_stat) != 0)
  {
    // Create a new archive.
    if (!mz_zip_writer_init_file(&zip_archive, pZip_filename, 0))
      return MZ_FALSE;
    created_new_archive = MZ_TRUE;
  }
  else
  {
    // Append to an existing archive.
    if (!mz_zip_reader_init_file(&zip_archive, pZip_filename, level_and_flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY))
      return MZ_FALSE;
    if (!mz_zip_writer_init_from_reader(&zip_archive, pZip_filename))
    {
      mz_zip_reader_end(&zip_archive);
      return MZ_FALSE;
    }
  }
  status = mz_zip_writer_add_mem_ex(&zip_archive, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, 0, 0);
  // Always finalize, even if adding failed for some reason, so we have a valid central directory. (This may not always succeed, but we can try.)
  if (!mz_zip_writer_finalize_archive(&zip_archive))
    status = MZ_FALSE;
  if (!mz_zip_writer_end(&zip_archive))
    status = MZ_FALSE;
  if ((!status) && (created_new_archive))
  {
    // It's a new archive and something went wrong, so just delete it.
    int ignoredStatus = MZ_DELETE_FILE(pZip_filename);
    (void)ignoredStatus;
  }
  return status;
}

void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags)
{
  int file_index;
  mz_zip_archive zip_archive;
  void *p = NULL;

  if (pSize)
    *pSize = 0;

  if ((!pZip_filename) || (!pArchive_name))
    return NULL;

  MZ_CLEAR_OBJ(zip_archive);
  if (!mz_zip_reader_init_file(&zip_archive, pZip_filename, flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY))
    return NULL;

  if ((file_index = mz_zip_reader_locate_file(&zip_archive, pArchive_name, NULL, flags)) >= 0)
    p = mz_zip_reader_extract_to_heap(&zip_archive, file_index, pSize, flags);

  mz_zip_reader_end(&zip_archive);
  return p;
}

#endif // #ifndef MINIZ_NO_STDIO

#endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS

#endif // #ifndef MINIZ_NO_ARCHIVE_APIS

#ifdef __cplusplus
}
#endif

#endif // MINIZ_HEADER_FILE_ONLY

/*
  This is free and unencumbered software released into the public domain.

  Anyone is free to copy, modify, publish, use, compile, sell, or
  distribute this software, either in source code form or as a compiled
  binary, for any purpose, commercial or non-commercial, and by any
  means.

  In jurisdictions that recognize copyright laws, the author or authors
  of this software dedicate any and all copyright interest in the
  software to the public domain. We make this dedication for the benefit
  of the public at large and to the detriment of our heirs and
  successors. We intend this dedication to be an overt act of
  relinquishment in perpetuity of all present and future rights to this
  software under copyright law.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
  OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  OTHER DEALINGS IN THE SOFTWARE.

  For more information, please refer to <http://unlicense.org/>
*/

Deleted src/mkbuiltin.c.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168








































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
/*
** Copyright (c) 2014 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the Simplified BSD License (also
** known as the "2-Clause License" or "FreeBSD License".)
**
** This program is distributed in the hope that it will be useful,
** but without any warranty; without even the implied warranty of
** merchantability or fitness for a particular purpose.
**
** Author contact information:
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*******************************************************************************
**
** This is a stand-alone utility program that is part of the Fossil build
** process.  This program reads files named on the command line and converts
** them into ANSI-C static char array variables.  Output is written onto
** standard output.
**
** The makefiles use this utility 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>


/*
** Read the entire content of the file named zFilename into memory obtained
** from malloc() and return a pointer to that memory.  Write the size of the
** file into *pnByte.
*/
static unsigned char *read_file(const char *zFilename, int *pnByte){
  FILE *in;
  unsigned char *z;
  int nByte;
  int got;
  in = fopen(zFilename, "rb");
  if( in==0 ){
    return 0;
  }
  fseek(in, 0, SEEK_END);
  *pnByte = nByte = ftell(in);
  fseek(in, 0, SEEK_SET);
  z = malloc( nByte+1 );
  if( z==0 ){
    fprintf(stderr, "failed to allocate %d bytes\n", nByte+1);
    exit(1);
  }
  got = fread(z, 1, nByte, in);
  fclose(in);
  z[got] = 0;
  return z;
}

/*
** There is an instance of the following for each file translated.
*/
typedef struct Resource Resource;
struct Resource {
  char *zName;
  int nByte;
  int idx;
};

/*
** Compare two Resource objects for sorting purposes.  They sort
** in zName order so that Fossil can search for resources using
** a binary search.
*/
static int compareResource(const void *a, const void *b){
  Resource *pA = (Resource*)a;
  Resource *pB = (Resource*)b;
  return strcmp(pA->zName, pB->zName);
}

int main(int argc, char **argv){
  int i, sz;
  int j, n;
  Resource *aRes;
  int nRes;
  unsigned char *pData;
  int nErr = 0;
  int nSkip;
  int nPrefix = 0;

  if( argc>3 && strcmp(argv[1],"--prefix")==0 ){
    nPrefix = (int)strlen(argv[2]);
    argc -= 2;
    argv += 2;
  }
  nRes = argc - 1;
  aRes = malloc( nRes*sizeof(aRes[0]) );
  if( aRes==0 ){
    fprintf(stderr, "malloc failed\n");
    return 1;
  }
  for(i=0; i<argc-1; i++){
    aRes[i].zName = argv[i+1];
  }
  qsort(aRes, nRes, sizeof(aRes[0]), compareResource);
  printf("/* Automatically generated code:  Do not edit.\n**\n"
         "** Rerun the \"mkbuiltin.c\" program or rerun the Fossil\n"
         "** makefile to update this source file.\n"
         "*/\n");
  for(i=0; i<nRes; i++){
    pData = read_file(aRes[i].zName, &sz);
    if( pData==0 ){
      fprintf(stderr, "Cannot open file [%s]\n", aRes[i].zName);
      nErr++;
      continue;
    }

    /* Skip initial lines beginning with # */
    nSkip = 0;
    while( pData[nSkip]=='#' ){
      while( pData[nSkip]!=0 && pData[nSkip]!='\n' ){ nSkip++; }
      if( pData[nSkip]=='\n' ) nSkip++;
    }

    aRes[i].nByte = sz - nSkip;
    aRes[i].idx = i;
    printf("/* Content of file %s */\n", aRes[i].zName);
    printf("static const unsigned char bidata%d[%d] = {\n  ",
           i, sz+1-nSkip);
    for(j=nSkip, n=0; j<=sz; j++){
      printf("%3d", pData[j]);
      if( j==sz ){
        printf(" };\n");
      }else if( n==14 ){
        printf(",\n  ");
        n = 0;
      }else{
        printf(", ");
        n++;
      }
    }
    free(pData);
  }
  printf("typedef struct BuiltinFileTable BuiltinFileTable;\n");
  printf("struct BuiltinFileTable {\n");
  printf("  const char *zName;\n");
  printf("  const unsigned char *pData;\n");
  printf("  int nByte;\n");
  printf("};\n");
  printf("static const BuiltinFileTable aBuiltinFiles[] = {\n");
  for(i=0; i<nRes; i++){
    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]), compareResource);
  for(i=0; i<nRes; i++){
    printf("  { \"%s\", bidata%d, %d },\n",
           aRes[i].zName, aRes[i].idx, aRes[i].nByte);
  }
  printf("};\n");
  return nErr;
}

Deleted src/mkcss.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








































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
/*
** This C program generates the "default_css.h" header file from
** "default_css.txt" source file.
**
** The default_css.h header contains a definition of a structure
** with lots of default CSS snippets.  This information is used to
** generate the /style.css page as follows:
**
**    (1) Read the repository-specific CSS page from the skin
**    (2) Initialize the output to a copy of the repo-CSS from (1).
**    (3) For each entry in the cssDefaultList[], if the selector
**        described by cssDefaultList[i] is not found in the
**        repo-CSS, then append it to the output.
**
** The input file, "default_css.txt", is plain text with lots of
** comments.  This routine strips out the comments and breaks the
** text up into individual cssDefaultList[] elements.
**
** To run this program:
**
**       ./mkcss default_css.txt default_css.h
**
** In other words, there are two arguments.  The first is the name of
** the input file and the second is the name of the output file.
** Either argument can be "-" to indicate standard input or output.
**
** Input Format Summary:
**
**     # comment
**     selector {
**       rule; # comment
**     }
**     # comment
**
** It would be much easier to do this using a script, but that would
** make the Fossil source-code less cross-platform because it would then
** require that the script engine be installed on the build platform.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>

static FILE *open_for_reading(const char *zFilename){
  FILE *f;
  if( strcmp(zFilename, "-")==0 ) return stdin;
  f = fopen(zFilename, "r");
  if( f==0 ){
    fprintf(stderr, "cannot open \"%s\" for reading\n", zFilename);
    exit(1);
  }
  return f;
}
static FILE *open_for_writing(const char *zFilename){
  FILE *f;
  if( strcmp(zFilename, "-")==0 ) return stdout;
  f = fopen(zFilename, "w");
  if( f==0 ){
    fprintf(stderr, "cannot open \"%s\" for writing\n", zFilename);
    exit(1);
  }
  return f;
}
static void close_file(FILE *f){
  if( f!=stdin && f!=stdout){
    fclose(f);
  }
}

/*
** Print a string as a quoted C-language string.
*/
static void clang_puts(FILE *out, const char *z){
  int i;
  while( z[0] ){
    for(i=0; z[i] && z[i]!='"' && z[i]!='\\'; i++){}
    fprintf(out, "%.*s", i, z);
    if( z[i] ){
      fprintf(out, "\\%c", z[i]);
      z += i+1;
    }else{
      z += i;
    }
  }
}

int main(int argc, char *argv[]){
  FILE *in, *out;
  int inRules = 0;
  int nLine = 0;
  int iStart = 0;
  const char *zInFile;
  const char *zOutFile;
  char z[1000];
  if( argc!=3 ){
    fprintf(stderr, "Usage: %s INPUTFILE OUTPUTFILE\n", argv[0]);
    return 1;
  }
  zInFile = argv[1];
  zOutFile = argv[2];
  in = open_for_reading(zInFile);
  out = open_for_writing(zOutFile);

  fprintf(out,
     "/* DO NOT EDIT\n"
     "** This code is generated automatically using 'mkcss.c'\n"
     "*/\n"
     "const struct strctCssDefaults {\n"
     "  const char *elementClass;  /* Name of element needed */\n"
     "  const char *value;         /* CSS text */\n"
     "} cssDefaultList[] = {\n"
  );
  while( fgets(z, sizeof(z), in) ){
    int n;  /* Line length */
    int i;
    nLine++;
    if( z[0]=='/' && z[1]=='/' ) continue;  /* Skip comments */
    if( z[0]=='-' && z[1]=='-' ) continue;  /* Skip comments */
    if( z[0]=='#' && !isalnum(z[1]) ) continue;  /* Skip comments */
    n = (int)strlen(z);
    while( n>0 && isspace(z[n-1]) ){ z[--n] = 0; }
    if( z[0]==0 ) continue;  /* Blank lines */
    if( isspace(z[0]) ){
      if( !inRules ){
        fprintf(stderr, "%s:%d: CSS rule not within a selector\n",
                zInFile, nLine);
        exit(1);
      }
      for(i=0; isspace(z[i]); i++){}
      fprintf(out, "    \"  ");
      clang_puts(out, z+i);
      fprintf(out, "\\n\"\n");
    }else if( z[0]=='}' ){
      if( !inRules ){
        fprintf(stderr, "%s:%d: surplus CSS rule terminator\n",
                zInFile, nLine);
        exit(1);
      }
      fprintf(out, "  },\n");
      inRules = 0;
    }else if( z[n-1]=='{' ){
      if( inRules ){
        fprintf(stderr, "%s:%d: selector where there should be rule\n",
                zInFile, nLine);
        exit(1);
      }
      inRules = 1;
      iStart = nLine;
      fprintf(out, "  { \"");
      n--;
      while( n>0 && isspace(z[n-1]) ){ z[--n] = 0; }
      clang_puts(out, z);
      fprintf(out, "\",\n");
    }else{
      fprintf(stderr, "%s:%d: syntax error\n",
              zInFile, nLine);
      exit(1);
    }
  }
  if( inRules ){
    fprintf(stderr, "%s:%d: unterminated CSS rule\n", zInFile, iStart);
    exit(1);
  }
  close_file(in);
  fprintf(out, "  {0,0}\n};\n");
  close_file(out);
  return 0;
}

Deleted src/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









































































































































































































































































































































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
/*
** 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.  Examples:
**
**        COMMAND:  abcde*
**        COMMAND:  fghij        2nd-tier
**        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.
**
** 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 */
/**************************************************************************/

/*
** 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{
      /* 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);
      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);
      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);
      aEntry[nUsed].eType |= CMDFLAG_TEST;
    }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==11 && strncmp(&zLine[i], "versionable", j)==0 ){
      aEntry[nUsed].eType |= CMDFLAG_VERSIONABLE;
    }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{
      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;
  }
  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;

  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( aEntry[i].zIf ){
      printf("%s", aEntry[i].zIf);
    }else if( (aEntry[i].eType & CMDFLAG_WEBPAGE)!=0 ){
      nWeb++;
    }
    printf("  { \"%.*s\",%*s%s,%*szHelp%03d, 0x%03x },\n",
      n, z,
      25-n, "",
      aEntry[i].zFunc,
      (int)(29-strlen(aEntry[i].zFunc)), "",
      aEntry[i].iHelp,
      aEntry[i].eType
    );
    if( aEntry[i].zIf ) printf("#endif\n");
  }
  printf("};\n");
  printf("#define FOSSIL_FIRST_CMD %d\n", nWeb);

  /* 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, \"%s\"%*s },\n",
      aEntry[i].iWidth,
      (aEntry[i].eType & CMDFLAG_VERSIONABLE)!=0,
      (aEntry[i].eType & CMDFLAG_BLOCKTEXT)!=0,
      zDef, (int)(10-strlen(zDef)), ""
    );
    if( aEntry[i].zIf ){
      printf("#endif\n");
    }
  }
  printf("{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;
  for(i=1; i<argc; i++){
    zFile = argv[i];
    process_file();
  }
  build_table();
  return nErr;
}

Deleted src/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


















































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
/*
** This C program generates the "VERSION.h" header file from information
** extracted out of the "manifest", "manifest.uuid", and "VERSION" files.
** Call this program with three arguments:
**
**     ./a.out manifest.uuid manifest VERSION
**
** Note that the manifest.uuid and manifest files are generated by Fossil.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

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;
}

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;
    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);
    m = open_for_reading(argv[2]);
    while(b ==  fgets(b, sizeof(b)-1,m)){
        if(0 == strncmp("D ",b,2)){
            printf("#define MANIFEST_DATE \"%.10s %.8s\"\n",b+2,b+13);
            printf("#define MANIFEST_YEAR \"%.4s\"\n",b+2);
        }
    }
    fclose(m);
    v = 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 */
    d = (_MSC_VER / 100); /* major */
    x = (_MSC_VER % 100); /* minor */
    printf("#define COMPILER_VERSION \"%d.%02d\"\n", d, x);
#endif
    return 0;
}

Changes to src/moderate.c.

145
146
147
148
149
150
151
152

153
154
155
156
157
158
159
160
161
162



163
164
165
166
167
168
169
145
146
147
148
149
150
151

152
153
154
155
156
157
158
159
160
161

162
163
164
165
166
167
168
169
170
171







-
+









-
+
+
+







  }
  db_end_transaction(0);
}

/*
** Approve an object held for moderation.
*/
void moderation_approve(int rid){
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 %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
182
183
184
185
186
187
188
189

190
191
192

193


































184
185
186
187
188
189
190

191
192
193

194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229







-
+


-
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
  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);
    www_print_timeline(&q, 0, 0, 0, 0, 0, 0, 0);
    db_finalize(&q);
  }
  style_footer();
  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.

17
18
19
20
21
22
23













24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44




































































































45
46
47









48
49

50
51

52

53
54
55
56
57


58
59
60
61
62
63















64
65
66
67
68
69
70


71
72

73

74






























































































75
76
























































77
78
79
80
81
82

83
84
85
86
87
88
89
90
91
92
93
94











95
96
97
98
99
100
101
102

103
104






105
106
107
108
109
110
111
112
113


114
115
116
117
118


119
120
121
122
123
124
125
126






127



128
129

130
131
132
133
134
135
136
137
138




139

140


141
142

143
144
145


146
147
148



149
150
151
152
153
154


155
156
157
158
159

160
161
162
163
164
165
166
167

168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186

187
188
189
190
191
192
193
194
195
196
197
198
199

200
201
202
203
204
205
206


207
208
209












210
211
212
213
214




215
216

217
218
219
220
221


222
223
224
225
226
227
228

229
230

231


232
233
234
235
236
237
238
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158


159
160
161
162
163
164
165
166
167
168

169
170
171
172

173




174
175
176






177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193





194
195


196

197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355

356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389

390
391
392
393
394
395
396
397
398
399
400
401
402


403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425

426
427
428
429

430
431
432
433
434
435
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







+
+
+
+
+
+
+
+
+
+
+
+
+





















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
+
+
+
+
+
+
+
+
+

-
+


+
-
+
-
-
-
-

+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


-
-
-
-
-
+
+
-
-
+
-
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





-
+












+
+
+
+
+
+
+
+
+
+
+








+

-
+
+
+
+
+
+







-
-
+
+





+
+








+
+
+
+
+
+
-
+
+
+

-
+









+
+
+
+
-
+
-
+
+

-
+


-
+
+


-
+
+
+






+
+




-
+







-
+


















-
+





-
-
-
-
-
-
-
-
+
-




-
-
+
+



+
+
+
+
+
+
+
+
+
+
+
+



-
-
+
+
+
+


+





+
+






-
+

-
+

+
+







**
** 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" if inBranch==0 or the first check-in in the branch
** if inBranch==1.
** 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 inBranch){
int start_of_branch(int rid, int eType){
  Stmt q;
  int rc;
  int ans = rid;
  char *zBr;
  char *zBr = branch_of_rid(rid);
  zBr = db_text("trunk","SELECT value FROM tagxref"
                        " WHERE rid=%d AND tagid=%d"
                        " AND tagtype>0",
                        rid, TAG_BRANCH);
  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)"
    "  FROM plink"
    " WHERE cid=:cid AND isprim",
    TAG_BRANCH, zBr
    "    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);
  do{
    db_reset(&q);
    db_bind_int(&q, ":cid", rid);
    rc = db_step(&q);
    if( rc!=SQLITE_ROW ) break;
  rc = db_step(&q);
  if( rc==SQLITE_ROW ){
    if( inBranch && db_column_int(&q,1)==0 ) break;
    rid = db_column_int(&q, 0);
    ans = db_column_int(&q, 0);
  }while( db_column_int(&q, 1)==1 && rid>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 a artifact
**   *  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 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 vid;
  int rid = 0;
  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]=='*' || zType[0]=='c') ){
  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", and "next" */
  /* special keywords: "prev", "previous", "current", "ckout", and
  if( g.localOpen && (vid=db_lget_int("checkout",0))!=0 ){
  ** "next" */
  if( (zType[0]=='*' || isCheckin!=0) && 0<ridCkout ){
    if( fossil_strcmp(zTag, "current")==0 ){
      rid = vid;
      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", vid);
      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", vid);
                      "  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",
      &zTag[5], zType);
      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",
      zTag, zType);
      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",
      &zTag[4], zType);
      fossil_roundup_date(&zTag[4]), zType);
    return rid;
  }

  /* "tag:" + symbolic-name */
  if( memcmp(zTag, "tag:", 4)==0 ){
    rid = db_int(0,
       "SELECT event.objid, max(event.mtime)"
       "  FROM tag, tagxref, event"
       " WHERE tag.tagname='sym-%q' "
       "   AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 "
       "   AND event.objid=tagxref.rid "
       "   AND event.type GLOB '%q'",
       &zTag[4], zType
    rid = most_recent_event_with_tag(&zTag[4], zType);
    );
    if( startOfBranch ) rid = start_of_branch(rid,1);
    return rid;
  }

  /* root:TAG -> The origin of the branch */
  if( memcmp(zTag, "root:", 5)==0 ){
  /* 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-10 && zTag[i]!=':'; i++){}
  if( zTag[i]==':' && fossil_isdate(&zTag[i+1]) ){
  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)"
      "   AND event.mtime<=julianday(%Q,fromLocal())"
      "   AND event.type GLOB '%q'",
      zTagBase, zDate, zType
      zTagBase, fossil_roundup_date(zXDate), zType
    );
    fossil_free(zDate);
    fossil_free(zTagBase);
    return rid;
  }

  /* Remove optional [...] */
  zXTag = zTag;
  nXTag = nTag;
  if( zXTag[0]=='[' ){
252
253
254
255
256
257
258
259

260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283














284
285
286
287












288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
























304
305
306
307
308
309
310
311







312
313

314
315



316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332

333
334
335
336
337
338
339
340
341
342
343
344
345


346
347
348
349

350
351
352
353

354


355
356
357
358
359
360
361
362
363



364
365
366
367
368
369
370
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







-
+














-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+




+
+
+
+
+
+
+
+
+
+
+
+
















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





-
-
-
+
+
+
+
+
+
+

-
+

-
+
+
+
















-
+











-
-
+
+



-
+




+
-
+
+







-
-
+
+
+







    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, event"
        "  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;
  }

  /* Symbolic name */
  rid = db_int(0,
    "SELECT event.objid, max(event.mtime)"
    "  FROM tag, tagxref, event"
    " WHERE tag.tagname='sym-%q' "
    "   AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 "
    "   AND event.objid=tagxref.rid "
    "   AND event.type GLOB '%q'",
    zTag, zType
  );
  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 UUID which might be in mixed
** case and might only be a prefix of the full UUID and converts it
** into the full-length UUID in canonical form.
** 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 UUID or a UUID prefix, then try to resolve
** 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.
** If the input name matches "tag:*" then always resolve as a tag.
** 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, "not found: %s", zName);
    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 a UUID, as described for
** name_to_uuid(). zType is also as described for that function. If
** 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 UUID, which must eventually be free()d by the caller.
** 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)
    *pUuid = db_text(NULL, "SELECT uuid FROM blob WHERE rid=%d", rid);
      ? 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 UUID based on its length on UUIDs no shorter
** than 4 characters in length.
** 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,
381
382
383
384
385
386
387


388


389
390
391

392


393



394






395
396
397
398
399
400
401
402
403










404
405
406
407
408
409
410
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







+
+
-
+
+



+

+
+

+
+
+

+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+







  }
  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.
** 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, "*") ){
      fossil_print("ERROR: %s\n", g.zErrMsg);
      fossil_error_reset();
    }else{
      fossil_print("%s\n", blob_buffer(&name));
    }
    blob_reset(&name);
      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:
**
423
424
425
426
427
428
429
430

431
432
433
434
435
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
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







-
+






+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







+
+
+




-
+






-
+










-
+


















-
+


-
+















-
+





-
+



















-
+






+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


-
+











-
+

















+







  int rid;

  if( zName==0 || zName[0]==0 ) return 0;
  rid = symbolic_name_to_rid(zName, zType);
  if( rid<0 ){
    fossil_fatal("ambiguous name: %s", zName);
  }else if( rid==0 ){
    fossil_fatal("not found: %s", zName);
    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 = P("src");
  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 id <b>%h(zName)</b> is ambiguous and might
  @ <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);
    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_uuid(zUuid);
    hyperlink_to_version(zUuid);
    @ - %h(zTitle).
    @ <ul><li>
    object_description(rid, 0, 0);
    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);
    object_description(rid, 0, 0, 0);
    @ </li></ul>
    @ </p></li>
  }
  @ </ol>
  db_finalize(&q);
  style_footer();
  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("%s/ambiguous/%T?src=%t", g.zTop, zName, g.zPath);
    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 verboseFlag){
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( verboseFlag ){
    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:       ";
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
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







-
+
+












-
-
+
+
+

+
+
+






-
+
+







      case 't':  zType = "Ticket-change";  break;
      case 'g':  zType = "Tag-change";     break;
      default:   zType = "Unknown";        break;
    }
    fossil_print("type:       %s by %s on %s\n", zType, db_column_text(&q,2),
                 db_column_text(&q, 1));
    fossil_print("comment:    ");
    comment_print(db_column_text(&q,3), 0, 12, -1, g.comFmtFlags);
    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 DESC /*sort*/",
    rid);
    " 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, g.comFmtFlags);
    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,"
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
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







-
+









-
+
+


+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+












-
+
+
+

-
+



-
+
-
-
+



-
+
+
+
+
+
+
+







-
+

-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
-
-
+
+
+
+
+
+

-
-
-
-
+
+

-
-
+







    " 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( verboseFlag ){
    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, g.comFmtFlags);
    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').
**                         'w', 'g', or 'e')
**    -v|--verbose         Provide extra information (such as the RID)
*/
void whatis_cmd(void){
  int rid;
  int mFlags = 0;
  const char *zName;
  int verboseFlag;
  int fileFlag;
  int i;
  const char *zType = 0;
  db_find_and_open_repository(0,0);
  verboseFlag = find_option("verbose","v",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++){
    zName = g.argv[i];
    const char *zName = g.argv[i];
    if( i>2 ) fossil_print("%.79c\n",'-');
    rid = symbolic_name_to_rid(zName, zType);
    if( rid<0 ){
      Stmt q;
      int cnt = 0;
      fossil_print("name:       %s (ambiguous)\n", zName);
      db_prepare(&q,
    if( fileFlag ){
      Blob in;
      Blob hash = empty_blob;
      const char *zHash;
      /* Always follow symlinks (when applicable) */
      blob_read_from_file(&in, zName, ExtFILE);

         "SELECT rid FROM blob WHERE uuid>=lower(%Q) AND uuid<(lower(%Q)||'z')",
         zName, zName
      );
      while( db_step(&q)==SQLITE_ROW ){
      /* 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( cnt++ ) fossil_print("%12s---- meaning #%d ----\n", " ", cnt);
        whatis_rid(db_column_int(&q, 0), verboseFlag);
      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);
      }
      db_finalize(&q);
    }else if( rid==0 ){
                 /* 0123456789 12 */
      fossil_print("unknown:    %s\n", zName);
      whatis_artifact(zHash, zName, zType, mFlags);
      blob_reset(&hash);
    }else{
      fossil_print("name:       %s\n", zName);
      whatis_rid(rid, verboseFlag);
      whatis_artifact(zName, 0, zType, mFlags);
    }
  }
}

/*
** COMMAND: test-whatis-all
**
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
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







-
+
+

-
+

+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+











-
-
-
+
+
+
+
+
+
+



-
+




-
-
+
+
+










-
-
+
+









-
-
+
+
+










-
-
+
+











-
-
+
+











-
-
+
+











-
-
+
+
+







-
-
+
+











-
-
+
+
+










-
+

-
-
-
+
+
+
+

-
+
+
+







+
+
+
+















-
+








-
-
+
+
+







*/
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, checkin, wiki, ticket, etc.
@   type TEXT,                     -- file, check-in, wiki, ticket, etc.
@   rcvid INT,                     -- When the artifact was received
@   summary TEXT,                  -- Summary comment for the object
@   detail TEXT                    -- File name, check-in comment, etc
@   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,ctime,type,summary)\n"
    "SELECT blob.rid, blob.uuid, event.mtime, 'checkin',\n"
    "       'check-in on ' || strftime('%%Y-%%m-%%d %%H:%%M',event.mtime)\n"
    "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;",
    zWhere /*safe-for-%s*/
    TAG_BRANCH, zWhere /*safe-for-%s*/
  );

  /* Describe files */
  db_multi_exec(
    "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n"
    "SELECT blob.rid, blob.uuid, event.mtime, 'file', 'file '||filename.name\n"
    "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,ctime,type,summary)\n"
    "SELECT blob.rid, blob.uuid, tagxref.mtime, 'tag',\n"
   "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,ctime,type,summary)\n"
    "SELECT blob.rid, blob.uuid, rcvfrom.mtime, 'cluster', 'cluster'\n"
    "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,ctime,type,summary)\n"
    "SELECT blob.rid, blob.uuid, tagxref.mtime, 'ticket',\n"
    "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,ctime,type,summary)\n"
    "SELECT blob.rid, blob.uuid, tagxref.mtime, 'wiki',\n"
    "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,ctime,type,summary)\n"
    "SELECT blob.rid, blob.uuid, tagxref.mtime, 'event',\n"
    "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,ctime,type,summary)\n"
    "SELECT blob.rid, blob.uuid, attachment.mtime, 'attach-control',\n"
    "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,ctime,type,summary)\n"
    "SELECT blob.rid, blob.uuid, attachment.mtime, 'attachment',\n"
    "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,ctime,type,summary)\n"
      "SELECT postblob.rid, postblob.uuid, forumpost.fmtime, 'forumpost',\n"
      "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*/
    );
  }

  /* Everything else */
  /* Mark all other artifacts as "unknown" for now */
  db_multi_exec(
    "INSERT OR IGNORE INTO description(rid,uuid,type,summary)\n"
    "SELECT blob.rid, blob.uuid,"
    "       CASE WHEN blob.size<0 THEN 'phantom' ELSE '' END,\n"
    "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 WHERE (blob.rid %s);",
    "  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, isPrivate\n"
    "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", db_column_text(&q,0), db_column_text(&q,1));
    if( db_column_int(&q,2) ) fossil_print(" (unpublished)");
    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;
}
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
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







-
+
+







-
+
+







+




+
+
+
+
+
+
+
+
+
+




-
+








-
+


-
+


-
+

+
+






-
+
+








-
+
+
+
+
+
+





+
+
+
+
+
+







+
+
+
+
+
+
+
+

+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+





-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+








-
+







/*
** 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
**   unpub       Show only unpublished artifacts
**   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 unpubOnly = PB("unpub");
  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( !unpubOnly && mx>n && P("s")==0 ){
  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_footer();
    style_finish_page();
    return;
  }
  if( !unpubOnly && mx>n ){
  if( phantomOnly || privOnly || mx>n ){
    style_submenu_element("Index", "bloblist");
  }
  if( unpubOnly ){
  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 FROM description ORDER BY rid"
    "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="0" cellspacing="0">
  @ <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>&nbsp;%z(href("%R/info/%!S",zUuid))%S(zUuid)</a>&nbsp;</td>
    if( g.perm.Admin ){
      int rcvid = db_column_int(&q,5);
      if( rcvid<=0 ){
        @ <td>&nbsp;
      }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>&nbsp;
    }
    if( isPriv || isPhantom ){
    if( isPriv ){
      @ <td>(unpublished)</td>
      if( isPriv==0 ){
        @ <td>phantom</td>
      }else if( isPhantom==0 ){
        @ <td>private</td>
      }else{
        @ <td>private,phantom</td>
      }
    }else{
      @ <td>&nbsp;
    }
    @ </tr>
  }
  @ </table>
  db_finalize(&q);
  style_footer();
  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>&nbsp;
      }
    }else{
      @ <td>&nbsp;<td>&nbsp;
    }
    @ </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
**   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; }
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
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







-
+



















-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







    "  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">Delta From<th>Hash<th>Description<th>Date</tr></thead>
  @ <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>&nbsp;%z(href("%R/info/%!S",zUuid))%S(zUuid)</a>&nbsp;</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_footer();
  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>&nbsp;%z(href("%R/info/%!S",zUuid))%S(zUuid)</a>&nbsp;</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>&nbsp;&nbsp;&nbsp;</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
**
1205
1206
1207
1208
1209
1210
1211
1212

1213
1214
1215
1216
1217
1218
1219
2084
2085
2086
2087
2088
2089
2090

2091
2092
2093
2094
2095
2096
2097
2098







-
+







#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, kk;
  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];
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
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







-



-








-
+







  @ </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>
  kk = 0;
  for(i=HNAME_MAX; i>=4; i--){
    if( aCollide[i].cnt==0 ) continue;
    if( aCollide[i].cnt>200 ) break;
    kk += aCollide[i].cnt;
    if( aCollide[i].cnt<25 ){
      @ <p>Collisions of length %d(i):
    }else{
      @ <p>First 25 collisions of length %d(i):
    }
    for(j=0; j<aCollide[i].cnt && j<MAX_COLLIDE; j++){
      char *zId = aCollide[i].azHit[j];
      if( zId==0 ) continue;
      @ %z(href("%R/whatis/%s",zId))%h(zId)</a>
      @ %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]);
    }
  }
1282
1283
1284
1285
1286
1287
1288
1289

1290
2159
2160
2161
2162
2163
2164
2165

2166
2167







-
+

  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_footer();
  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;
  }
}

Changes to src/path.c.

42
43
44
45
46
47
48

49
50
51
52
53
54
55
56
57
58
59
60
61
62
63





64
65
66
67
68
69
70
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76







+















+
+
+
+
+







** 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) );
119
120
121
122
123
124
125
126


127
128
129
130
131
132
133
125
126
127
128
129
130
131

132
133
134
135
136
137
138
139
140







-
+
+







**
** 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 */
  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);
163
164
165
166
167
168
169

170
171
172
173



174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194




195
196




















197
198
199
200
201
202
203
204
205
206
207
208
209
210

211
212
213
214
215
216
217
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203


204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242

243
244
245
246
247
248
249
250







+




+
+
+



















-
-
+
+
+
+


+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+













-
+







    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.nStep<2 ) return 0;
  for(p=path.pEnd, i=0; p && i<path.nStep/2; p=p->pFrom, 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);
  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;"
  );
245
246
247
248
249
250
251
252

253
254
255
256
257
258
259
278
279
280
281
282
283
284

285
286
287
288
289
290
291
292







-
+








  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);
  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)"
396
397
398
399
400
401
402
403

404
405
406
407
408
409
410
429
430
431
432
433
434
435

436
437
438
439
440
441
442
443







-
+







** 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 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 */
418
419
420
421
422
423
424
425

426
427
428
429
430
431
432
451
452
453
454
455
456
457

458
459
460
461
462
463
464
465







-
+







  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);
  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"
  );
516
517
518
519
520
521
522
523

524
525
526
527

528
529
530
531
532

533
534
535
536
537
538
539
549
550
551
552
553
554
555

556
557
558
559

560
561
562
563
564

565
566
567
568
569
570
571
572







-
+



-
+




-
+







void test_name_change(void){
  int iFrom;
  int iTo;
  int *aChng;
  int nChng;
  int i;
  const char *zDebug = 0;
  int revOk = 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;
  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);
    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]);
582
583
584
585
586
587
588
589

590
591
592
593
594
595
596
597
598

599
600
601
602
603
604
605
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







-
+









+







@  GROUP BY 2, 3;
;

/*
** WEBPAGE: test-rename-list
**
** Print a list of all file rename operations throughout history.
** This page is intended for for testing purposes only and may change
** 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*/);
626
627
628
629
630
631
632
633

634
660
661
662
663
664
665
666

667
668







-
+

    @ <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_footer();
  style_finish_page();
}

Changes to src/piechart.c.

140
141
142
143
144
145
146
147




148
149
150
151
152
153
154
155
156
157
158

159
160
161
162
163
164
165
140
141
142
143
144
145
146

147
148
149
150
151
152
153
154
155
156
157
158
159
160

161
162
163
164
165
166
167
168







-
+
+
+
+










-
+







  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 ) return;
  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_double(&q, 1);
      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);
272
273
274
275
276
277
278

279
280
281
282
283
284
285
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289







+







  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;
306
307
308
309
310
311
312
313

314
315
316
317


318
319

320
321
322
323
324
325
326
327
328
329
330

331
310
311
312
313
314
315
316

317
318
319


320
321
322

323
324
325
326
327
328
329
330
331
332
333

334
335







-
+


-
-
+
+

-
+










-
+

    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 />
    @ <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 />
  @ <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 />
  @ 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_footer();
  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
35

36
37
38
39
40
41
42
28
29
30
31
32
33
34

35
36
37
38
39
40
41
42







-
+







** 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 checkout are the secondaries.
** 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("
60
61
62
63
64
65
66
67

68
69
70
71
72
73
74
60
61
62
63
64
65
66

67
68
69
70
71
72
73
74







-
+








/*
** 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 primary record */
  /* 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
  );
}

150
151
152
153
154
155
156
157

158
159

160

161

162
163
164
165
166

167
168
169


170
171
172
173
174
175
176
177
178



179
180


181

182
183
184
185
186
187
188
150
151
152
153
154
155
156

157
158

159
160
161

162
163
164
165
166

167
168
169

170
171
172
173
174
175
176
177
178
179
180
181
182
183


184
185

186
187
188
189
190
191
192
193







-
+

-
+

+
-
+




-
+


-
+
+









+
+
+
-
-
+
+
-
+







  db_finalize(&q2);
  db_finalize(&i1);
  db_finalize(&u1);
  return rid;
}

/*
** COMMAND: test-find-pivot
** COMMAND: merge-base
**
** Usage: %fossil test-find-pivot ?options? PRIMARY SECONDARY ...
** Usage: %fossil merge-base ?options? PRIMARY SECONDARY ...
**
** Find a common ancestor given two or more check-in versions to
** Test the pivot_find() procedure.
** hypothetically merge.
**
** Options:
**    --ignore-merges       Ignore merges for discovering name pivots
*/
void test_find_pivot(void){
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;
  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)
    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"

Changes to src/popen.c.

119
120
121
122
123
124
125
126







127
128
129
130
131
132
133
119
120
121
122
123
124
125

126
127
128
129
130
131
132
133
134
135
136
137
138
139







-
+
+
+
+
+
+
+







** *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, int *pfdIn, FILE **ppOut, int *pChildPid){
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);
175
176
177
178
179
180
181
182
183
184
185
186

187
188
189
190
191

192
193



194


195
196
197
198
199
200
201
181
182
183
184
185
186
187

188
189
190

191
192
193
194
195

196
197
198
199
200
201

202
203
204
205
206
207
208
209
210







-



-
+




-
+


+
+
+
-
+
+







    close(pout[1]);
    *pChildPid = 0;
    return 1;
  }
  signal(SIGPIPE,SIG_IGN);
  if( *pChildPid==0 ){
    int fd;
    int nErr = 0;
    /* This is the child process */
    close(0);
    fd = dup(pout[0]);
    if( fd!=0 ) nErr++;
    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 ) nErr++;
    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);
      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");

Changes to src/pqueue.c.

40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
40
41
42
43
44
45
46

47
48
49
50
51
52
53







-







** Integers must be positive.
*/
struct PQueue {
  int cnt;   /* Number of entries in the queue */
  int sz;    /* Number of slots in a[] */
  struct QueueElement {
    int id;          /* ID of the element */
    void *p;         /* Content pointer */
    double value;    /* Value of element.  Kept in ascending order */
  } *a;
};
#endif

/*
** Initialize a PQueue structure
72
73
74
75
76
77
78
79

80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103

104
105
106
107
108
109
110
111
112
113
114
115
116
71
72
73
74
75
76
77

78
79
80
81
82
83
84
85
86
87
88
89
90
91

92
93
94
95
96
97
98
99
100

101
102
103

104
105
106

107
108
109
110
111
112







-
+













-









-
+


-



-






  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, void *pData){
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].p = pData;
  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, void **pp){
int pqueuex_extract(PQueue *p){
  int e, i;
  if( p->cnt==0 ){
    if( pp ) *pp = 0;
    return 0;
  }
  e = p->a[0].id;
  if( pp ) *pp = p->a[0].p;
  for(i=0; i<p->cnt-1; i++){
    p->a[i] = p->a[i+1];
  }
  p->cnt--;
  return e;
}

Changes to src/printf.c.

32
33
34
35
36
37
38
39

40
41
42
43
44
45
46
47
48
49
50
51
52
53

54
55
56
57
58
59

60
61
62

63
64
65
66
67
68
69
70
71

72
73
74
75
76
77
78
32
33
34
35
36
37
38

39
40
41
42
43
44
45
46
47
48
49
50
51
52

53
54
55
56
57
58

59
60
61

62
63
64
65
66
67
68
69
70

71
72
73
74
75
76
77
78







-
+













-
+





-
+


-
+








-
+







**      %S        Prefix of a length appropriate for human display
**
** The following macros help determine those lengths.  FOSSIL_HASH_DIGITS
** is the default number of digits to display to humans.  This value can
** be overridden using the hash-digits setting.  FOSSIL_HASH_DIGITS_URL
** is the minimum number of digits to be used in URLs.  The number used
** will always be at least 6 more than the number used for human output,
** or 40 if the number of digits in human output is 34 or more.
** 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.
*/
static int hashDigits(int bForUrl){
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 > 40 ) nDigitHuman = 40;
    if( nDigitHuman > HNAME_MAX ) nDigitHuman = HNAME_MAX;
    nDigitUrl = nDigitHuman + 6;
    if( nDigitUrl < FOSSIL_HASH_DIGITS_URL ) nDigitUrl = FOSSIL_HASH_DIGITS_URL;
    if( nDigitUrl > 40 ) nDigitUrl = 40;
    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 hashDigits(0);
  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 */
95
96
97
98
99
100
101
102

103






104
105
106
107
108
109
110
95
96
97
98
99
100
101

102
103
104
105
106
107
108
109
110
111
112
113
114
115
116







-
+

+
+
+
+
+
+







#define etPOINTER    16 /* The %p conversion */
#define etHTMLIZE    17 /* Make text safe for HTML */
#define etHTTPIZE    18 /* Make text safe for HTTP.  "/" encoded as %2f */
#define etURLIZE     19 /* Make text safe for HTTP.  "/" not encoded */
#define etFOSSILIZE  20 /* The fossil header encoding format. */
#define etPATH       21 /* Path type */
#define etWIKISTR    22 /* Timeline comment text rendered from a char*: %W */
#define etSTRINGID   23 /* String with length limit for a UUID prefix: %S */
#define 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;

128
129
130
131
132
133
134



135
136
137

138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154

155
156
157
158
159
160
161
162
163
164
165
166
167
168


169
170













171
172
173
174
175
176
177
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
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:
200
201
202
203
204
205
206



207
208
209
210
211

212
213
214
215
216
217
218
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248







+
+
+





+








/*
** 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
227
228
229
230
231
232
233



234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253

254
255
256
257
258
259
260
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285

286
287
288
289
290
291
292
293







+
+
+



















-
+







      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 a error.
**          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 */
291
292
293
294
295
296
297

298
299
300
301
302
303
304

305
306
307





308
309

310
311
312
313
314
315
316
324
325
326
327
328
329
330
331
332
333
334
335
336

337
338



339
340
341
342
343


344
345
346
347
348
349
350
351







+





-

+
-
-
-
+
+
+
+
+
-
-
+







#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!='%' ){
      int amt;
      bufpt = (char *)fmt;
#if HAVE_STRCHRNUL
      amt = 1;
      while( (c=(*++fmt))!='%' && c!=0 ) amt++;
      blob_append(pBlob,bufpt,amt);
      fmt = strchrnul(fmt, '%');
#else
      do{ fmt++; }while( *fmt && *fmt != '%' );
#endif
      blob_append(pBlob, bufpt, (int)(fmt - bufpt));
      count += amt;
      if( c==0 ) break;
      if( *fmt==0 ) break;
    }
    if( (c=(*++fmt))==0 ){
      errorflag = 1;
      blob_append(pBlob,"%",1);
      count++;
      break;
    }
375
376
377
378
379
380
381
382

383
384
385
386
387



388
389



390
391
392
393
394
395
396
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 */
    infop = 0;
    zFmtLookup = strchr(fmtchr,c);
    xtype = etERROR;
    for(idx=0; idx<etNINFO; idx++){
      if( c==fmtinfo[idx].fmttype ){
        infop = &fmtinfo[idx];
        xtype = infop->type;
    if( zFmtLookup ){
      infop = &fmtinfo[zFmtLookup-fmtchr];
      xtype = infop->type;
        break;
      }
    }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;
    }
672
673
674
675
676
677
678
679

680
681
682
683
684
685
686
706
707
708
709
710
711
712

713
714
715
716
717
718
719
720







-
+







        int limit = flag_alternateform ? va_arg(ap,int) : -1;
        bufpt = va_arg(ap,char*);
        if( bufpt==0 ){
          bufpt = "";
        }else if( xtype==etDYNSTRING ){
          zExtra = bufpt;
        }else if( xtype==etSTRINGID ){
          precision = hashDigits(flag_altform2);
          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;
777
778
779
780
781
782
783














784
785
786
787
788
789
790
791
792
793






794
795
796
797
798
799
800
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+










+
+
+
+
+
+







        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;
        idx = 1+(c!=0);
        blob_append(pBlob,"%",idx);
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
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







-
+















-
+







    ** the output.
    */
    if( !flag_leftjustify ){
      register int nspace;
      nspace = width-length;
      if( nspace>0 ){
        count += nspace;
        while( nspace>=etSPACESIZE ){
        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>=etSPACESIZE ){
        while( nspace>=(int)etSPACESIZE ){
          blob_append(pBlob,spaces,etSPACESIZE);
          nspace -= etSPACESIZE;
        }
        if( nspace>0 ) blob_append(pBlob,spaces,nspace);
      }
    }
    if( zExtra ){
902
903
904
905
906
907
908
909

910
911
912
913
914
915
916
917
918
956
957
958
959
960
961
962

963
964

965
966
967
968
969
970
971







-
+

-







** 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){
void fossil_puts(const char *z, int toStdErr, int n){
  FILE* out = (toStdErr ? stderr : stdout);
  int n = (int)strlen(z);
  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;
  }
928
929
930
931
932
933
934
935

936
937
938
939
940
941
942
981
982
983
984
985
986
987

988
989
990
991
992
993
994
995







-
+








/*
** 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);
    fossil_puts("\n", 0, 1);
    return 1;
  }
  return 0;
}

/*
** Indicate that the cursor has moved to the start of a line by means
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
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







-
-
+
-
-


+
+
+
+
+
+
+











-
+







+
+
+







+















-
+


+
+
+
+



+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+

















-
+
+
+
+
+
+
+

-
+

+
+
+
+








+

+


-
+










-
+

-
+

+
-
-
-
+
+
+
+
+
+
+





-
+
+







*/
void fossil_print(const char *zFormat, ...){
  va_list ap;
  va_start(ap, zFormat);
  if( g.cgiOutput ){
    cgi_vprintf(zFormat, ap);
  }else{
    Blob b = empty_blob;
    vxprintf(&b, zFormat, ap);
    vxprintf(0, zFormat, ap);
    fossil_puts(blob_str(&b), 0);
    blob_reset(&b);
  }
  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_str(&b), 1);
  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+1,
          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);
    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 ){
  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 ){
    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_footer();
    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.
** routines never return and produce a non-zero process exit status.
**
** The only different between fossil_fatal() and fossil_panic() is that
** 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
** does not.  If there is not error log, then both routines work the
** same.  Hence, the routines are interchangable for commands and only
** make a difference with processing web pages.
** 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.
** 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;

1092
1093
1094
1095
1096
1097
1098


1099
1100

1101

1102
1103
1104
1105
1106
1107
1108
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190

1191
1192
1193
1194
1195
1196
1197
1198







+
+


+
-
+







  }
  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);
  va_list ap;
  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();
1145
1146
1147
1148
1149
1150
1151
1152







1153
1154
1155
1156
1157

1158
1159
1160
1161
1162
1163
1164
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







-
+
+
+
+
+
+
+





+







  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){
  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);

Changes to src/publish.c.

19
20
21
22
23
24
25
26

27
28
29
30
31
32
33
34
35
36

37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

56
57
58
59
60
61
62
19
20
21
22
23
24
25

26
27
28
29
30
31
32
33
34
35

36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

55
56
57
58
59
60
61
62







-
+









-
+


















-
+







** "unpublished" commands.
*/
#include "config.h"
#include "publish.h"
#include <assert.h>

/*
** COMMAND: unpublished
** 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:
** 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
** 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.
**

Changes to src/purge.c.

1
2
3
4
5
6
7

8
9
10
11
12
13
14
1
2
3
4
5
6

7
8
9
10
11
12
13
14






-
+







/*
** 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/
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
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







-
-
-
+
+
+

-
-
+
+

-
+

-
+


-
+




-
+






-
+






-
+




-
+





-
+



-
+



-
+





-
+

-
-
-
-
-
-
-
-
-
-
-







** 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!       ====
** 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.                                                     ====
** FURTHER WARNING: This command is a work-in-progress and may yet
** contain bugs.
**
**   fossil purge artifacts UUID... ?OPTIONS?
** > fossil purge artifacts HASH... ?OPTIONS?
**
**      Move arbitrary artifacts identified by the UUID list into the
**      Move arbitrary artifacts identified by the HASH list into the
**      graveyard.
**
**   fossil purge cat UUID...
** > fossil purge cat HASH...
**
**      Write the content of one or more artifacts in the graveyard onto
**      standard output.
**
**   fossil purge checkins TAGS... ?OPTIONS?
** > 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?
** > 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?
** > 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?
** > 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?
** > fossil purge tickets NAME ... ?OPTIONS?
**
**      TBD...
**
**   fossil purge undo ID
** > fossil purge undo ID
**
**      Restore the content previously removed by purge ID.
**
**   fossil purge wiki NAME ... ?OPTIONS?
** > fossil purge wiki NAME ... ?OPTIONS?
**
**      TBD...
**
** COMMON OPTIONS:
**
**   --explain         Make no changes, but show what would happen.
**   --explain         Make no changes, but show what would happen
**   --dry-run         An alias for --explain
**
** SUMMARY:
**   fossil purge artifacts UUID.. [OPTIONS]
**   fossil purge cat UUID...
**   fossil purge checkins TAGS... [OPTIONS]
**   fossil purge files FILENAME... [OPTIONS]
**   fossil purge list
**   fossil purge obliterate ID...
**   fossil purge tickets NAME... [OPTIONS]
**   fossil purge undo ID
**   fossil purge wiki NAME... [OPTIONS]
*/
void purge_cmd(void){
  int purgeFlags = PURGE_MOVETO_GRAVEYARD | PURGE_PRINT_SUMMARY;
  const char *zSubcmd;
  int n;
  int i;
  Stmt q;
545
546
547
548
549
550
551
552

553
554
555
556
557
558
559
534
535
536
537
538
539
540

541
542
543
544
545
546
547
548







-
+







    }
    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 UUID...");
    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);
569
570
571
572
573
574
575
576

577
578
579
580
581
582
583
558
559
560
561
562
563
564

565
566
567
568
569
570
571
572







-
+







    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 checkout");
      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();

Changes to src/rebuild.c.

50
51
52
53
54
55
56

57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76

77
78
79
80
81

82
83
84
85

86
87
88
89
90
91
92
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96







+




















+





+




+







    );
    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(
142
143
144
145
146
147
148
149

150
151
152
153
154
155
156

157

158
159
160

161
162
163

164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184




185
186


187

188
189
190
191
192
193
194
195
196
197
198
199
200
201
202







203
204
205
206
207
208
209
146
147
148
149
150
151
152

153
154
155
156
157
158
159
160
161
162
163
164
165

166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187




188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226







-
+







+

+


-
+



+

















-
-
-
-
+
+
+
+


+
+

+















+
+
+
+
+
+
+








/*
** 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_master"
  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_master SET sql=%Q WHERE name LIKE 'blob';"
           "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 of records rebuilt */
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);
237
238
239
240
241
242
243
244

245
246
247
248
249
250
251
254
255
256
257
258
259
260

261
262
263
264
265
266
267
268







-
+







  Blob copy;
  Blob *pUse;
  int nChild, i, cid;

  while( rid>0 ){

    /* Fix up the "blob.size" field if needed. */
    if( size!=blob_size(pBase) ){
    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");
272
273
274
275
276
277
278











279
280
281
282
283
284
285
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313







+
+
+
+
+
+
+
+
+
+
+







      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);

338
339
340
341
342
343
344
345
346
347
348
349
350
351

352
353
354
355
356
357

358
359
360
361
362
363

364
365

366
367

368
369
370
371
372
373

374
375
376
377
378
379
380
366
367
368
369
370
371
372





373

374
375
376
377
378
379

380
381
382
383
384
385

386
387
388
389
390

391
392
393
394
395
396

397
398
399
400
401
402
403
404







-
-
-
-
-

-
+





-
+





-
+


+

-
+





-
+








/*
** 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.
**
** If the randomize parameter is true, then the BLOBs are deliberately
** extracted in a random order.  This feature is used to test the
** ability of fossil to accept records in any order and still
** construct a sane repository.
*/
int rebuild_db(int randomize, int doOut, int doClustering){
int rebuild_db(int doOut, int doClustering){
  Stmt s, q;
  int errCnt = 0;
  int incrSize;
  Blob sql;

  bag_init(&bagDone);
  bag_clear(&bagDone);
  ttyOutput = doOut;
  processCnt = 0;
  if (ttyOutput && !g.fQuiet) {
    percent_complete(0);
  }
  alert_triggers_disable();
  manifest_disable_event_triggers();
  rebuild_update_schema();
  blob_init(&sql, 0, 0);
  db_unprotect(PROTECT_ALL);
  db_prepare(&q,
     "SELECT name FROM sqlite_master /*scan*/"
     "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','alert_bounce')"
                       "'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);
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
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







-




+










-
+
+

-
+





+
+
+
















-
+
+
+
+
+







    percent_complete((processCnt*1000)/totalSize);
  }
  if( doClustering ) create_cluster();
  if( ttyOutput && !g.fQuiet && totalSize>0 ){
    processCnt += incrSize;
    percent_complete((processCnt*1000)/totalSize);
  }
  alert_triggers_enable();
  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.
** storage efficiency.  Return the number of bytes of storage space
** saved.
*/
void extra_deltification(void){
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 ){
      content_deltify(rid, aPrev, 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;
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
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







-
+
+
+
+
+












+
+







  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 ){
      content_deltify(rid, aPrev, 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.
*/
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
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+















-





-
+

-



-
-



-



















-


-
+








-
+







        " 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
**   --deanalyze       Remove ANALYZE tables from the database
**   --force           Force the rebuild to complete even if errors are seen
**   --ifneeded        Only do the rebuild if it would change the schema version
**   --index           Always add in the full-text search index
**   --noverify        Skip the verification of changes to the BLOB table
**   --noindex         Always omit the full-text search index
**   --pagesize N      Set the database pagesize to N. (512..65536 and power of 2)
**   --pagesize N      Set the database pagesize to N (512..65536, power of 2)
**   --quiet           Only show output if there are errors
**   --randomize       Scan artifacts in a random order
**   --stats           Show artifact statistics after rebuilding
**   --vacuum          Run VACUUM on the database after rebuilding
**   --wal             Set Write-Ahead-Log journalling mode on the database
**
** See also: deconstruct, reconstruct
*/
void rebuild_database(void){
  int forceFlag;
  int randomizeFlag;
  int errCnt = 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;
  randomizeFlag = find_option("randomize", 0, 0)!=0;
  doClustering = find_option("cluster", 0, 0)!=0;
  runVacuum = find_option("vacuum",0,0)!=0;
  runDeanalyze = find_option("deanalyze",0,0)!=0;
  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 = runVacuum = 1;
  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");
    }
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
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







+



-
+
















+
+

-
-
+
+
+
+
+
+
+
+
+
+
+
+



-
+







    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(randomizeFlag, 1, doClustering);
    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);
      extra_deltification();
      runVacuum = 1;
      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("done\n");
    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 ){
699
700
701
702
703
704
705

706
707
708
709
710
711
712
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808







+







      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:" },
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
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







-
+

-
+

-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


+
+
+
+
+
+
+
+
+

+
+
+
+

+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





+

+







      if( k>0 ) subtotal += g.parseCnt[k];
    }
    fossil_print("%-15s %6d\n", "Other:", g.parseCnt[CFTYPE_ANY] - subtotal);
  }
}

/*
** COMMAND: test-detach
** COMMAND: detach*
**
** Usage: %fossil test-detach  ?REPOSITORY?
** Usage: %fossil detach ?REPOSITORY?
**
** Change the project-code and make other changes in order to prevent
** the repository from ever again pushing or pulling to other
** repositories.  Used to create a "test" repository for development
** testing by cloning a working project 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(
    "DELETE FROM config WHERE name='last-sync-url';"
    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.
855
856
857
858
859
860
861
862
863
864



865
866
867
868
869
870
871
1001
1002
1003
1004
1005
1006
1007



1008
1009
1010
1011
1012
1013
1014
1015
1016
1017







-
-
-
+
+
+







** 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)
**   --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);
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
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







+

+

-
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+











+


+



+

+

-
+















+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







  }
  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 GLOB 'last-sync-*';"
      "DELETE FROM config WHERE name IN"
      "DELETE FROM config WHERE name GLOB 'peer-*';"
      "DELETE FROM config WHERE name GLOB 'login-group-*';"
      "DELETE FROM config WHERE name GLOB 'skin:*';"
      "DELETE FROM config WHERE name GLOB 'subrepo:*';"
      "(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(0, 1, 0);
    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;

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
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







-
+






+

+













+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





-
+

-
-
+
+
-
-
+

-
+
+
+
+
+



+
+
+







      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{
      }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 FILENAME DIRECTORY
** 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
** This command studies the artifacts (files) in DIRECTORY and reconstructs the
** Fossil record from them.  It places the new Fossil repository in FILENAME.
** fossil repository in FILENAME. Subdirectories are read, files
** with leading '.' in the filename are ignored.
** Subdirectories are read, files with leading '.' in the filename are ignored.
**
** See also: deconstruct, rebuild
** 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");
  }
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
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







-
+
+
+

+
+
+
+
+
+











-
+
+
+







-
-
-
-
-
-
-
+
+
+
+
+
+


-
-
+
+
+
+

-
+
-
-
+
+
+


-



+

+














+
+







  db_begin_transaction();
  db_initial_setup(0, 0, 0);

  fossil_print("Reading files from directory \"%s\"...\n", g.argv[3]);
  recon_read_dir(g.argv[3]);
  fossil_print("\nBuilding the Fossil repository...\n");

  rebuild_db(0, 1, 1);
  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);
  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.
** This command exports all artifacts of a given repository and writes all
** artifacts to the file system.  The DESTINATION directory will be populated
** with subdirectories AA and files AA/BBBBBBBBB.., where AABBBBBBBBB.. is the
** 40+ character artifact ID, AA the first 2 characters.
** If -L|--prefixlength is given, the length (default 2) of the directory prefix
** can be set to 0,1,..,9 characters.
**
** Options:
**   -R|--repository REPOSITORY  deconstruct given REPOSITORY
**   -L|--prefixlength N         set the length of the names of the DESTINATION
**   -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.
**   --private                   Include private artifacts
**
** See also: rebuild, reconstruct
**   -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 *zDestDir;
  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];
1090
1091
1092
1093
1094
1095
1096

1097
1098
1099
1100
1101
1102
1103
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482







+







  */
#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);
1132
1133
1134
1135
1136
1137
1138








1139
1140
1141
1142
1143
1144
1145
1146
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533







+
+
+
+
+
+
+
+








        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;
}

Changes to src/regexp.c.

9
10
11
12
13
14
15
16

17
18

19
20
21




































22
23
24
25
26
27
28
9
10
11
12
13
14
15

16
17

18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64







-
+

-
+



+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







** 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 test_regexp.c file in SQLite3.  That
** 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 */

85
86
87
88
89
90
91
92

93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112

113
114
115
116
117
118
119
120
121
122
123
124
125
126
127

128
129
130
131
132
133
134
121
122
123
124
125
126
127

128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147

148
149
150
151
152
153
154
155
156
157
158
159
160
161
162

163
164
165
166
167
168
169
170







-
+



















-
+














-
+







};
#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++] = newState;
  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<=0x3ff || (c>=0xd800 && c<=0xdfff) ) c = 0xfffd;
      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,1);
  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=='_';
}

154
155
156
157
158
159
160
161

162
163
164
165
166
167
168
169
170
171
172
173
174
175

176
177
178
179
180
181
182
190
191
192
193
194
195
196

197
198
199
200
201
202
203
204
205
206
207
208
209
210

211
212
213
214
215
216
217
218







-
+













-
+







  int c = RE_EOF+1;
  int cPrev = 0;
  int rc = 0;
  ReInput in;

  in.z = zIn;
  in.i = 0;
  in.mx = nIn>=0 ? nIn : strlen((const char*)zIn);
  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<=count(aSpace)*2 ){
  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;
  }
195
196
197
198
199
200
201
202

203
204
205
206
207
208
209
210

211
212
213
214
215
216
217
218

219
220
221
222
223
224
225
226

227
228
229
230
231
232
233
231
232
233
234
235
236
237

238
239
240
241
242
243
244
245

246
247
248
249
250
251
252
253

254
255
256
257
258
259
260
261

262
263
264
265
266
267
268
269







-
+







-
+







-
+







-
+







      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: {
          re_add_state(pNext, x+1);
          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) ) re_add_state(pNext, x+1);
          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) ) re_add_state(pNext, x+1);
          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) ) re_add_state(pNext, x+1);
          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: {
244
245
246
247
248
249
250
251
252





253
254
255
256
257
258
259
280
281
282
283
284
285
286


287
288
289
290
291
292
293
294
295
296
297
298







-
-
+
+
+
+
+







          re_add_state(pThis, x+pRe->aArg[x]);
          break;
        }
        case RE_OP_ACCEPT: {
          rc = 1;
          goto re_match_end;
        }
        case RE_OP_CC_INC:
        case RE_OP_CC_EXC: {
        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;
305
306
307
308
309
310
311
312

313
314
315
316
317
318
319
344
345
346
347
348
349
350

351
352
353
354
355
356
357
358







-
+







  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] = op;
  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){
594
595
596
597
598
599
600
601

602
603
604
605
606
607
608
633
634
635
636
637
638
639

640
641
642
643
644
645
646
647







-
+







  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 = strlen(zIn);
  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);
620
621
622
623
624
625
626
627
628


629
630
631

632
633

634
635
636

637
638
639
640
641
642
643
659
660
661
662
663
664
665


666
667
668
669

670
671

672
673
674

675
676
677
678
679
680
681
682







-
-
+
+


-
+

-
+


-
+







  ** ".*" (if the input regex lacks an initial "^") and afterwards there are
  ** one or more matching characters, enter those matching characters into
  ** zInit[].  The re_match() routine can then search ahead in the input
  ** string looking for the initial match without having to run the whole
  ** regex engine over the string.  Do not worry able trying to match
  ** unicode characters beyond plane 0 - those are very rare and this is
  ** just an optimization. */
  if( pRe->aOp[0]==RE_OP_ANYSTAR ){
    for(j=0, i=1; j<sizeof(pRe->zInit)-2 && pRe->aOp[i]==RE_OP_MATCH; i++){
  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++] = x;
        pRe->zInit[j++] = (unsigned char)x;
      }else if( x<=0xfff ){
        pRe->zInit[j++] = 0xc0 | (x>>6);
        pRe->zInit[j++] = (unsigned char)(0xc0 | (x>>6));
        pRe->zInit[j++] = 0x80 | (x&0x3f);
      }else if( x<=0xffff ){
        pRe->zInit[j++] = 0xd0 | (x>>12);
        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--;
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
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







+

+




-
+

+







-
+





+
+
+



-
+

-
-
-
-
-
-
-


+
-
-
+
+
+
+
+
+
+
+
+







  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, 0);
    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;
    }
    sqlite3_set_auxdata(context, 0, pRe, (void(*)(void*))re_free);
    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 in order to install the REGEXP function in an
** Invoke this routine to register the regexp() function with the
** SQLite database connection.
**
** Use:
**
**      sqlite3_auto_extension(sqlite3_add_regexp_func);
**
** to cause this extension to be automatically loaded into each new
** database connection.
*/
int re_add_sql_func(sqlite3 *db){
  int rc;
  return sqlite3_create_function(db, "regexp", 2, SQLITE_UTF8, 0,
                                 re_sql_func, 0, 0);
  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;
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
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







+














-




-
+


+
+
-
+
+
+
+














-







  }
}

/*
** 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;
    if( z[j]=='\n' ) j++;
    ln++;
    if( re_match(pRe, (const unsigned char*)(z+i), j-i) ){
      cnt++;
      if( flags & GREP_EXISTS ){
        fossil_print("%s\n", zName);
        if( (flags & GREP_QUIET)==0 && zName ) fossil_print("%s\n", zName);
        break;
      }
      if( (flags & GREP_QUIET)==0 ){
        if( cnt==1 && zName ){
      fossil_print("%s:%d:%.*s\n", zName, ln, n, z+i);
          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 ){
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
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







-
+

-
-
-
+
+
+
+
+
+
+
+


-
-
-
-
+
+
+
+
+
+
+
+
+
+








+
+
+
+
+
+
+
+
+



-
+
+
+
+
+
+
+
+
+
+
+
+



-
+




+
+
+
+
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
-
+
+
+
+
+
+
+



  }
  re_free(pRe);
}

/*
** COMMAND: grep
**
** Usage: %fossil grep [OPTIONS] PATTERN FILENAME
** Usage: %fossil grep [OPTIONS] PATTERN FILENAME ...
**
** Attempt to match the given POSIX extended regular expression PATTERN
** over all historic versions of FILENAME.  For details of the supported
** RE dialect, see https://fossil-scm.org/fossil/doc/trunk/www/grep.md
** 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:
**
**     -i|--ignore-case         Ignore case
**     -l|--files-with-matches  List only checkin ID for versions that match
**     -v|--verbose             Show each file as it is analyzed
**     -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","v",0)!=0 ) bVerbose = 1;
  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");
    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(g.argv[3], &fullName, 0, 0) ){
    int fnid = db_int(0, "SELECT fnid FROM filename WHERE name=%Q",
                      blob_str(&fullName));
    if( fnid ){
    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 ){
      Stmt q;
      add_content_sql_commands(g.db);
      db_prepare(&q,
        "SELECT content(ux), substr(ux,1,10) FROM ("
        "  SELECT blob.uuid AS ux, min(event.mtime) AS mx"
        "    FROM mlink, blob, event"
        "   WHERE mlink.mid=event.objid"
        "     AND mlink.fid=blob.rid"
        "     AND mlink.fnid=%d"
        "   GROUP BY blob.uuid"
        ") ORDER BY mx DESC;",
        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;"
        fnid
      );
      while( db_step(&q)==SQLITE_ROW ){
        if( bVerbose ) fossil_print("%s:\n", db_column_text(&q,1));
        grep_buffer(pRe, db_column_text(&q,1), db_column_text(&q,0), flags);
  );
  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);
  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.

25
26
27
28
29
30
31








32
33
34
35
36
37
38
39
40
41
42

43
44
45
46
47
48
49
50
51

52
53

54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71

72
73
74
75
76
77
78
79

80
81
82
83
84
85

86
87
88
89




90
91
92
93
94
95
96
97
98
99
100

101
102

103
104
105
106
107
108
109
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

50
51
52
53
54
55
56
57
58

59
60

61
62
63
64
65
66
67
68
69
70
71
72
73

74
75
76
77

78
79
80
81
82
83
84
85

86
87
88
89
90
91

92
93
94
95

96
97
98
99
100
101
102
103
104
105
106
107
108
109

110
111

112
113
114
115
116
117
118
119







+
+
+
+
+
+
+
+










-
+








-
+

-
+












-




-
+







-
+





-
+



-
+
+
+
+










-
+

-
+







/* 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;
  int cnt = 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);
  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);
  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);
    cnt++;
    blob_appendf(&ril, "<li>");
    if( zTitle[0] == '_' ){
      blob_appendf(&ril, "%s", zTitle);
    } else {
      blob_appendf(&ril, "%z%h</a>", href("%R/rptview?rn=%d", rn), zTitle);
      blob_appendf(&ril, "%z%h</a>", href("%R/rptview/%d", rn), zTitle);
    }
    blob_appendf(&ril, "&nbsp;&nbsp;&nbsp;");
    if( g.perm.Write && zOwner && zOwner[0] ){
      blob_appendf(&ril, "(by <i>%h</i>) ", zOwner);
    }
    if( g.perm.TktFmt ){
      blob_appendf(&ril, "[%zcopy</a>] ",
                   href("%R/rptedit?rn=%d&copy=1", rn));
                   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?rn=%d", rn));
                         href("%R/rptedit/%d", rn));
    }
    if( g.perm.TktFmt ){
      blob_appendf(&ril, "[%zsql</a>]",
                         href("%R/rptsql?rn=%d", rn));
                         href("%R/rptsql/%d", rn));
    }
    if( fossil_strcmp(zTitle, defaultReport)==0 ){
      blob_appendf(&ril, "&nbsp;← 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);
  if( g.thTrace ) Th_Trace("END_REPORTLIST<br>\n", -1);

  style_footer();
  style_finish_page();
}

/*
** Remove whitespace from both ends of a string.
*/
char *trim_string(const char *zOrig){
  int i;
159
160
161
162
163
164
165



166
167
168
169
170
171
172
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185







+
+
+







/*********************************************************************/

/*
** 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,
181
182
183
184
185
186
187
188

189
190

191


192
193
194
195
196


197
198
199
200








201
202
203
204
205
206
















207
208
209

210
211
212
213
214
215
216
217
218
219
220
221
222
223












224




225
226
227

228
229
230
231

232
233
234
235
236
237
238
194
195
196
197
198
199
200

201

202
203
204
205
206
207
208

209
210
211
212
213
214


215
216
217
218
219
220
221
222
223
224




225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242

243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269

270
271
272
273
274
275

276
277
278
279

280
281
282
283
284
285
286
287







-
+
-

+

+
+


-


+
+


-
-
+
+
+
+
+
+
+
+


-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


-
+














+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+


-
+



-
+







    case SQLITE_SELECT:
    case SQLITE_RECURSIVE:
    case SQLITE_FUNCTION: {
      break;
    }
    case SQLITE_READ: {
      static const char *const azAllowed[] = {
         "ticket",
         "backlink",
         "ticketchng",
         "blob",
         "event",
         "filename",
         "json_each",
         "json_tree",
         "mlink",
         "plink",
         "event",
         "tag",
         "tagxref",
         "ticket",
         "ticketchng",
         "unversioned",
      };
      int i;
      if( fossil_strncmp(zArg1, "fx_", 3)==0 ){
      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;
      }
      for(i=0; i<count(azAllowed); i++){
        if( fossil_stricmp(zArg1, azAllowed[i])==0 ) break;
      }
      if( i>=count(azAllowed) ){
      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 && strncmp(zArg2, "private_", 8)==0 ){
      }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 query authorizer

/*
** Activate the ticket report query authorizer. Must be followed by an
** eventual call to report_unrestrict_sql().
*/
void report_restrict_sql(char **pzErr){
  sqlite3_set_authorizer(g.db, report_query_authorizer, (void*)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){
  sqlite3_set_authorizer(g.db, 0, 0);
  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
282
283
284
285
286
287
288
289
290




































291
292

293
294
295


296
297
298
299
300
301
302
303
304
305
306
307
308
309
310

311
312

313
314
315
316

317
318
319
320
321
322
323
324
325
326
327
328
329
330
331

332
333
334
335
336
337
338

339
340
341
342
343
344
345
346
347
348

349


350
351
352
353



354
355
356
357

358
359
360
361
362








363
364
365
366
367
368

369
370

371
372
373
374
375




376

377
378
379
380
381

382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398

399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420




421

422
423
424





425

426
427
428




429
430








431

432
433
434
435
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
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376

377
378


379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394

395
396
397
398
399
400
401

402
403
404
405
406
407
408
409
410
411
412
413
414
415
416

417
418
419
420
421
422
423

424
425
426
427
428
429
430
431
432
433
434
435

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









+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+

-
-
+
+














-
+


+



-
+














-
+






-
+










+
-
+
+




+
+
+



-
+

-
-
-
-
+
+
+
+
+
+
+
+






+

-
+





+
+
+
+
-
+
-



-
+
















-
+




















-
-
+
+
+
+

+
-
-
-
+
+
+
+
+

+
-
-
-
+
+
+
+


+
+
+
+
+
+
+
+
-
+








+
+
+
+
-
+


+




+
+


+
+
+
+
+
+
+
+
+
+







-
+

-
+






-
-
-
-
+
+
+
+





-
+

+
+
+

-
+
+
+
+




-
+


+
+
+
+
+
+
+
+
+
+





-
+


-
+

-
+



-
+








-
+

-
+


-
+

-
+







  }
  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?rn=N
** URL: /rptsql/N
**
** Display the SQL query used to generate a ticket report.  The rn=N
** query parameter identifies the specific report number to be displayed.
** 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 = atoi(PD("rn","0"));
  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_footer();
    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>
  @ %h(zSQL)
  @ <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_footer();
  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. (required)
**     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;
  const char *zTitle;           /* Title of the report */
  const char *z;
  const char *zOwner;
  const char *zClrKey;
  char *zSQL;
  char *zErr = 0;
  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 = atoi(PD("rn","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") ){
  if( rn>0 && P("del2") && cgi_csrf_safe(2) ){
    login_verify_csrf_secret();
    db_multi_exec("DELETE FROM reportfmt WHERE rn=%d", rn);
    cgi_redirect("reportlist");
    return;
  }else if( rn>0 && P("del1") ){
  }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_footer();
    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 ){
      login_verify_csrf_secret();
    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(
        db_multi_exec("UPDATE reportfmt SET title=%Q, sqlcode=%Q,"
                      " owner=%Q, cols=%Q, mtime=now() WHERE rn=%d",
           zTitle, zSQL, zOwner, zClrKey, rn);
            "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(
        db_multi_exec("INSERT INTO reportfmt(title,sqlcode,owner,cols,mtime) "
           "VALUES(%Q,%Q,%Q,%Q,now())",
           zTitle, zSQL, zOwner, zClrKey);
           "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?rn=%d", rn));
      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 "
    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", "reportlist");
  style_submenu_element("Cancel", "%R/reportlist");
  if( rn>0 ){
    style_submenu_element("Delete", "rptedit?rn=%d&del1=1", rn);
    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 />
  @ <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)" />
    @ <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)" />
    @ <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 />
  @ 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_footer();
    style_finish_page();
    return;
  }
  @ <input type="submit" value="Apply Changes" />
  @ <input type="submit" value="Apply Changes">
  if( rn>0 ){
    @ <input type="submit" value="Delete This Report" name="del1" />
    @ <input type="submit" value="Delete This Report" name="del1">
  }
  @ </div></form>
  report_format_hints();
  style_footer();
  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_master WHERE name='ticket'");
  zSchema = db_text(0,"SELECT sql FROM sqlite_schema WHERE name='ticket'");
  if( zSchema==0 ){
    zSchema = db_text(0,"SELECT sql FROM repository.sqlite_master"
    zSchema = db_text(0,"SELECT sql FROM repository.sqlite_schema"
                        " WHERE name='ticket'");
  }
  @ <hr /><h3>TICKET Schema</h3>
  @ <hr><h3>TICKET Schema</h3>
  @ <blockquote><pre>
  @ %h(zSchema)
  @ <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
661
662
663
664
665
666
667
668

669
670
671
672
673
674
675
676
677

678
679
680
681
682
683
684
808
809
810
811
812
813
814

815
816
817
818
819
820
821
822
823

824
825
826
827
828
829
830
831







-
+








-
+







  void *pUser,     /* Pointer to output state */
  int nArg,        /* Number of columns in this result row */
  const char **azArg, /* Text of data in all columns */
  const char **azName /* Names of the columns */
){
  struct GenerateHTML *pState = (struct GenerateHTML*)pUser;
  int i;
  const char *zTid;  /* Ticket UUID.  (value of column named '#') */
  const char *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.
    */
    sqlite3_set_authorizer(g.db, 0, 0);
    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;
699
700
701
702
703
704
705
706

707
708

709
710
711
712
713
714
715
846
847
848
849
850
851
852

853
854

855
856
857
858
859
860
861
862







-
+

-
+







          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?rn=%d", pState->rn);
            style_submenu_element("Formatted", "%R/rptview/%d", pState->rn);
          }else{
            style_submenu_element("Plaintext", "%R/rptview?rn=%d&plaintext",
            style_submenu_element("Plaintext", "%R/rptview/%d?plaintext",
                                  pState->rn);
          }
        }else{
          pState->nCol++;
        }
      }
    }
779
780
781
782
783
784
785
786

787
788
789
790
791
792
793
926
927
928
929
930
931
932

933
934
935
936
937
938
939
940







-
+







        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?name=%h",zData))%h(zData)</a></td>
      @ <td valign="top">%z(href("%R/tktview/%h",zData))%h(zData)</a></td>
    }else if( zData[0]==0 ){
      @ <td valign="top">&nbsp;</td>
    }else{
      @ <td valign="top">
      @ %h(zData)
      @ </td>
    }
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
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







+
+
+
+
+
+
+
+
+
+
+
+






+
+







+
+


-
+
-
+


+
+
+


-
-
+
+
+




+
-
+
+







+
+




















+


+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
















+
-
+
+







**
** 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 FROM reportfmt WHERE rn=%d",
    "SELECT title, sqlcode, owner, cols, rn, jx->>'desc', jx->>'descmt'"
     atoi(PD("rn","0")));
    "  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 FROM reportfmt WHERE title GLOB %Q",
      P("title"));
      "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");
      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", "rptview?tablist=1&%h", PD("QUERY_STRING",""));
    if( g.perm.Admin
       || (g.perm.TktFmt && g.zLogin && fossil_strcmp(g.zLogin,zOwner)==0) ){
      style_submenu_element("Edit", "rptedit?rn=%d", rn);
    }
    if( g.perm.TktFmt ){
      style_submenu_element("SQL", "rptsql?rn=%d",rn);
    }
    if( g.perm.NewTkt ){
      style_submenu_element("New Ticket", "%s/tktnew", g.zTop);
    }
    style_header("%s", zTitle);
          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_footer();
      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");
  }
}
1166
1167
1168
1169
1170
1171
1172
1173


1174
1175
1176
1177
1178
1179
1180
1366
1367
1368
1369
1370
1371
1372

1373
1374
1375
1376
1377
1378
1379
1380
1381







-
+
+







  Stmt q;
  char *zSql;
  char *zErr1 = 0;
  char *zErr2 = 0;
  int count = 0;
  int rn;

  if( !zRep || !strcmp(zRep,zFullTicketRptRn) || !strcmp(zRep,zFullTicketRptTitle) ){
  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{

Changes to src/rss.c.

20
21
22
23
24
25
26
27

28
29
30
31
32



33
34
35
36

37
38
39
40
41
42
43
20
21
22
23
24
25
26

27
28
29
30


31
32
33
34
35
36

37
38
39
40
41
42
43
44







-
+



-
-
+
+
+



-
+







#include "config.h"
#include <time.h>
#include "rss.h"
#include <assert.h>

/*
** WEBPAGE: timeline.rss
** URL:  /timeline.rss?y=TYPE&n=LIMIT&tkt=UUID&tag=TAG&wiki=NAME&name=FILENAME
** 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 tickets only),
** w (show wiki only).
** 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=UUID filters for only those events for the specified ticket. tag=TAG
** 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).
*/

54
55
56
57
58
59
60

61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78

79
80
81
82
83

84
85
86
87
88
89
90
91




92
93
94



95
96
97


98
99
100
101


102
103

104

105
106
107
108
109
110
111
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79

80
81
82
83
84
85
86
87
88






89
90
91
92



93
94
95



96
97




98
99


100
101
102
103
104
105
106
107
108
109







+

















-
+





+


-
-
-
-
-
-
+
+
+
+
-
-
-
+
+
+
-
-
-
+
+
-
-
-
-
+
+
-
-
+

+







  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( &bSQL, zSQL1, -1 );
  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{
    if( !g.perm.Read ){
      if( g.perm.RdTkt && g.perm.RdWiki ){
        blob_append(&bSQL, " AND event.type!='ci'", -1);
      }else if( g.perm.RdTkt ){
        blob_append(&bSQL, " AND event.type=='t'", -1);

    blob_append_sql(&bSQL, " AND event.type in (");
    if( g.perm.Read ){
      blob_append_sql(&bSQL, "'ci',");
    }
      }else{
        blob_append(&bSQL, " AND event.type=='w'", -1);
      }
    if( g.perm.RdTkt ){
      blob_append_sql(&bSQL, "'t',");
    }
    }else if( !g.perm.RdWiki ){
      if( g.perm.RdTkt ){
        blob_append(&bSQL, " AND event.type!='w'", -1);
    if( g.perm.RdWiki ){
      blob_append_sql(&bSQL, "'w',");
      }else{
        blob_append(&bSQL, " AND event.type=='ci'", -1);
      }
    }else if( !g.perm.RdTkt ){
    }
    if( g.perm.RdForum ){
      assert( !g.perm.RdTkt && g.perm.Read && g.perm.RdWiki );
      blob_append(&bSQL, " AND event.type!='t'", -1);
      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;
131
132
133
134
135
136
137
138


139
140
141
142
143

144
145
146
147
148
149
150


151
152
153
154
155
156
157
129
130
131
132
133
134
135

136
137
138
139
140
141

142
143
144
145
146
147


148
149
150
151
152
153
154
155
156







-
+
+




-
+





-
-
+
+







  }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)",
      " 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 );
  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);
    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));
165
166
167
168
169
170
171

172
173


174
175
176
177
178
179


180
181
182
183
184
185

186
187
188
189
190
191














192
193
194
195
196
197
198
164
165
166
167
168
169
170
171


172
173
174
175
176
177


178
179
180
181
182
183
184
185
186






187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207







+
-
-
+
+




-
-
+
+






+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+







  @     <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, 3);
    const char *zAuthor = db_column_text(&q, 4);
    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, 6);
    const char *zTagList = db_column_text(&q, 7);
    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* ";
      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>
219
220
221
222
223
224
225
226

227
228
229
230



231
232
233

234
235
236

237
238

239
240
241
242

243
244
245
246
247
248



249
250
251
252


253
254
255
256
257
258
259

260
261
262
263
264
265
266
228
229
230
231
232
233
234

235
236



237
238
239
240


241
242


243
244

245

246


247
248
249
250



251
252
253
254



255
256
257
258
259
260
261
262

263
264
265
266
267
268
269
270







-
+

-
-
-
+
+
+

-
-
+

-
-
+

-
+
-

-
-
+



-
-
-
+
+
+

-
-
-
+
+






-
+








/*
** 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:
** feed of the timeline to stdout.
**
** -type|y FLAG
**    may be: all (default), ci (show check-ins only), t (show tickets only),
**    w (show wiki only).
** 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.
**   -limit|n LIMIT  The maximum number of items to show
**
** -tkt UUID
**    Filters for only those events for the specified ticket.
**   -tkt HASH       Filter for only those events for the specified ticket
**
** -tag TAG
**   -tag TAG        Filter for a tag
**    filters for a tag
**
** -wiki NAME
**   Filters on a specific wiki page.
**   -wiki NAME      Filter on a specific wiki page
**
** Only one of -tkt, -tag, or -wiki may be used.
**
** -name FILENAME
**   filters for a specific file. This may be combined with one of the other
**   filters (useful for looking at a specific branch).
**   -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
**   Sets the RSS feed's root URL to the given string. The default is
** "URL-PLACEHOLDER" (without quotes).
**   -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 *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" );
326
327
328
329
330
331
332
333


334
335
336
337
338
339
340
341
342
343


344
345
346
347
348
349
350
351
352
353


354
355
356
357
358
359
360
330
331
332
333
334
335
336

337
338
339
340
341
342
343
344
345
346


347
348
349
350
351
352
353
354
355
356
357

358
359
360
361
362
363
364
365
366







-
+
+








-
-
+
+









-
+
+







  }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)",
      " 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);
    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("<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);

Deleted src/sbsdiff.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






























-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
/* The javascript in this file was added by Joel Bruick on 2013-07-06,
** originally as in-line javascript.  It does some kind of setup for
** side-by-side diff display, but I'm not really sure what.
*/
(function(){
  var SCROLL_LEN = 25;
  function initSbsDiff(diff){
    var txtCols = diff.querySelectorAll('.difftxtcol');
    var txtPres = diff.querySelectorAll('.difftxtcol pre');
    var width = Math.max(txtPres[0].scrollWidth, txtPres[1].scrollWidth);
    for(var i=0; i<2; i++){
      txtPres[i].style.width = width + 'px';
      txtCols[i].onscroll = function(e){
        txtCols[0].scrollLeft = txtCols[1].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;
      txtCols[0].scrollLeft += len;
      return false;
    };
  }
  var diffs = document.querySelectorAll('.sbsdiffcols');
  for(var i=0; i<diffs.length; i++){
    initSbsDiff(diffs[i]);
  }
}())

Changes to src/schema.c.

1
2
3
4
5
6
7

8
9
10
11
12
13
14
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/
124
125
126
127
128
129
130
131


132
133
134
135
136
137
138
124
125
126
127
128
129
130

131
132
133
134
135
136
137
138
139







-
+
+







@   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
@   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
156
157
158
159
160
161
162






163
164
165
166
167
168
169
170
171
172
173
174
175


176
177
178
179
180
181
182
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181

182
183
184
185
186
187
188
189
190







+
+
+
+
+
+












-
+
+







@   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
@    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.
@ --
258
259
260
261
262
263
264
265

266
267
268
269
270
271
272
266
267
268
269
270
271
272

273
274
275
276
277
278
279
280







-
+







@ --
@ 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 REFERENCES filename, -- Previous name. 0 if unchanged
@   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);
313
314
315
316
317
318
319
320

321
322
323
324
325
326
327
328

329
330
331
332
333
334
335
321
322
323
324
325
326
327

328
329
330
331
332
333
334
335

336
337
338
339
340
341
342
343







-
+







-
+







@   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
@ -- UUID but we do not (yet) know the file content.
@ -- 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 maniftests so that when the baseline arrives,
@ -- 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);
@
350
351
352
353
354
355
356
357

358
359
360
361
362


363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382

383
384
385
386
387
388


389
390
391




392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407


408
409
410
411
412
413
414
415
416
417
418
419
420
421


422
423
424
425
426
427
428
358
359
360
361
362
363
364

365
366
367
368


369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389

390
391
392
393
394
395

396
397
398


399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416


417
418
419
420
421
422
423
424
425
426
427
428
429
430


431
432
433
434
435
436
437
438
439







-
+



-
-
+
+



















-
+





-
+
+

-
-
+
+
+
+














-
-
+
+












-
-
+
+







@ -- 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 baseline or manifest can have one or more tags.  A tag
@ -- 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-UUID" where
@ -- UUID is the indentifier of the ticket.  Tags used to assign symbolic
@ -- 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 baselines.  Note that we allow tags to
@ -- 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 that added or removed
@   tagid INTEGER REFERENCES tag,   -- The tag being added, removed,
@                                   -- or propagated
@   tagtype INTEGER,                -- 0:-,cancel  1:+,single  2:*,propagate
@   srcid INTEGER REFERENCES blob,  -- Artifact of tag. 0 for propagated tags
@   origid INTEGER REFERENCES blob, -- check-in holding propagated tag
@   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: check-in  1: ticket  2: wiki
@   srcid INT,             -- rid for check-in or wiki.  tkt_id for ticket.
@   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,                       -- UUID of the attachment.  NULL to delete
@   target TEXT,                    -- Object attached to. Wikiname or Tkt UUID
@   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);
@
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
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







+







+
+
+
+
+
+
+
+
+


+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+




















-
+





-
+













-
+




-
-
-
-
-
+
+
+
+
+
+
-
-
-


-
-
+
+









+



+
+
+
+
+
+
+
+
+
+






-
+
+




-
+

+

-
-
-
+
+
+
+
+
+
+
+
+
+
+
+







@   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 checkout.
** 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 checkout.
@ -- 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 checkout.
@ -- current check-out.
@ --
@ -- The file.rid field is 0 for files or folders that have been
@ -- added but not yet committed.
@ --
@ -- Vfile.chnged is 0 for unmodified files, 1 for files that have
@ -- been edited or which have been subjected to a 3-way merge.
@ -- Vfile.chnged is 2 if the file has been replaced from a different
@ -- version by the merge and 3 if the file has been added by a merge.
@ -- Vfile.chnged is 4|5 is the same as 2|3, but the operation has been
@ -- 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
@ -- done by an --integrate merge.  The difference between vfile.chnged==3|5
@ -- and a regular add is that with vfile.chnged==3|5 we know that the
@ -- current version of the file is already in the repository.
@ --
@ CREATE TABLE vfile(
@   id INTEGER PRIMARY KEY,           -- ID of the checked out file
@   vid INTEGER REFERENCES blob,      -- The baseline this file is part of.
@   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 a integrate merge.
@ -- 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
@   UNIQUE(id, merge)
@   mhash TEXT                        -- SHA1/SHA3 hash for merge object
@ );
@ CREATE UNIQUE INDEX vmergex1 ON vmerge(id,mhash);
@
@ -- Identifier for this file type.
@ -- The integer is the same as 'FSLC'.
@ PRAGMA application_id=252006674;
@ -- 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.
*/

Changes to src/scroll.js.

1
2




1
2
-
-
+
+
/* Cause the the page to scroll so that the #scrollToMe is visible */
document.getElementById('scrollToMe').scrollIntoView(true);
/* Cause the page to scroll so that the #scrollToMe is visible */
(document.getElementById('scrollToMe')||document.body).scrollIntoView(true);

Changes to src/search.c.

12
13
14
15
16
17
18
19


20
21
22
23


24
25
26
27
28
29
30
12
13
14
15
16
17
18

19
20
21
22


23
24
25
26
27
28
29
30
31







-
+
+


-
-
+
+







** 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, and/or tickets.
** 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 or FTS5 search engine of
** SQLite.  The choice is a administrator configuration option.
** 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
329
330
331
332
333
334
335







336
337
338
339
340
341
342
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350







+
+
+
+
+
+
+







/*
** 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;
370
371
372
373
374
375
376
377


378
379
380
381
382
383
384
378
379
380
381
382
383
384

385
386
387
388
389
390
391
392
393







-
+
+







**
**     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, or SRCH_ALL bits to determine what is to be searched.
** 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;
405
406
407
408
409
410
411
412

413
414
415
416
417
418
419
414
415
416
417
418
419
420

421
422
423
424
425
426
427
428







-
+







  }
}

/*     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 global full-scan search object.
** 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
){
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
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







+

-
+

-
+

-
+

-
+

-
+

-
+

-
+

-
+








-
+




+
+
+
+
+








-
-
-
+
+
+
+
+
+









-
+
-
-


+
+
-
+
-

+
+















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+















-
+
+








/*
** 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, SQLITE_UTF8, 0,
  sqlite3_create_function(db, "search_match", -1, enc, 0,
     search_match_sqlfunc, 0, 0);
  sqlite3_create_function(db, "search_score", 0, SQLITE_UTF8, 0,
  sqlite3_create_function(db, "search_score", 0, enc, 0,
     search_score_sqlfunc, 0, 0);
  sqlite3_create_function(db, "search_snippet", 0, SQLITE_UTF8, 0,
  sqlite3_create_function(db, "search_snippet", 0, enc, 0,
     search_snippet_sqlfunc, 0, 0);
  sqlite3_create_function(db, "search_init", -1, SQLITE_UTF8, 0,
  sqlite3_create_function(db, "search_init", -1, enc, 0,
     search_init_sqlfunc, 0, 0);
  sqlite3_create_function(db, "stext", 3, SQLITE_UTF8, 0,
  sqlite3_create_function(db, "stext", 3, enc, 0,
     search_stext_sqlfunc, 0, 0);
  sqlite3_create_function(db, "title", 3, SQLITE_UTF8, 0,
  sqlite3_create_function(db, "title", 3, enc, 0,
     search_title_sqlfunc, 0, 0);
  sqlite3_create_function(db, "body", 3, SQLITE_UTF8, 0,
  sqlite3_create_function(db, "body", 3, enc, 0,
     search_body_sqlfunc, 0, 0);
  sqlite3_create_function(db, "urlencode", 1, SQLITE_UTF8, 0,
  sqlite3_create_function(db, "urlencode", 1, enc, 0,
     search_urlencode_sqlfunc, 0, 0);
}

/*
** Testing the search function.
**
** COMMAND: search*
**
** Usage: %fossil search [-all|-a] [-limit|-n #] [-width|-W #] pattern...
** 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.
**     -n|--limit N      Limit output to N matches.
**     -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); /* If set, do not lop
  char fAll = NULL != find_option("all", "a", 0);
                                                     off the end of the
                                                     results. */
  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;   /* Max number of matching
  int nLimit = zLimit ? atoi(zLimit) : -1000;
                                                   lines/entries to list */
  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);
    (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);
  db_finalize(&q);
    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.
** 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"  },
689
690
691
692
693
694
695
696

697
698
699
700
701
702
703
782
783
784
785
786
787
788

789
790
791
792
793
794
795
796







-
+







** 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().
*/
static void search_fullscan(
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","");
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
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







-
+




+
+
+

-
+

+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+







-
+


-
-
+
+

+







** 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().
*/
static void search_indexed(
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, 0,
  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),"
    "        snippet(ftsidx,'<mark>','</mark>',' ... ',-1,35)"
    "        %s"
    "   FROM ftsidx CROSS JOIN ftsdocs"
    "  WHERE ftsidx MATCH %Q"
    "    AND ftsdocs.rowid=ftsidx.docid",
    zPattern
    "    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' },
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
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







+

+
+
+













-
+






+








-
+
+
+
+
+
+







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"
  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))</span></li>
    @ <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;
}
1108
1109
1110
1111
1112
1113
1114
1115

1116
1117
1118
1119
1120
1121
1122
1231
1232
1233
1234
1235
1236
1237

1238
1239
1240
1241
1242
1243
1244
1245







-
+







  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 { char *z; char *zNm; unsigned m; } aY[] = {
    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    },
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
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







+

-
+
+

-
+







+
+
+







+



+

+
-
+
+
+
+


+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+



-
-
+
+
+
-
-
+
-
-
+
-
-
-
-
+
-
-
+
+

-







**                      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");
  style_header("Search%s", isSearch ? " Results" : "");
  cgi_check_for_malice();
  search_screen(SRCH_ALL, 1);
  style_footer();
  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);
    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);
      Blob tail;
      blob_init(&tail, 0, 0);
      if( wiki_find_title(pIn, &title, &tail) ){
        blob_appendf(pOut, "%s\n", blob_str(&title));
        wiki_convert(&tail, &html, 0);
        blob_reset(&tail);
      }else{
        blob_append(pOut, "\n", 1);
        wiki_convert(pIn, &html, 0);
      }
    }
    html_to_plaintext(blob_str(&html), pOut);
  }else if( fossil_strcmp(zMimetype,"text/x-markdown")==0 ){
    markdown_to_html(pIn, &title, &html);
    if( blob_size(&title) ){
    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);
      blob_appendf(pOut, "%s\n", blob_str(&title));
    }else{
    pHtml = pIn;
      blob_append(pOut, "\n", 1);
    }
  }
    html_to_plaintext(blob_str(&html), pOut);
  }else if( fossil_strcmp(zMimetype,"text/html")==0 ){
    if( doc_is_embedded_html(pIn, &title) ){
      blob_appendf(pOut, "%s\n", blob_str(&title));
  blob_appendf(pOut, "%s\n", blob_str(&title));
    }
    html_to_plaintext(blob_str(pIn), pOut);
  if( blob_size(pHtml) ){
    html_to_plaintext(blob_str(pHtml), pOut);
  }else{
    blob_append(pOut, "\n", 1);
    blob_append(pOut, blob_buffer(pIn), blob_size(pIn));
  }
  blob_reset(&html);
  blob_reset(&title);
}

/*
1261
1262
1263
1264
1265
1266
1267
1268

1269
1270
1271
1272
1273
1274
1275
1394
1395
1396
1397
1398
1399
1400

1401
1402
1403
1404
1405
1406
1407
1408







-
+







      if( fossil_strcmp(zMime,"text/plain")==0 ) zMime = 0;
    }else if( zMime==0 || eType!=SQLITE_TEXT ){
      blob_appendf(pAccum, "%s: %s |\n", zColName, db_column_text(pQuery,i));
    }else{
      Blob txt;
      blob_init(&txt, db_column_text(pQuery,i), -1);
      blob_appendf(pAccum, "%s: ", zColName);
      get_stext_by_mimetype(&txt, zMime, pAccum);
      get_stext_by_mimetype(&txt, zMime, NULL, pAccum);
      blob_append(pAccum, " |", 2);
      blob_reset(&txt);
    }
  }
}


1300
1301
1302
1303
1304
1305
1306
1307

1308
1309
1310
1311
1312
1313
1314
1433
1434
1435
1436
1437
1438
1439

1440
1441
1442
1443
1444
1445
1446
1447







-
+







){
  blob_init(pOut, 0, 0);
  switch( cType ){
    case 'd': {   /* Documents */
      Blob doc;
      content_get(rid, &doc);
      blob_to_utf8_no_bom(&doc, 0);
      get_stext_by_mimetype(&doc, mimetype_from_name(zName), pOut);
      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,
1322
1323
1324
1325
1326
1327
1328
1329

1330
1331
1332
1333
1334
1335
1336
1455
1456
1457
1458
1459
1460
1461

1462
1463
1464
1465
1466
1467
1468
1469







-
+







          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),
                            pOut);
                            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;
1352
1353
1354
1355
1356
1357
1358
1359

1360
1361
1362
1363
1364
1365
1366
1485
1486
1487
1488
1489
1490
1491

1492
1493
1494
1495
1496
1497
1498
1499







-
+







        blob_append(pOut, "\n", 1);
        if( isPlainText ){
          db_column_blob(&q, 0, pOut);
        }else{
          Blob x;
          blob_init(&x,0,0);
          db_column_blob(&q, 0, &x);
          get_stext_by_mimetype(&x, "text/x-fossil-wiki", pOut);
          get_stext_by_mimetype(&x, "text/x-fossil-wiki", NULL, pOut);
          blob_reset(&x);
        }
      }
      db_reset(&q);
      break;
    }
    case 't': {   /* Tickets */
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
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







-
+





+
-
+
+
+




-
+

















-
+






+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+






-
-
+
+
+
+
+
+
+
+










-
+
+







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







*/
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], &out);
  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 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.docid
@   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 fts4(content="ftscontent", title, body%s);
@   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){
  int useStemmer = db_get_boolean("search-stemmer",0);
  const char *zExtra = useStemmer ? ",tokenize=porter" : "";
  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
** 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){
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
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







-
+





-
+










-
+







/*
** 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() ){
  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 docid IN"
       "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 docid IN"
        "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
      );
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
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







-
+


















-
+
















-
+







    "INSERT OR IGNORE INTO current_docs(rid, name)"
    "  SELECT blob.rid, foci.filename FROM foci, blob"
    "   WHERE foci.checkinID=%d AND blob.uuid=foci.uuid"
    "     AND %z",
    ckid, glob_expr("foci.filename", db_get("doc-glob",""))
  );
  db_multi_exec(
    "DELETE FROM ftsidx WHERE docid IN"
    "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(docid,title,body)"
    "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(docid,title,body)"
    "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,"
1682
1683
1684
1685
1686
1687
1688
1689

1690
1691
1692
1693
1694
1695
1696
1929
1930
1931
1932
1933
1934
1935

1936
1937
1938
1939
1940
1941
1942
1943







-
+







}

/*
** Deal with all of the unindexed 't' terms in FTSDOCS
*/
static void search_update_ticket_index(void){
  db_multi_exec(
    "INSERT INTO ftsidx(docid,title,body)"
    "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) ="
1705
1706
1707
1708
1709
1710
1711
1712

1713
1714
1715
1716
1717
1718
1719
1952
1953
1954
1955
1956
1957
1958

1959
1960
1961
1962
1963
1964
1965
1966







-
+







}

/*
** Deal with all of the unindexed 'w' terms in FTSDOCS
*/
static void search_update_wiki_index(void){
  db_multi_exec(
    "INSERT INTO ftsidx(docid,title,body)"
    "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) = "
1727
1728
1729
1730
1731
1732
1733
1734

1735
1736
1737
1738
1739
1740
1741
1974
1975
1976
1977
1978
1979
1980

1981
1982
1983
1984
1985
1986
1987
1988







-
+







}

/*
** Deal with all of the unindexed 'f' terms in FTSDOCS
*/
static void search_update_forum_index(void){
  db_multi_exec(
    "INSERT INTO ftsidx(docid,title,body)"
    "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) = "
1750
1751
1752
1753
1754
1755
1756
1757

1758
1759
1760
1761
1762
1763
1764
1997
1998
1999
2000
2001
2002
2003

2004
2005
2006
2007
2008
2009
2010
2011







-
+







}

/*
** Deal with all of the unindexed 'e' terms in FTSDOCS
*/
static void search_update_technote_index(void){
  db_multi_exec(
    "INSERT INTO ftsidx(docid,title,body)"
    "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) = "
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
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







+
















+







** 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...");
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
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







-
-
+
+
+

-
+

-
-
+
+
+
+





-
+
+
+
+




-
+

-
+
+
+
+
+







** 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 cdtwe       Enable various kinds of search. c=Check-ins,
**                        d=Documents, t=Tickets, w=Wiki, e=Tech Notes.
**     enable cdtwef      Enable various kinds of search. c=Check-ins,
**                        d=Documents, t=Tickets, w=Wiki, e=Tech Notes,
**                        f=Forum.
**
**     disable cdtwe      Disable various kinds of search
**     disable cdtwef     Disable various kinds of search
**
**     stemmer (on|off)   Turn the Porter stemmer on or off for indexed
**                        search.  (Unindexed search is never stemmed.)
**     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[] = {
  static const struct {
    int iCmd;
    const char *z;
  } aCmd[] = {
     { 1,  "reindex"  },
     { 2,  "index"    },
     { 3,  "disable"  },
     { 4,  "enable"   },
     { 5,  "stemmer"  },
     { 5,  "tokenizer"},
  };
  static const struct { char *zSetting; char *zName; char *zSw; } aSetng[] = {
  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" },
  };
1863
1864
1865
1866
1867
1868
1869
1870

1871
1872
1873
1874
1875
1876
1877
2122
2123
2124
2125
2126
2127
2128

2129
2130
2131
2132
2133
2134
2135
2136







-
+







    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 on of:%s",
      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;
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



























































































































































































































































































































































































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







-
+


-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+














-
-
+
+

+
+
+
+
+
+
-
+


+
+
+

















-
+


+


-
+


+
+



+






+
+
+


-
+






+
+
+
+
+
+
+
+
+
+

+
+
+
+


-
+






+


+
+
+
+
+
+
+
+












-
+




+
-
-
+
+



-
+




-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
-
+

+

-
+
+


-
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
  /* 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, iCmd-3, 0);
        db_set_int(aSetng[j].zSetting/*works-like:"x"*/, iCmd-3, 0);
      }
    }
  }
  if( iCmd==5 ){
    if( g.argc<4 ) usage("porter ON/OFF");
    db_set_int("search-stemmer", is_truth(g.argv[3]), 0);
  }

  }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", "Porter stemmer:",
       db_get_boolean("search-stemmer",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 enabled\n", "full-text index:");
    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 cnt = 0;
  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_footer();
    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>&nbsp;&nbsp;<td>%d(id)
      @ <tr><td align='right'>id:<td><td>%s(db_column_text(&q,0))
      @ <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_footer();
    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_footer();
    style_finish_page();
    return;
  }
  style_header("Summary of ftsdocs");
  db_prepare(&q,
     "SELECT type, sum(idxed IS TRUE), sum(idxed IS FALSE), count(*)"
     "SELECT type, idxed, count(*) FROM ftsdocs"
     " GROUP BY 1, 2 ORDER BY 3 DESC"
     "  FROM ftsdocs"
     " GROUP BY 1 ORDER BY 4 DESC"
  );
  @ <table border=1 cellpadding=3 cellspacing=0>
  @ <thead>
  @ <tr><th>Type<th>Indexed?<th>Count<th>Link
  @ <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 idxed = db_column_int(&q,1);
    int n = db_column_int(&q,2);
    @ <tr><td>%h(zType)<td>%d(idxed)
    @ <td>%d(n)
    @ <td><a href='test-ftsdocs?y=%s(zType)&ixed=%d(idxed)'>listing</a>
    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;
    cnt += n;
    cnt3 += nTotal;
  }
  db_finalize(&q);
  @ </tbody><tfooter>
  @ <tr><th>Total<th><th>%d(cnt)<th>
  @ <tr><th>Total<th align="right">%d(cnt1)<th align="right">%d(cnt2)
  @ <th align="right">%d(cnt3)
  @ </tfooter>
  @ </table>
  style_footer();
  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;
}

Changes to src/security_audit.c.

30
31
32
33
34
35
36


















37






























38
39
40
41
42







43
44
45


46




47
48



49
50
51

52
53
54
55
56
57
58
59
60
61
62
63


64





65
66
67
68
69
70
71







72
73
74
75

76
77






78
79
80
81




82

83
84
85
86
87
88
89


90

91
92
93
94
95
96
97
98









99
100
101
102
103
104


105
106
107
108



109
110
111
112
113
114
115
116































117
118


119
120

121

122
123
124





125


























































126
127
128
129
130
131
132
133
134

135
136
137
138
139
140
141
142
143
144
145
146

147
148
149
150
151
152
153
154
155
156
157

158
159

160
161
162
163
164
165

166
167


















168



169
170
171



172
173


174
175
176
177
178
179
180
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88


89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106

107
108
109
110
111

112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149

150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174

175
176
177
178








179
180
181
182
183
184
185
186
187
188
189
190
191


192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239


240
241
242

243
244
245



246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317

318
319
320
321
322
323
324
325
326
327
328
329

330
331
332
333
334
335
336
337
338
339
340

341
342

343
344
345
346
347
348

349

350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368

369
370
371
372


373
374
375


376
377
378
379
380
381
382
383
384







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



-
-
+
+
+
+
+
+
+



+
+

+
+
+
+

-
+
+
+


-
+












+
+

+
+
+
+
+







+
+
+
+
+
+
+



-
+


+
+
+
+
+
+




+
+
+
+

+






-
+
+

+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+




-
-
+
+




+
+
+








+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+

-
+

+
-
-
-
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+








-
+











-
+










-
+

-
+





-
+
-

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+

-
-
+
+
+
-
-
+
+







  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.
** This page requires administrator access
** 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;
  int n, i;
  CapabilityString *pCap;
  char **azCSP;              /* Parsed content security policy */

  login_check_credentials();
  if( !g.perm.Setup && !g.perm.Admin ){
  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.
    @ <p>Fix this by <a href="takeitprivate">taking the repository private</a>
    @ 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 anonymously:
    @ content can be accessed either anonymously or by self-registered
    @ users:
    @ <ul>
    if( hasSelfReg ){
    if( hasAnyCap(zAnonCap,"j") ){
      @ <li> Wiki pages
    }
    if( hasAnyCap(zAnonCap,"r") ){
      @ <li> Tickets
    }
    if( hasAnyCap(zAnonCap,"234567") ){
      @ <li> Forum posts
      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> URLs that match any of these GLOB patterns:
      @ <ul>
      @ <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, so that the password
  ** does not go across the internet in the clear.
  /* 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_boolean("redirect-to-https",0)==0 ){
  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
    @ Login passwords can be sent over an unencrypted connection.
    @ <p>Fix this by activating the "Redirect to HTTPS on the Login page"
    @ setting on the <a href="setup_access">Access Control</a> page.
    @ 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.
    @ <p>Fix this by removing the "Email" privilege
    @ 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.
    @ <p>Fix this by removing the "Check-in" privilege
    @ 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 
    @ Anonymous users can act as moderators for wiki, tickets, or
    @ forum posts. This defeats the whole purpose of moderation.
    @ <p>Fix this by removing the "Mod-Wiki", "Mod-Tkt", and "Mod-Forum"
    @ 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.
  }

  /* Anonymous users probably should not be allowed to delete
  /* Check to see if any TH1 scripts are configured to run on a sync
  ** wiki or tickets.
  */
  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") ){
  if( hasAnyCap(zAnonCap, "d") ||
      hasAnyCap(zDevCap,  "d") ||
      hasAnyCap(zReadCap, "d") ){
    @ <li><p><b>WARNING:</b>
    @ Anonymous users can delete wiki and tickets.
    @ <p>Fix this by removing the "Delete"
    @ 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
    @ privilege from users "anonymous" and "nobody" on the
    @ <a href="setup_ulist">User Configuration</a> page.
    @ <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 ){
254
255
256
257
258
259
260
261

262
263
264
265
266
267
268
458
459
460
461
462
463
464

465
466
467
468
469
470
471
472







-
+







    @ <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, administator privilege ('s' or 'a') should only
      @ 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.
295
296
297
298
299
300
301





























302
303
304
305
306

307
308
309
310

311
312
313
314

315
316
317
318
319

320
321
322
323
324
325
326
327
328

329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344

345
346
347
348

349
350
351
352
353
354
355



356
357
358
359
360
361


362
363
364
365
366
367
368
369
370
371
372
373
374
375
376








377
378
379
380
381
382
383
384
385
386
387

388
389
390
391


















392
393
394




















395
396
397
398
399
400
401
402
403






































404
405

406
407
408
409
410
411
412
413
414
415

416
417
418
419
420
421
422
423

424
425
426
427
428


429
430
431
432
433
434
435
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


































































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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+




-
+



-
+



-
+




-
+








-
+















-
+



-
+





-
-
+
+
+




-
-
+
+








-
-
-
-
-
-
-
+
+
+
+
+
+
+
+










-
+




+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+









+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+









-
+








+





+
+















-
+



+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+

-
+










+


-
+






+
+
+

-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
+


-
+












+




-
+


+
-
+
+




-
-
+
+
















-
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
      @ <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>
    @ <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.
    @ <p>Fix this by deactivating the "Allow REMOTE_USER authentication"
    @ 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>
    @ <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.
    @ <p>Fix this by deactivating the "Allow HTTP_AUTHENTICATION authentication"
    @ 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 unsucessful login attempts and is
    @ 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.
    @ <p>If running in a chroot jail on Linux, verify that the /proc
    @ 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"));
    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.
      @ <p>To fix this, set the "Server Load Average Limit" on the
      @ <a href="setup_access">Access Control</a> page to approximately
      @ 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 "Server Load Average Limit" on the
      @ <a href="setup_access">Access Control</a> page is set to %g(r),
      @ 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:
    @ <ul>
    @ <li>If running from CGI, make an entry "errorlog: <i>FILENAME</i>"
    @ in the CGI script.
    @ <li>If running the "fossil server" or "fossil http" commands,
    @ add the "--errorlog <i>FILENAME</i>" command-line option.
    @ </ul>
    @ 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>" that is
      @ 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>&lt;head&gt;</tt> element by omitting <tt>&lt;body&gt;</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_footer();
  style_finish_page();
}

/*
** WEBPAGE: takeitprivate
**
** Disable anonymous access to this website
*/
void takeitprivate_page(void){
  login_check_credentials();
  if( !g.perm.Setup && !g.perm.Admin ){
  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_footer();
  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 log to show
** The maximum number of bytes of the error log to show by default.
*/
#define MXSHOWLOG 50000
#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.Setup && !g.perm.Admin ){
  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 ){
    @ <p>To create a server error log:
    @ <ol>
    @ <li><p>
    @ If the server is running as CGI, then create a line in the CGI file
    @ like this:
    @ <blockquote><pre>
    @ errorlog: <i>FILENAME</i>
    no_error_log_available();
    @ </pre></blockquote>
    @ <li><p>
    @ If the server is running using one of 
    @ the "fossil http" or "fossil server" commands then add
    @ a command-line option "--errorlog <i>FILENAME</i>" to that
    @ command.
    @ </ol>
    style_footer();
    style_finish_page();
    return;
  }
  if( P("truncate1") && cgi_csrf_safe(1) ){
  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_footer();
    style_finish_page();
    return;
  }
  zLog = file_canonical_name_dup(g.zErrlog);
  @ <p>The server error log at "%h(g.zErrlog)" is %,lld(szFile) bytes in size.
  @ <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 top open that file for reading!</p>
    style_footer();
    @ <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_footer();
  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.

25
26
27
28
29
30
31

32
33
34
35

36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

53
54
55
56
57
58
59
60
61
62
63
64



65
66

67
68

69
70

71

72
73
74
75
76
77
78
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

54
55
56
57
58
59
60
61
62
63
64
65

66
67
68
69
70
71
72

73
74
75
76
77
78
79
80
81
82
83
84
85







+




+
















-
+











-
+
+
+


+

-
+


+

+







** 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)">%h(zTitle)</a>
    @ <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 privileges.
** 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.Setup ){
  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>&lt;base href="$secureurl/$current_page"&gt;</tt> after
93
94
95
96
97
98
99

100
101
102
103





104
105



106
107



108
109

110
111
112
113
114














115
116
117
118

119
120


121
122
123
124
125




126
127
128
129
130
131
132
133

134
135



136
137
138
139
140
141
142

143
144
145

146
147
148
149
150
151
152
153

154
155
156
157





158
159
160






























































161
162
163
164
165
166
167
168

169
170

171
172
173
174
175
176
177
178
179
180
181
182





183
184
185
186
187
188


189
190

191
192
193

194
195

196
197
198
199
200
201
202
203
204

205
206

207
208
209
210
211

212
213
214




215
216
217
218

219

220
221
222
223
224

225
226
227
228
229
230
231
232
233
234

235
236

237
238
239
240
241

242
243
244




245
246
247
248
249
250


251
252

253
254
255

256
257
258
259
260
261
262
263
264
265
266
267

268
269

270
271

272
273
274
275
276

277
278
279




280
281
282
283
284

285
286
287
288
289
290
291












































































































292
293
294
295
296

297
298





299
300
301
302
303
304

305
306
307

308
309
310
311
312
313
314









315
316

317
318
319



320
321
322
323
324
325
326
100
101
102
103
104
105
106
107




108
109
110
111
112
113
114
115
116
117


118
119
120
121
122
123





124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142


143
144





145
146
147
148
149
150
151
152
153
154
155
156
157


158
159
160
161
162
163
164



165



166


167
168
169
170
171
172
173




174
175
176
177
178
179
180

181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249

250
251

252
253
254
255
256
257
258
259
260
261



262
263
264
265
266
267
268
269
270
271

272
273
274

275
276
277

278
279

280
281
282
283
284
285
286
287
288

289
290

291
292
293
294
295

296
297


298
299
300
301
302
303
304
305
306

307

308
309
310

311
312
313
314
315
316
317
318
319
320

321
322

323
324
325
326
327

328
329


330
331
332
333
334
335
336
337
338

339
340
341

342
343
344

345
346
347
348
349
350
351
352
353
354
355
356

357
358

359
360

361
362
363
364
365

366
367


368
369
370
371
372
373
374
375

376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
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







+
-
-
-
-
+
+
+
+
+


+
+
+
-
-
+
+
+


+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+




+
-
-
+
+
-
-
-
-
-
+
+
+
+








+
-
-
+
+
+




-
-
-
+
-
-
-
+
-
-






+
-
-
-
-
+
+
+
+
+


-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







-
+

-
+









-
-
-
+
+
+
+
+





-
+
+

-
+


-
+

-
+








-
+

-
+




-
+

-
-
+
+
+
+




+
-
+
-



-
+









-
+

-
+




-
+

-
-
+
+
+
+





-
+
+

-
+


-
+











-
+

-
+

-
+




-
+

-
-
+
+
+
+




-
+







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+




-
+


+
+
+
+
+






+


-
+

-
-
-
-
-
-
+
+
+
+
+
+
+
+
+

-
+
-
-
-
+
+
+







    @ 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("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("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("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("Notification", "setup_notification",
      "Automatic notifications of changes via outbound email");
  setup_menu_entry("Email-Server", "setup_smtp",
    "Activate and configure the built-in email server");
  setup_menu_entry("Transfers", "xfersetup",
    "Configure the transfer system for this repository");
  setup_menu_entry("Skins", "setup_skin",
    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("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("Artifact Receipts Log", "rcvfromlist",
    "A record of received artifacts and their sources");
  setup_menu_entry("User Log", "access_log",
  setup_menu_entry("Log Files", "setup-logmenu",
    "A record of login attempts");
  setup_menu_entry("Administrative Log", "admin_log",
    "View the admin_log entries");
    "A menu of available log files");
  setup_menu_entry("Error Log", "errorlog",
    "View the Fossil server error log");
  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");
    setup_menu_entry("SQL", "admin_sql",
      "Enter raw SQL commands");
    setup_menu_entry("TH1", "admin_th1",
      "Enter raw TH1 commands");
  }
  @ </table>

  style_footer();
  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 VAR table */
  const char *zVar,     /* The corresponding row in the CONFIG table */
  const char *zQParm,   /* The query parameter */
  int dfltVal,          /* Default value if VAR table entry does not exist */
  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 ){
      login_verify_csrf_secret();
      db_set(zVar, iQ ? "1" : "0", 0);
    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)"
  @ <label><input type="checkbox" name="%s(zQParm)" \
  @ aria-label="%h(zLabel[0]?zLabel:zQParm)" \
  if( iVal ){
    @ checked="checked"
    @ checked="checked" \
  }
  if( disabled ){
    @ disabled="disabled"
    @ disabled="disabled" \
  }
  @ /> <b>%s(zLabel)</b></label>
  @ > <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 VAR table */
  const char *zVar,     /* The corresponding row in the CONFIG table */
  const char *zQParm,   /* The query parameter */
  const char *zDflt,    /* Default value if VAR table entry does not exist */
  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 ){
  if( zQ && fossil_strcmp(zQ,zVal)!=0 && cgi_csrf_safe(2) ){
    const int nZQ = (int)strlen(zQ);
    login_verify_csrf_secret();
    db_set(zVar, zQ, 0);
    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" \
  @ <input type="text" id="%s(zQParm)" name="%s(zQParm)" value="%h(zVal)" \
  @ id="%s(zQParm)" name="%s(zQParm)" value="%h(zVal)" size="%d(width)" \
  @ size="%d(width)" \
  if( disabled ){
    @ disabled="disabled" \
  }
  @ /> <b>%s(zLabel)</b>
  @ > <b>%s(zLabel)</b>
}

/*
** Generate a text box for an attribute.
*/
const char *textarea_attribute(
  const char *zLabel,   /* The text label on the textarea */
  int rows,             /* Rows in the textarea */
  int cols,             /* Columns in the textarea */
  const char *zVar,     /* The corresponding row in the VAR table */
  const char *zVar,     /* The corresponding row in the CONFIG table */
  const char *zQP,      /* The query parameter */
  const char *zDflt,    /* Default value if VAR table entry does not exist */
  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){
  if( zQ && !disabled && fossil_strcmp(zQ,z)!=0 && cgi_csrf_safe(2) ){
    const int nZQ = (int)strlen(zQ);
    login_verify_csrf_secret();
    db_set(zVar, zQ, 0);
    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)"
    @ <textarea id="id%s(zQP)" name="%s(zQP)" rows="%d(rows)" \
    @ aria-label="%h(zLabel[0]?zLabel:zQP)" \
    if( disabled ){
      @ disabled="disabled"
      @ disabled="disabled" \
    }
    @ cols="%d(cols)">%h(z)</textarea>
    if( zLabel && *zLabel ){
    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 VAR table */
  const char *zVar,     /* The corresponding row in the CONFIG table */
  const char *zQP,      /* The query parameter */
  const char *zDflt,    /* Default value if VAR table entry does not exist */
  const char *zDflt,    /* Default value if CONFIG table entry does not exist */
  int nChoice,          /* Number of choices */
  const char *const *azChoice /* Choices. 2 per choice: (VAR value, Display) */
  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){
  if( zQ && fossil_strcmp(zQ,z)!=0 && cgi_csrf_safe(2) ){
    const int nZQ = (int)strlen(zQ);
    login_verify_csrf_secret();
    db_set(zVar, zQ, 0);
    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 size="1" name="%s(zQP)" id="id%s(zQP)">
  @ <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 &lt;a&gt;
  @ elements.  In either case, &lt;a&gt; 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 Admin privileges.
** 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="%s(g.zTop)/setup_access" method="post"><div>
  @ <form action="%R/setup_access" method="post"><div>
  login_insert_csrf_secret();
  @ <input type="submit"  name="submit" value="Apply Changes" /></p>
  @ <hr />
  onoff_attribute("Redirect to HTTPS on the Login page",
     "redirect-to-https", "redirhttps", 0, 0);
  @ <p>When selected, force the use of HTTPS for the Login page.
  @ <p>Details:  When enabled, this option causes the $secureurl TH1
  @ <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.  Also when enabled, the
  @ $secureurl is just an alias for $baseurl.
  @ Login page redirects to https if accessed via http.
  @ (Property: "redirhttps")
  @ <hr />
  @ (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
338
339
340
341
342
343
344
345

346
347
348
349
350
351
352
353
354










355
356
357
358
359
360
361
362
363

364
365
366
367
368

369
370
371
372

373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389

390
391
392
393
394
395
396
397
398

399
400
401
402
403
404
405
406
407

408

409
410
411
412
413
414
415
416
417
418
419
420
421
422
423

424
425
426
427
428
429

430
431
432
433
434
435
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
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







-
+








-
+
+
+
+
+
+
+
+
+
+








-
+




-
+



-
+
-
-
-
-
-
-
-
-
-







-
+








-
+









+
-
+











-
-
-
-
+
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
+
-

-
+






-
+




-
-
+
+
+
+
+





-
+

-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+











-
+








-
-
+
+


-
+







  @ <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 />
  @ <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 />
  @ <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 />
  @ <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 Authenitication.
  @ password. This is another way of supporting Basic Authentication.
  @ (Property: "http_authentication_ok")
  @ </p>
  @
  @ <hr />
  @ <hr>
  entry_attribute("IP address terms used in login cookie", 3,
                  "ip-prefix-terms", "ipt", "2", 0);
  @ <p>The number of octets of of the IP address used in the login cookie.
  @ Set to zero to omit the IP address from the login cookie.  A value of
  @ 2 is recommended.
  @ (Property: "ip-prefix-terms")
  @ </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 />
  @ <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 />
  @ <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 />
  @ <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 />
  onoff_attribute(
      "Enable hyperlinks for \"nobody\" based on User-Agent and Javascript",
      "auto-hyperlink", "autohyperlink", 1, 0);
  /* Add the auto-hyperlink settings controls.  These same controls
  @ <p>Enable hyperlinks (the equivalent of the "h" permission) for all users,
  @ including user "nobody", as long as
  @ <ol><li>the User-Agent string in the
  @ HTTP header indicates that the request is coming from an actual human
  @ being, and
  @ <li>the user agent is able to
  ** are also accessible from the /setup_robot page.
  @ run Javascript in order to set the href= attribute of hyperlinks, and
  @ <li>mouse movement is detected (optional - see the checkbox below), and
  @ <li>a number of milliseconds have passed since the page loaded.</ol>
  @
  @ <p>This setting is designed to give easy access to humans while
  @ keeping out robots and spiders.
  @ 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>Additional parameters that control this behavior:</p>
  @ <blockquote>
  onoff_attribute("Require mouse movement before enabling hyperlinks",
                  "auto-hyperlink-mouseover", "ahmo", 0, 0);
  @ <br />
  */
  @ <hr>
  entry_attribute("Delay in milliseconds before enabling hyperlinks", 5,
                  "auto-hyperlink-delay", "ah-delay", "50", 0);
  @ </blockquote>
  @ <p>For maximum robot defense, the "require mouse movement" should
  @ be turned on and the "Delay" should be at least 50 milliseconds.</p>
  @ (Properties: "auto-hyperlink",
  addAutoHyperlinkSettings();
  @ "auto-hyperlink-mouseover", and "auto-hyperlink-delay")</p>

  @ <hr />
  @ <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 />
  @ <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.  Example use case: Set this field
  @ to "/doc/trunk/www/*" to give anonymous users read-only permission to 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 />
  @ <hr>
  onoff_attribute("Allow users to register themselves",
                  "self-register", "selfregister", 0, 0);
  @ <p>Allow users to register themselves through the HTTP UI.
  @ The registration form always requires filling in a CAPTCHA
  @ (<em>auto-captcha</em> setting is ignored). Still, bear in mind that anyone
  @ can register under any user name. This option is useful for public projects
  @ where you do not want everyone in any ticket discussion to be named
  @ "Anonymous".  (Property: "self-register")</p>
                  "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 />
  @ <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 />
  @ <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>
  @ <hr>
  @ <p><input type="submit"  name="submit" value="Apply Changes"></p>
  @ </div></form>
  db_end_transaction(0);
  style_footer();
  style_finish_page();
}

/*
** WEBPAGE: setup_login_group
**
** Change how the current repository participates in a login
** group.
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
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







-
+



+










-
+



+
-
+

+
-
+

-
+

+
-
+

-
+

-
+
+

-
+

+
-
+







    login_needed(0);
    return;
  }
  file_canonical_name(g.zRepositoryName, &fullName, 0);
  zSelfRepo = fossil_strdup(blob_str(&fullName));
  blob_reset(&fullName);
  if( P("join")!=0 ){
    login_group_join(zRepo, zLogin, zPw, zNewName, &zErrMsg);
    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="%s(g.zTop)/setup_login_group" method="post"><div>
    @ <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 \
    @ <tr><th align="right">Repository filename in group to join:</th>
    @ in group to join:</th>
    @ <td width="5"></td><td>
    @ <input aria-labelledby="rfigtj" type="text" size="50" \
    @ <input type="text" size="50" value="%h(zRepo)" name="repo"></td></tr>
    @ value="%h(zRepo)" name="repo"></td></tr>
    @
    @ <tr><th align="right">Login on the above repo:</th>
    @ <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" \
    @ <input type="text" size="20" value="%h(zLogin)" name="login"></td></tr>
    @ value="%h(zLogin)" name="login"></td></tr>
    @
    @ <tr><th align="right">Password:</th>
    @ <tr><th align="right" id="lgpw">Password:</th>
    @ <td width="5"></td><td>
    @ <input type="password" size="20" name="pw"></td></tr>
    @ <input aria-labelledby="lgpw" type="password" size="20" name="pw">\
    @ </td></tr>
    @
    @ <tr><th align="right">Name of login-group:</th>
    @ <tr><th align="right" id="nolg">Name of login-group:</th>
    @ <td width="5"></td><td>
    @ <input aria-labelledby="nolg" type="text" size="30" \
    @ <input type="text" size="30" value="%h(zNewName)" name="newname">
    @ 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;
595
596
597
598
599
600
601
602

603
604
605
606
607
608
609
610

611
612
613
614
615
616
617
821
822
823
824
825
826
827

828
829
830
831
832




833
834
835
836
837
838
839
840







-
+




-
-
-
-
+







      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="%s(g.zTop)/setup_login_group" method="post"><div>
    @ <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>
    @ <br />For best results, use the same number of <a href="setup_access#ipt">
    @ IP octets</a> in the login cookie across all repositories in the
    @ same Login Group.
    @ <hr /><h2>Implementation Details</h2>
    @ <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>
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
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







-
+



















-
+




+


-
+

-
+

-
+






-
+







-
-
+
+


+
-
+

-
+
+
+
+
+
+
+
+



















+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+








-
+






-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


-
+






-
+













+











-
+

-
+







-
+



-
+

-
+



-
+

+

-
+


-
-
+
-


-
+

-
+

+
+
+
+
+


+


-
+



-
+

-
+


-
+

-
+





-
+



+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


-
+








+


-
+

-
-
+
+





-
+





-
+
+
+
+
+
+
+
+
+
+
+
+








-
+

-
+






-
+
-
-
-
-
-
-
-














-
+







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



-
+
-
-








-
-
+
+


-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+









-
+




+




-
+










-
+











-
-
+
+


-
+











-
+




-
+
+

+

+
+


+

-
+

-
+

-
-
+
+

-
+


-
+


-
+


-
-
-
+
+
+

-
+







      @ <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_footer();
  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.Setup ){
  if( !g.perm.Admin ){
    login_needed(0);
    return;
  }

  style_set_current_feature("setup");
  style_header("Timeline Display Preferences");
  db_begin_transaction();
  @ <form action="%s(g.zTop)/setup_timeline" method="post"><div>
  @ <form action="%R/setup_timeline" method="post"><div>
  login_insert_csrf_secret();
  @ <p><input type="submit"  name="submit" value="Apply Changes" /></p>
  @ <p><input type="submit"  name="submit" value="Apply Changes"></p>

  @ <hr />
  @ <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 />
  @ <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",
  @ <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
  @ the first blank line. (Property: "timeline-truncate-at-blank")</p>
  @ in Git repositories (Property: "timeline-truncate-at-blank")</p>

  @ <hr />
  @ <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 />
  @ <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&nbsp;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 />
  @ <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 />
  @ <p><input type="submit"  name="submit" value="Apply Changes" /></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_footer();
  style_finish_page();
}

/*
** WEBPAGE: setup_settings
**
** Change or view miscellaneous settings.  Part of the
** Admin pages requiring Admin privileges.
** /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>
  @ changed on this screen.</p><hr><p>
  @
  @ <form action="%s(g.zTop)/setup_settings" method="post"><div>
  @ <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,
                      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 />
        @  (v)<br>
      } else {
        @ <br />
        @ <br>
      }
    }
  }
  @ <br /><input type="submit"  name="submit" value="Apply Changes" />
  @ <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 ){
    if( pSet->width>0 && !pSet->forceTextArea ){
      int hasVersionableValue = pSet->versionable &&
          (db_get_versioned(pSet->name, NULL)!=0);
      entry_attribute("", /*pSet->width*/ 25, pSet->name,
                      pSet->var!=0 ? pSet->var : pSet->name,
      @ <tr><td>
                      (char*)pSet->def, hasVersionableValue);
      @ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a>
      if( pSet->versionable ){
        @  (v)<br />
        @  (v)
      } else {
        @ <br />
        @
      }
      @</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 ){
    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 />
        @  (v)<br>
      } else {
        @ <br />
        @ <br>
      }
      textarea_attribute("", /*rows*/ 2, /*cols*/ 35, pSet->name,
                      pSet->var!=0 ? pSet->var : pSet->name,
                      pSet->var!=0 ? pSet->var : pSet->name /*works-like:"x"*/,
                      (char*)pSet->def, hasVersionableValue);
      @<br />
      @<br>
    }
  }
  @ </td></tr></table>
  @ </div></form>
  db_end_transaction(0);
  style_footer();
  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 Admin privilege.
** 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="%s(g.zTop)/setup_config" method="post"><div>
  @ <form action="%R/setup_config" method="post"><div>
  login_insert_csrf_secret();
  @ <input type="submit"  name="submit" value="Apply Changes" /></p>
  @ <hr />
  @ <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 />
  @ <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 />
  @ <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 />
  @ <hr>
  entry_attribute("Download Tag", 20, "download-tag", "dlt", "trunk", 0);
  @ <p>The <a href='%R/download'>/download</a> page is designed to provide 
  @ <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 />
  @ <hr>
  onoff_attribute("Enable WYSIWYG Wiki Editing",
                  "wysiwyg-wiki", "wysiwyg-wiki", 0, 0);
  @ <p>Enable what-you-see-is-what-you-get (WYSIWYG) editing of wiki pages.
  @ The WYSIWYG editor generates HTML instead of markup, which makes
  @ subsequent manual editing more difficult.
  @ (Property: "wysiwyg-wiki")</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/tip/index.wiki") or to "/timeline".</p>
  @ 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.
  @ Often these are filled in with links like 
  @ "/doc/trunk/doc/<i>filename</i>.md" so that they refer to 
  @ embedded documentation, or like "/wiki/<i>pagename</i>" to refer
  @ to wiki pages.
  @ Leave blank to omit.
  @ <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>
  entry_attribute("Documentation Index", 40, "sitemap-docidx", "smdocidx",
                  "", 0);
  @ (Property: sitemap-docidx)<br>
  entry_attribute("Download", 40, "sitemap-download", "smdownload",
                  "", 0);
  @ (Property: sitemap-download)<br>
  entry_attribute("License", 40, "sitemap-license", "smlicense",
                  "", 0);
  @ (Property: sitemap-license)<br>
  entry_attribute("Contact", 40, "sitemap-contact", "smcontact",
                  "", 0);
  @ (Property: sitemap-contact)
  @ <hr />
  @ 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: &lt;script&gt;, &lt;form&gt;, 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> &rarr; checked-in files, embedded documentation
  @ <li> <b>f</b> &rarr; forum posts
  @ <li> <b>t</b> &rarr; tickets
  @ <li> <b>w</b> &rarr; 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. This option is helpful
  @ but all other wiki formatting will be ignored.</p>
  @ if you have chosen to use a rich HTML editor for wiki markup such as
  @ TinyMCE.</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>
  @ <hr>
  @ <p><input type="submit"  name="submit" value="Apply Changes"></p>
  @ </div></form>
  db_end_transaction(0);
  style_footer();
  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.Setup ){
  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 />
  @ <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 />
  @ <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>
  @ <hr>
  @ <p><input type="submit"  name="submit" value="Apply Changes"></p>
  @ </div></form>
  db_end_transaction(0);
  style_footer();
  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.Setup ){
  if( !g.perm.Admin ){
    login_needed(0);
    return;
  }
  db_begin_transaction();
  if( P("clear")!=0 && cgi_csrf_safe(1) ){
  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="%s(g.zTop)/setup_adunit" method="post"><div>
  @ <form action="%R/setup_adunit" method="post"><div>
  login_insert_csrf_secret();
  @ <b>Banner Ad-Unit:</b><br />
  @ <b>Banner Ad-Unit:</b><br>
 textarea_attribute("", 6, 80, "adunit", "adunit", "", 0);
  @ <br />
  @ <b>Right-Column Ad-Unit:</b><br />
  @ <br>
  @ <b>Right-Column Ad-Unit:</b><br>
  textarea_attribute("", 6, 80, "adunit-right", "adright", "", 0);
  @ <br />
  @ <br>
  onoff_attribute("Omit ads to administrator",
     "adunit-omit-if-admin", "oia", 0, 0);
  @ <br />
  @ <br>
  onoff_attribute("Omit ads to logged-in users",
     "adunit-omit-if-user", "oiu", 0, 0);
  @ <br />
  @ <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" />
  @ <br>
  @ <input type="submit" name="submit" value="Apply Changes">
  @ <input type="submit" name="clear" value="Delete Ad-Unit">
  @ </div></form>
  @ <hr />
  @ <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
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
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







-
+






-
+










+
+
+
+






+
+
+

-
+




-
+





+











+



+




+






+












+



+




+


+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+



-
-
+
+


-
+









-
+

-
-
+
+


-
+



-
-
+
+


-
+









-
+

-
-
+
+


-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+




-
+







  @   width: 600px;
  @   height: 90px;
  @   border: 1px solid #f11;
  @   background-color: #fcc;
  @ '&gt;Demo Ad&lt;/div&gt;
  @ </pre></blockquote>
  @ </li>
  style_footer();
  style_finish_page();
  db_end_transaction(0);
}

/*
** WEBPAGE: setup_logo
**
** Administrative page for changing the logo image.
** 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.Setup ){
  if( !g.perm.Admin ){
    login_needed(0);
    return;
  }
  db_begin_transaction();
  if( !cgi_csrf_safe(1) ){
  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="%s(g.zTop)/logo/%z(zLogoMtime)" \
  @ alt="logo" border="1" />
  @ <blockquote><p>
  @ <img src="%R/logo/%z(zLogoMtime)" alt="logo" border="1">
  @ </p></blockquote>
  @
  @ <form action="%s(g.zTop)/setup_logo" method="post"
  @ <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/*" />
  @ <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>
  @ <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 />
  @ <hr>
  @
  @ <p>The current background image has a MIME-Type of <b>%h(zBgMime)</b>
  @ and looks like this:</p>
  @ <blockquote><p><img src="%s(g.zTop)/background/%z(zBgMtime)" \
  @ alt="background" border=1 />
  @ <blockquote><p><img src="%R/background/%z(zBgMtime)" \
  @ alt="background" border=1>
  @ </p></blockquote>
  @
  @ <form action="%s(g.zTop)/setup_logo" method="post"
  @ <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/*" />
  @ <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>
  @ <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 />
  @ <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_footer();
  style_finish_page();
  db_end_transaction(0);
}

/*
** Prevent the RAW SQL feature from being used to ATTACH a different
** database and query it.
**
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
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







-
+










-
+
+







}


/*
** WEBPAGE: admin_sql
**
** Run raw SQL commands against the database file using the web interface.
** Requires Admin privileges.
** 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(1) ? P("q") : 0;
  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.
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
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







-
+

-
-
+
+







-
+



-
+
-
-


-
+






-
+
-

+













-
+







         "           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="%s(g.zTop)/admin_sql">
  @ <form method="post" action="%R/admin_sql">
  login_insert_csrf_secret();
  @ SQL:<br />
  @ <textarea name="q" rows="8" cols="80">%h(zQ)</textarea><br />
  @ 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_master"
            "SELECT sql FROM repository.sqlite_schema"
            " WHERE sql IS NOT NULL ORDER BY name");
    go = 1;
  }else if( P("tablelist") ){
    zQ = sqlite3_mprintf(
    zQ = sqlite3_mprintf("SELECT*FROM pragma_table_list ORDER BY schema, name");
            "SELECT name FROM repository.sqlite_master WHERE type='table'"
            " ORDER BY name");
    go = 1;
  }
  if( go ){
  if( go && cgi_csrf_safe(2) ){
    sqlite3_stmt *pStmt;
    int rc;
    const char *zTail;
    int nCol;
    int nRow = 0;
    int i;
    @ <hr />
    @ <hr>
    login_verify_csrf_secret();
    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>
      @ <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>
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
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







-
+


















+





-
+
-
-
-
+
+


-
+



-
+
-








-
+
















-
+



+

+















-
+







        }
        @ </tr>
      }
      sqlite3_finalize(pStmt);
      @ </table>
    }
  }
  style_footer();
  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 method="post" action="%s(g.zTop)/admin_th1">
  form_begin(0, "%R/admin_th1");
  login_insert_csrf_secret();
  @ TH1:<br />
  @ <textarea name="q" rows="5" cols="80">%h(zQ)</textarea><br />
  @ TH1:<br>
  @ <textarea name="q" rows="5" cols="80">%h(zQ)</textarea><br>
  @ <input type="submit" name="go" value="Run TH1">
  @ </form>
  if( go ){
  if( go && cgi_csrf_safe(2) ){
    const char *zR;
    int rc;
    int n;
    @ <hr />
    @ <hr>
    login_verify_csrf_secret();
    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_footer();
  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.Setup && !g.perm.Admin ){
  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");
    "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>
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
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







-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+









-
+



+

-
+





-
+















-
+




-
+

-
+

-
+

-
+

-
+

-
+

-
-
-
+
+
+



+
+






+
+
+
+
+
+
+
-
-
+
+
-
+
+
+


+

-
+


-
+



-
+
















-
+







    @ </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_footer();
  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.Setup && !g.perm.Admin ){
  if( !g.perm.Admin ){
    login_needed(0);
    return;
  }
  style_set_current_feature("setup");
  style_header("Search Configuration");
  @ <form action="%s(g.zTop)/srchsetup" method="post"><div>
  @ <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 />
  @ <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 />
  @ <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 />
  @ <hr>
  onoff_attribute("Search Check-in Comments", "search-ci", "sc", 0, 0);
  @ <br />
  @ <br>
  onoff_attribute("Search Documents", "search-doc", "sd", 0, 0);
  @ <br />
  @ <br>
  onoff_attribute("Search Tickets", "search-tkt", "st", 0, 0);
  @ <br />
  @ <br>
  onoff_attribute("Search Wiki", "search-wiki", "sw", 0, 0);
  @ <br />
  @ <br>
  onoff_attribute("Search Tech Notes", "search-technote", "se", 0, 0);
  @ <br />
  @ <br>
  onoff_attribute("Search Forum", "search-forum", "sf", 0, 0);
  @ <hr />
  @ <p><input type="submit"  name="submit" value="Apply Changes" /></p>
  @ <hr />
  @ <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 FTS4 search index. This makes search
    @ run faster, especially on large repositories, but takes up space.</p>
    @ <p>Currently using an SQLite FTS%d(search_index_type(0)) search index.
    @ The index helps search run faster, especially on large repositories,
    onoff_attribute("Use Porter Stemmer","search-stemmer","ss",0,0);
    @ 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 FTS4 search index is disabled.  All searching will be
    @ <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>
    onoff_attribute("Use Porter Stemmer","search-stemmer","ss",0,0);
    select_fts_tokenizer();
    @ <p><input type="submit" name="fts1" value="Create A Full-Text Index">
  }
  @ </div></form>
  style_footer();
  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(1) ) return;
  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;
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
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







-
+



+

-
+





-

















+

+








-
+







** Configure the URL aliases
*/
void page_waliassetup(){
  Stmt q;
  int cnt = 0;
  Blob namelist;
  login_check_credentials();
  if( !g.perm.Setup && !g.perm.Admin ){
  if( !g.perm.Admin ){
    login_needed(0);
    return;
  }
  style_set_current_feature("setup");
  style_header("URL Alias Configuration");
  if( P("submit")!=0 ){
  if( P("submit")!=0 && cgi_csrf_safe(2) ){
    Blob token;
    Blob sql;
    const char *zNewName;
    const char *zValue;
    char zCnt[10];
    login_verify_csrf_secret();
    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="%s(g.zTop)/waliassetup" method="post"><div>
  @ <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);
1688
1689
1690
1691
1692
1693
1694
1695

1696
2327
2328
2329
2330
2331
2332
2333

2334
2335







-
+

  @ </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_footer();
  style_finish_page();
}

Changes to src/setupuser.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
46
47
48
49




50
51

52
53
54
55
56
57
58
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

61
62
63
64
65
66
67
68







+
+
+














+





+
+










+
+
+
+

-
+







**   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 ){
  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,
89
90
91
92
93
94
95



96
97
98
99
100
101










102
103

104
105


106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121














122

123
124
125
126
127
128

129
130
131
132
133


134

135
136
137
138
139
140
141
142
143
144
145
146
147
148


149
150
151



152


153
154
155
156
157
158
159








160
161
162
163
164
165
166

167
168
169
170
171
172
173
174
175
176

177
178
179
180
181
182
183
99
100
101
102
103
104
105
106
107
108






109
110
111
112
113
114
115
116
117
118
119

120
121

122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153

154
155
156
157
158
159

160
161
162
163
164

165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190

191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213

214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232







+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+

-
+

-
+
+
















+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+





-
+




-
+
+

+














+
+



+
+
+
-
+
+







+
+
+
+
+
+
+
+






-
+










+







      @ </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( 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='ktxTTK' data-init-sort='2'>
  @  data-column-types='ktxTTKt' data-init-sort='2'>
  @ <thead><tr>
  @ <th>Login Name<th>Caps<th>Info<th>Date<th>Expire<th>Last Login</tr></thead>
  @ <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":"");
  if( zWith && zWith[0] ){
  }else if( zWith && zWith[0] ){
    zWith = mprintf(" AND fullcap(cap) GLOB '*[%q]*'", zWith);
  }else{
    zWith = "";
  }
  db_prepare(&s,
     "SELECT uid, login, cap, info, date(mtime,'unixepoch'),"
     "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"
             "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>
      @ <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_footer();
  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>
205
206
207
208
209
210
211
212

213
214
215
216
217
218
219
220
221

222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239

240
241
242
243
244
245
246
254
255
256
257
258
259
260

261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288

289
290
291
292
293
294
295
296







-
+









+

















-
+







  @ <span class="usertype">nobody</span>.
  @ </p></li>
  @
  @ <li><p>The permission flags are as follows:</p>
  capabilities_table(CAPCLASS_ALL);
  @ </li>
  @ </ol>
  style_footer();
  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_footer();
  style_finish_page();
}

/*
** Return true if zPw is a valid password string.  A valid
** password string is:
**
**  (1)  A zero-length string, or
290
291
292
293
294
295
296
297

298
299
300






301




302
303
304
305
306
307
308
309
310
311
312
313
314
315
316


317
318
319
320
321
322
323
324
325
326
327
328
329

330
331
332
333
334
335
336
337
338
339

340
341
342
343
344
345
346
340
341
342
343
344
345
346

347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390

391
392
393
394
395
396
397
398
399
400

401
402
403
404
405
406
407
408







-
+



+
+
+
+
+
+

+
+
+
+















+
+












-
+









-
+







  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(1) ){
  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(1) ){
  }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' || g.perm.Setup) && P(zNm)!=0;
      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;
    }
356
357
358
359
360
361
362
363

364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380

381
382
383


384
385
386
387
388










389
390
391
392
393
394
395
396
397
398
399
400
401
402
403














404
405
406
407
408
409
410
411
412
413
414

415

416
417
418
419
420
421
422
423
424
425
426
427
428

429
430
431
432
433
434
435
418
419
420
421
422
423
424

425
426
427
428
429
430
431
432
433
434
435
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







-
+
















-
+


-
+
+





+
+
+
+
+
+
+
+
+
+















+
+
+
+
+
+
+
+
+
+
+
+
+
+











+

+












-
+







    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_footer();
      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_footer();
      style_finish_page();
      return;
    }
    login_verify_csrf_secret();
    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_footer();
        style_finish_page();
        return;
      }
    }
    cgi_redirect(cgi_referer("setup_ulist"));
    return;
  }

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
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







+
-
+
+
+














-
+

+
-
+
+

-
+
+



-
+



+
-
-
+
+
+
+
+
+
+
+
+
+
+

-
-
+
+
+









-
+


-
+

-
+

-
+

+
-
+

+
-
+

-
+

-
+

-
+

-
+

-
+

-
+

-
+

-
+

-
+

-
+

-
+

-
+

-
+

-
+

-
+

-
+

-
+

-
+

-
+

-
+

-
+

-
+

-
+

-
+

-
+

-
+

-
+

-
+
+
+













-
+


+
-
+
+


+
-
+
+









-
+







  }

  /* 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("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">User ID:</td>
  @   <td class="usetupEditLabel" id="suuid">User ID:</td>
  if( uid ){
    @   <td>%d(uid) <input aria-labelledby="suuid" type="hidden" \
    @   <td>%d(uid) <input type="hidden" name="id" value="%d(uid)" /></td>
    @   name="id" value="%d(uid)"/>\
    @ </td>
  }else{
    @   <td>(new user)<input type="hidden" name="id" value="0" /></td>
    @   <td>(new user)<input aria-labelledby="suuid" type="hidden" name="id" \
    @ value="0"></td>
  }
  @ </tr>
  @ <tr>
  @   <td class="usetupEditLabel">Login:</td>
  @   <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" \
    @   <td><input type="text" name="login" value="%h(zLogin)" /></td>
    @ </tr>
    @ value="%h(zLogin)">
    if( alert_tables_exist() ){
      int sid;
      sid = db_int(0, "SELECT subscriberId FROM subscriber"
                      " WHERE suname=%Q", zLogin);
      if( sid>0 ){
        @ &nbsp;&nbsp;<a href="%R/alerts?sid=%d(sid)">\
        @ (subscription info for %h(zLogin))</a>\
      }
    }
    @ </td></tr>
    @ <tr>
    @   <td class="usetupEditLabel">Contact&nbsp;Info:</td>
    @   <td><textarea name="info" cols="40" rows="2">%h(zInfo)</textarea></td>
    @   <td class="usetupEditLabel" id="sucnfo">Contact&nbsp;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']) />
    @  <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']) />
  @  <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']) />
  @  <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']) />
  @  <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']) />
  @  <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']) />
  @  <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']) />
  @  <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']) />
  @  <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']) />
  @  <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']) />
  @  <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']) />
  @  <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']) />
  @  <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']) />
  @  <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']) />
  @  <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']) />
  @  <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']) />
  @  <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']) />
  @  <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']) />
  @  <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']) />
  @  <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']) />
  @  <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']) />
  @  <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']) />
  @  <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']) />
  @  <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']) />
  @  <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']) />
  @  <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']) />
  @  <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']) />
  @  <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']) />
  @  <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']) />
  @  <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']) />
  @  <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']) />
  @  <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']) />
  @  <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']) />
  @  <li><label><input type="checkbox" name="aA"%s(oa['A'])>
  @  Send Announcements%s(B('A'))</label>
  @  <li><label><input type="checkbox" name="aD"%s(oa['D']) />
  @  <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">Password:</td>
    @   <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" \
      @   <td><input type="password" name="pw" value="**********" /></td>
      @   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 type="password" name="pw" value="" /></td>
      @   <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 />
    @ 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>
660
661
662
663
664
665
666
667

668
669
670
671
672
673
674
774
775
776
777
778
779
780

781
782
783
784
785
786
787
788







-
+







    }
    @   <input type="submit" name="can" value="Cancel"></td>
    @ </tr>
  }
  @ </table>
  @ </div></form>
  @ </div>
  style_load_one_js_file("useredit.js");
  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).
694
695
696
697
698
699
700
701

702
703
704
705
706
707
708
808
809
810
811
812
813
814

815
816
817
818
819
820
821
822







-
+







  @ 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 
  @ 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
821
822
823
824
825
826
827
828

829
935
936
937
938
939
940
941

942
943







-
+

  @ <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_footer();
  style_finish_page();
}

Changes to src/sha1.c.

30
31
32
33
34
35
36
37


38
39
40
41
42
43
44
30
31
32
33
34
35
36

37
38
39
40
41
42
43
44
45







-
+
+







**
** 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*);
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;
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
100
101
102
103
104
105
106













107
108
109






110
111
112
113
114
115
116







-
-
-
-
-
-
-
-
-
-
-
-
-



-
-
-
-
-
-








/*
 * 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.
 */
#if __GNUC__ && (defined(__i386__) || defined(__x86_64__))
/*
 * GCC by itself only generates left rotates.  Use right rotates if
 * possible to be kinder to dinky implementations with iterative rotate
 * instructions.
 */
#define SHA_ROT(op, x, k) \
        ({ unsigned int y; asm(op " %1,%0" : "=r" (y) : "I" (k), "0" (x)); y; })
#define rol(x,k) SHA_ROT("roll", x, k)
#define ror(x,k) SHA_ROT("rorl", x, k)

#else
/* Generic C equivalent */
#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)
#endif





#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))

/*
293
294
295
296
297
298
299
300

301
302
303
304
305
306
307
275
276
277
278
279
280
281

282
283
284
285
286
287
288
289







-
+







    *zBuf++ = zEncode[(*digest>>4)&0xf];
    *zBuf++ = zEncode[*digest++ & 0xf];
  }
  *zBuf = '\0';
}

/*
** The state of a incremental SHA1 checksum computation.  Only one
** 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.
409
410
411
412
413
414
415













416
417
418
419
420
421
422
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417







+
+
+
+
+
+
+
+
+
+
+
+
+







    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;
518
519
520
521
522
523
524
525
526

527
528
529
530






531
532
533
534
535
536
537
513
514
515
516
517
518
519

520
521




522
523
524
525
526
527
528
529
530
531
532
533
534







-

+
-
-
-
-
+
+
+
+
+
+







/*
** 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:
**
** 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.
**    -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 ){

Changes to src/sha1hard.c.

71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87










88
89
90
91
92
93





94
95
96
97
98
99
100
71
72
73
74
75
76
77










78
79
80
81
82
83
84
85
86
87
88





89
90
91
92
93
94
95
96
97
98
99
100







-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+

-
-
-
-
-
+
+
+
+
+







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;
  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];
  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

Changes to src/sha3.c.

414
415
416
417
418
419
420
421

422
423
424
425
426
427
428
414
415
416
417
418
419
420

421
422
423
424
425
426
427
428







-
+







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 && ((aData - (const unsigned char*)0)&7)==0 ){
  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;
      }
482
483
484
485
486
487
488
489

490
491
492
493
494
495
496
482
483
484
485
486
487
488

489
490
491
492
493
494
495
496







-
+







    *zBuf++ = zEncode[(*digest>>4)&0xf];
    *zBuf++ = zEncode[*digest++ & 0xf];
  }
  *zBuf = '\0';
}

/*
** The state of a incremental SHA3 checksum computation.  Only one
** 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.
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
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







-






-
+

+
+




-
+







** 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
**    -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;
  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;
661
662
663
664
665
666
667
668
669
670
671
672
673


674
675
676
677
678
662
663
664
665
666
667
668

669
670
671


672
673
674
675
676
677
678







-



-
-
+
+





      }
      iSize = n;
    }
  }
  verify_all_options();

  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);
      sha3sum_blob(&in, iSize, &cksum);
    }else{
      sha3sum_file(g.argv[i], eFType, 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);
  }
}

Deleted src/shell.c.

more than 10,000 changes

Changes to src/shun.c.

45
46
47
48
49
50
51

52
53
54
55
56
57
58
59
60
61
62
63
64
65

66
67
68
69
70
71
72
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65

66
67
68
69
70
71
72
73







+













-
+







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, 0);
    rebuild_db(0, 0);
    admin_log("Rebuilt database.");
    db_end_transaction(0);
  }
  if( zUuid ){
    char *p;
    int i = 0;
    int j = 0;
83
84
85
86
87
88
89
90

91
92
93
94
95
96
97
98
99
100
101
102
103

104
105
106
107
108
109
110
111
112
113
114
115
116

117
118

119
120
121
122

123
124

125
126
127
128
129
130
131
132

133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154

155
156

157
158
159
160
161





















































162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180


181
182
183
184
185
186
187
188
189
190

191
192

193
194
195
196
197
198
199
200
201
202




203


204
205


206
207
208
209
210
211
212
213
214
215
216
217

218
219

220
221
222
223
224
225
226
227
228
229
230
231
232

233
234
235
236
237
238
239
240
241
242

243
244

245
246
247
248

249
250
251
252
253
254
255
256
257
258

259
260

261
262
263
264
265
266
267
268

269
270
271
272
273
274
275
84
85
86
87
88
89
90

91
92
93
94
95
96
97
98
99
100
101
102
103

104
105
106

107
108
109
110
111
112
113
114
115

116
117

118
119
120
121

122
123

124
125
126
127
128
129
130
131

132
133
134

135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152

153
154

155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230


231
232
233
234
235
236
237
238
239
240
241

242
243

244
245
246
247
248
249
250
251
252
253
254
255
256
257
258

259
260
261

262
263
264
265
266
267
268
269
270
271
272
273
274

275
276

277
278
279
280
281
282
283
284
285
286
287
288
289

290
291
292
293
294
295
296
297
298
299

300
301

302
303
304
305

306
307
308
309
310
311
312
313
314
315

316
317

318
319
320
321
322
323
324
325

326
327
328
329
330
331
332
333







-
+












-
+


-









-
+

-
+



-
+

-
+







-
+


-


















-
+

-
+





+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

















-
-
+
+









-
+

-
+










+
+
+
+
-
+
+

-
+
+











-
+

-
+












-
+









-
+

-
+



-
+









-
+

-
+







-
+







      }
      i++;
    }
    zCanonical[j+1] = zCanonical[j] = 0;
    p = zCanonical;
    while( *p ){
      int nUuid = strlen(p);
      if( !hname_validate(p, nUuid) ){
      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") ){
  if( zUuid && P("sub") && cgi_csrf_safe(2) ){
    const char *p = zUuid;
    int allExist = 1;
    login_verify_csrf_secret();
    while( *p ){
      db_multi_exec("DELETE FROM shun WHERE uuid=%Q", p);
      if( !db_exists("SELECT 1 FROM blob WHERE uuid=%Q", p) ){
        allExist = 0;
      }
      admin_log("Unshunned %Q", p);
      p += strlen(p)+1;
    }
    if( allExist ){
      @ <p class="noMoreShun">Artifact(s)<br />
      @ <p class="noMoreShun">Artifact(s)<br>
      for( p = zUuid ; *p ; p += strlen(p)+1 ){
        @ <a href="%R/artifact/%s(p)">%s(p)</a><br />
        @ <a href="%R/artifact/%s(p)">%s(p)</a><br>
      }
      @ are no longer being shunned.</p>
    }else{
      @ <p class="noMoreShun">Artifact(s)<br />
      @ <p class="noMoreShun">Artifact(s)<br>
      for( p = zUuid ; *p ; p += strlen(p)+1 ){
        @ %s(p)<br />
        @ %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") ){
  if( zUuid && P("add") && cgi_csrf_safe(2) ){
    const char *p = zUuid;
    int rid, tagid;
    login_verify_csrf_secret();
    while( *p ){
      db_multi_exec(
        "INSERT OR IGNORE INTO shun(uuid,mtime)"
        " VALUES(%Q, now())", p);
      db_multi_exec("DELETE FROM attachment WHERE src=%Q", p);
      rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", p);
      if( rid ){
        db_multi_exec("DELETE FROM event WHERE objid=%d", rid);
      }
      tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname='tkt-%q'", p);
      if( tagid ){
        db_multi_exec("DELETE FROM ticket WHERE tkt_uuid=%Q", p);
        db_multi_exec("DELETE FROM tag WHERE tagid=%d", tagid);
        db_multi_exec("DELETE FROM tagxref WHERE tagid=%d", tagid);
      }
      admin_log("Shunned %Q", p);
      p += strlen(p)+1;
    }
    @ <p class="shunned">Artifact(s)<br />
    @ <p class="shunned">Artifact(s)<br>
    for( p = zUuid ; *p ; p += strlen(p)+1 ){
      @ <a href="%R/artifact/%s(p)">%s(p)</a><br />
      @ <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 the full 40- or 64-character artifact hashes,
  @ not an abbreviation or a symbolic tag.</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="%s(g.zTop)/%s(g.zPath)"><div>
  @ <form method="post" action="%R/%s(g.zPath)"><div>
  login_insert_csrf_secret();
  @ <textarea class="fullsize-text" cols="50" rows="%d(numRows)" name="uuid">
  @ <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="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="%s(g.zTop)/%s(g.zPath)"><div>
  @ <form method="post" action="%R/%s(g.zPath)"><div>
  login_insert_csrf_secret();
  @ <textarea class="fullsize-text" cols="50" rows="%d(numRows)" name="uuid">
  @ <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" />
  @ <input type="submit" name="sub" value="Accept">
  @ </div></form>
  @ </blockquote>
  @
  @ <p>Press the Rebuild button below to rebuild the repository.  The
  @ content of newly shunned artifacts is not purged until the repository
  @ is rebuilt.  On larger repositories, the rebuild may take minute or
  @ two, so be patient after pressing the button.</p>
  @
  @ <blockquote>
  @ <form method="post" action="%s(g.zTop)/%s(g.zPath)"><div>
  @ <form method="post" action="%R/%s(g.zPath)"><div>
  login_insert_csrf_secret();
  @ <input type="submit" name="rebuild" value="Rebuild" />
  @ <input type="submit" name="rebuild" value="Rebuild">
  @ </div></form>
  @ </blockquote>
  @
  @ <hr /><p>Shunned Artifacts:</p>
  @ <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 />
      @ <b><a href="%R/artifact/%s(zUuid)">%s(zUuid)</a></b><br>
    }else{
      @ <b>%s(zUuid)</b><br />
      @ <b>%s(zUuid)</b><br>
    }
  }
  if( cnt==0 ){
    @ <i>no artifacts are shunned on this server</i>
  }
  db_finalize(&q);
  @ </p></blockquote>
  style_footer();
  style_finish_page();
  fossil_free(zCanonical);
}

/*
** Remove from the BLOB table all artifacts that are in the SHUN table.
*/
void shun_artifacts(void){
314
315
316
317
318
319
320

321
322
323
324
325
326
327
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386







+








  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",
379
380
381
382
383
384
385
386

387
388
389
390
391
392
393
438
439
440
441
442
443
444

445
446
447
448
449
450
451
452







-
+







  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 *zHashType[] = { "", "sha1", "sha3", "both" };
    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) ){
401
402
403
404
405
406
407
408

409
410
411
412
413
414
415
460
461
462
463
464
465
466

467
468
469
470
471
472
473
474







-
+







      @ <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_footer();
  style_finish_page();
}

/*
** WEBPAGE: rcvfrom
**
** Show a single RCVFROM table entry identified by the rcvid= query
** parameters.  Requires Admin privilege.
476
477
478
479
480
481
482
483

484
485
486
487
488
489
490
535
536
537
538
539
540
541

542
543
544
545
546
547
548
549







-
+







    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 />
    @ %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)
    ){
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
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







-
+

-
+


















-
+

      int isDeleted = zHash==0;
      if( cnt==0 ){
        @ <tr><th valign="top" align="right">Unversioned&nbsp;Files:</th>
        @ <td valign="top">
      }
      cnt++;
      if( isDeleted ){
        @ %h(zName) (deleted)<br />
        @ %h(zName) (deleted)<br>
      }else{
        @ <a href="%R/uv/%h(zName)">%h(zName)</a> (size: %d(size))<br />
        @ <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_footer();
  style_finish_page();
}

Changes to src/sitemap.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
52
53

54
55
56
57
58
59

60

61




62
63
64
65
66
67
68
69
70
71
72
73
74








































75
76
77
78
79
80
81


82
83
84

85
86
87

88
89
90
91
92
93
94



95
96
97
98
99
100
101
102

103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118



119
120
121
122
123
124
125
126
127

128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145

146

147
148
149
150
151
152
153
154


155
156
157
158
159

160
161
162



163
164
165
166
167
168
169
170
171
172

173
174
175
176
177
178


179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196

197




198
199


200

201

202



203
204















205
206
207
208














209
210








211

212
213




214
215

216
217
218

219


220

221
222
223






















































224
225
226

227
228
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73


74
75
76
77
78

79
80
81
82
83
84
85
86
87
88

89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156

157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177


178


179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203

204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221

222
223
224
225
226
227
228
229
230


231
232
233

234
235

236



237
238
239

240
241
242




243

244
245
246
247
248


249
250


251
252
253
254
255
256
257
258
259
260
261

262
263
264

265
266
267
268
269
270


271
272

273
274
275

276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295




296
297
298
299
300
301
302
303
304
305
306
307
308
309


310
311
312
313
314
315
316
317
318
319


320
321
322
323
324

325
326
327

328
329
330
331

332



333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388

389
390
391







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+






+
+
+
+









+


-
-
+
+



-
+






+

+
-
+
+
+
+













+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







+
+


-
+



+







+
+
+






-
-
+
-
-














+
+
+








-
+

















-
+

+






-
-
+
+

-


-
+
-
-
-
+
+
+
-



-
-
-
-

-
+




-
-
+
+
-
-











-



-
+

+
+
+
+
-
-
+
+
-
+

+
-
+
+
+


+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+

+
-
-
+
+
+
+

-
+


-
+

+
+
-
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


-
+


/*
** 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 && cgi_csrf_safe(0) ){
    /* If this is a POST from the same origin with the popup=1 parameter,
  if( P("popup")!=0 ){
    /* The "popup" query parameter
    ** then disable anti-robot defenses */
    isPopup = 1;
    g.perm.Hyperlink = 1;
    g.javascriptHyperlink = 0;
    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>
    @ <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;    
    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/timeline?n=all&namechng"))File name changes</a></li>
    @   <li>%z(href("%R/timeline?n=all&forks"))Forks</a></li>
    @   <li>%z(href("%R/sitemap-timeline"))Other timelines</a></li>
    @   <li>%z(href("%R/timeline?a=1970-01-01&y=ci&n=10"))First 10
    @       check-ins</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</a>
    @ <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/wiki?name=Sandbox"))Sandbox</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>
    if( login_self_register_available(0) ){
       @ <ul>
    @ <ul>
    if( login_self_register_available(0) ){
       @ <li>%z(href("%R/register"))Create a new account</a></li>
       inSublist = 1;
    }
  }else {
    @ <li>%z(href("%R/logout"))Logout</a>
    @ <li>%z(href("%R/logout"))Logout from %h(g.zLogin)</a>
    if( g.perm.Password ){
      @ <ul>
      @ <li>%z(href("%R/logout"))Change Password</a></li>
    @ <ul>
    if( g.perm.Password ){
      @ <li>%z(href("%R/logout"))Change Password for %h(g.zLogin)</a></li>
      inSublist = 1;
    }
  }
  if( alert_enabled() && g.perm.EmailAlert ){
    if( !inSublist ){
      inSublist = 1;
      @ <ul>
    }
    if( login_is_individual() ){
      @ <li>%z(href("%R/alerts"))Email Alerts</a></li>
      @ <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>
    }
  }
  if( inSublist ){
    @ </ul>
  @ <li>%z(href("%R/cookies"))Cookies</a></li>
  @ </ul>
    inSublist = 0;
  }
  @ </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>
    @   <li>%z(href("%R/timewarps"))List of "Timewarp" Check-ins</a></li>
    @   </ul>
    @ </li>
  }
  @ <li>Help
  @ <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/wiki_rules"))Wiki Formatting Rules</a></li>
    @   <li>%z(href("%R/md_rules"))Markdown Formatting Rules</a></li>
  @   <li>%z(href("%R/help"))List of All Commands and Web Pages</a></li>
  }
  @   <li>%z(href("%R/test-all-help"))All "help" text on a single page</a></li>
  if( g.perm.Admin || g.perm.Write || g.perm.WrUnver ){
  @   <li>%z(href("%R/mimetype_list"))Filename suffix to mimetype map</a></li>
    @   <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 ){
    @ <li>%z(href("%R/setup"))Administration Pages</a>
    @   <ul>
    @   <li>%z(href("%R/modreq"))Pending Moderation Requests</a></li>
    @   <li>%z(href("%R/admin_log"))Admin log</a></li>
    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 */

    @   <li>%z(href("%R/cachestat"))Status of the web-page cache</a></li>
    @   </ul></li>
  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 ){
  @ <li>Test Pages
  @   <ul>
    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>
    @ <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-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"))Page to experiment with the automatic
  @ <li>%z(href("%R/hash-color-test"))Hash color test</a>
  @       colors assigned to branch names</a>
  @   <li>%z(href("%R/test-captcha"))Random ASCII-art Captcha image</a></li>
  @   </ul></li>
  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_footer();
    style_finish_page();
  }
}

Changes to src/skins.c.

17
18
19
20
21
22
23







24
25
26
27
28
29
30
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37







+
+
+
+
+
+
+







**
** 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").
**
38
39
40
41
42
43
44


45
46
47
48




49
50
51
52

53
54
55
56

57
58
59
60
61
62

63
64
65
66
67
68
69
70
71
72
73
74
75
76
77























78
79
80
81
82
83
84
85
86
87
88
89




90
91
92
93




94
95
96
97
98
99
100
101
102
103
104
105
106









































107
108

109
110


111

112

113
114
115

116












117
118
119
120
121

122
123
124
125
126
127
128
129
130
131
132
133
134
135



136
137
138
139
140

141
142
143
144
145
146
147
148
149

150
151
152
153
154
155
156
45
46
47
48
49
50
51
52
53
54



55
56
57
58
59



60




61
62
63
64
65
66

67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121




122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180

181
182
183
184
185

186
187
188
189
190

191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231

232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249







+
+

-
-
-
+
+
+
+

-
-
-
+
-
-
-
-
+





-
+















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+












+
+
+
+
-
-
-
-
+
+
+
+













+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+


+
+
-
+

+


-
+

+
+
+
+
+
+
+
+
+
+
+
+





+














+
+
+




-
+









+







*/
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 },
  { "Blitz, No Logo",                    "blitz_no_logo",     0 },
  { "Bootstrap",                         "bootstrap",         0 },
  { "Xekri",                             "xekri",             0 },
  { "Dark Mode",                         "darkmode",          0 },
  { "Eagle",                             "eagle",             0 },
  { "Étienne",                           "etienne",           0 },
  { "Khaki",                             "khaki",             0 },
  { "Original",                          "original",          0 },
  { "Enhanced Original",                 "enhanced1",         0 },
  { "Shadow boxes & Rounded Corners",    "rounded1",          0 },
  { "Eagle",                             "eagle",             0 },
  { "Plain Gray",                        "plain_gray",        0 },
  { "Black & White, Menu on Left",       "black_and_white",   0 },
  { "Plain Gray, No Logo",               "plain_gray",        0 },
  { "Khaki, No Logo",                    "khaki",             0 },
  { "Ardoise",                           "ardoise",           0 },
  { "Xekri",                             "xekri",             0 },
};

/*
** A skin consists of five "files" named here:
*/
static const char *azSkinFile[] = { 
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"  },
  { "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){
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( strchr(zName, '/')!=0 ){
  if( zName && 1==rank && strchr(zName, '/')!=0 ){
    zAltSkinDir = fossil_strdup(zName);
    iSkinSource = iSource;
    return 0;
  }
  if( sqlite3_strglob("draft[1-9]", zName)==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);
    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"
169
170
171
172
173
174
175














176
177
178
179
180
181
182
183
184
185
186


187
188
189
190
191
192
193
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302







+
+
+
+
+
+
+
+
+
+
+
+
+
+











+
+







    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
279
280
281
282
283
284
285



286
287
288
289
290
291
292
293
294
295
296
297


298

299
300
301
302
303
304
305
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411

412
413
414
415
416
417
418
419







+
+
+












+
+
-
+








/*
** 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, MANIFEST_UUID);
  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.
**
353
354
355
356
357
358
359

360
361
362
363

364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381

382
383
384
385
386
387

388
389
390
391
392
393
394
395
396
397
398
399

400
401

402
403
404
405

406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421

422
423
424
425
426
427

428
429
430
431
432
433
434
435
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
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







+




+


















+





-
+











-
+


+




+
















+





-
+









-
+


+





+



+
+
+
+
+
+
+
+
+
+












-

+
+
+
+


-
+









+
+
-
+

-
+

-
+


-
-
-
+
+
+


-
+



+
+
+
+
+
-
-
+
+
+
+




+

+

+
+
+
+
+





-
+
+
+
+
+
+
+
+
+
+
+
+



-
+


+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
-
+



+

+






+

+



-
+
+
+
+
+
+
+





-
+



+
+
-
-
+
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+






-



-
+
-


-
+
-
-
-
-
-
-
+
+
+
-





-
-
-
-
-
-
-
-
-











-
+
-


-
+
+
+


+





-
+













+

-
-
+
+






-
+


-
+


-
-
-
+
+
+
+







+









-
+



-
+
+



-
+
+





+
+
+
+
+
+
+





-
+







      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="%s(g.zTop)/setup_skin_admin" method="post"><div>
    @ <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_footer();
    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="%s(g.zTop)/setup_skin_admin" method="post"><div>
    @ <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_footer();
    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 seenCurrent = 0;
  int once;
  const char *zOverride = 0;
  const char *zDfltSkin = 0;
  int seenDefault = 0;
  int hasCustom;

  login_check_credentials();
  if( !g.perm.Setup ){
  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(1) ){
  if( cgi_csrf_safe(2) ){
    /* Process requests to delete a user-defined skin */
    if( P("del1") && (zName = skinVarName(P("sn"), 1))!=0 ){
    if( P("del1") && P("sn")!=0 ){
      style_header("Confirm Custom Skin Delete");
      @ <form action="%s(g.zTop)/setup_skin_admin" method="post"><div>
      @ <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" />
      @ <input type="hidden" name="sn" value="%h(P("sn"))">
      @ <input type="submit" name="del2" value="Confirm - Delete The Skin">
      @ <input type="submit" name="cancel" value="Cancel - Do Not Delete">
      login_insert_csrf_secret();
      @ </div></form>
      style_footer();
      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')");
    if( P("del2")!=0 && (zName = skinVarName(P("sn"), 1))!=0 ){
      db_multi_exec("DELETE FROM config WHERE name=%Q", zName);
      }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_multi_exec(
            "INSERT INTO config(name,value,mtime) VALUES("
            "  strftime('skin:Backup On %%Y-%%m-%%d %%H:%%M:%%S'),"
            "  %Q,now())", zCurrent
          );
        }
      }
        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; i<count(aBuiltinSkin); i++){
      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></th>
  @ <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>&nbsp;&nbsp;<td>
    @ <form action="%R/setup_skin_admin" method="POST">
    login_insert_csrf_secret();
    if( fossil_strcmp(aBuiltinSkin[i].zSQL, zCurrent)==0 ){
      @ (Currently In Use)
    if( zDfltSkin==0 || fossil_strcmp(aBuiltinSkin[i].zLabel, zDfltSkin)!=0 ){
                                /* vvvv--- mnemonic: Built-In Skin Label */
      seenCurrent = 1;
    }else{
      @ <form action="%s(g.zTop)/setup_skin_admin" method="post">
      @ <input type="hidden" name="sn" value="%h(z)" />
      @ <input type="submit" name="load" value="Install" />
      if( pAltSkin==&aBuiltinSkin[i] ){
        @ (Current override)
      }
      @ </form>
    }
      @ <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>&nbsp;<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>&nbsp;&nbsp;<td>
  }else{
    @ <td><i>(None)</i><td>&nbsp;&nbsp;<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), value FROM config"
     "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);
    const char *zV = db_column_text(&q, 1);
    i++;
    if( once ){
      once = 0;
      @ <tr><td colspan=4><h2>Skins saved as "skin:*' entries \
      @ <tr><td colspan=4><h2>Backups of past custom skins:</h2></td></tr>
      @ in the CONFIG table:</h2></td></tr>
    }
    @ <tr><td>%d(i).<td>%h(zN)<td>&nbsp;&nbsp;<td>
    @ <form action="%s(g.zTop)/setup_skin_admin" method="post">
    @ <form action="%R/setup_skin_admin" method="post">
    if( fossil_strcmp(zV, zCurrent)==0 ){
      @ (Currently In Use)
      seenCurrent = 1;
    }else{
      @ <input type="submit" name="load" value="Install">
      @ <input type="submit" name="del1" value="Delete">
    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);
  if( !seenCurrent ){
    i++;
    @ <tr><td colspan=4><h2>Current skin in css/header/footer/details entries \
    @ in the CONFIG table:</h2></td></tr>
    @ <tr><td>%d(i).<td><i>Current</i><td>&nbsp;&nbsp;<td>
    @ <form action="%s(g.zTop)/setup_skin_admin" method="post">
    @ <input type="submit" name="save" value="Backup">
    @ </form>
  }
  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 stored as "draft[1-9]-*' entries \
      @ <tr><td colspan=4><h2>Draft skins:</h2></td></tr>
      @ in the CONFIG table:</h2></td></tr>
    }
    @ <tr><td>%d(i).<td>%h(zN)<td>&nbsp;&nbsp;<td>
    @ <form action="%s(g.zTop)/setup_skin_admin" method="post">
    @ <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_footer();
  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 ){
    @ <option value='current'>Currently In Use</option>
  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) (built-in)</option>
      @ %h(aBuiltinSkin[i].zDesc)</option>
    }else{
      @ <option value='%s(zName)'>\
      @ %h(aBuiltinSkin[i].zDesc) (built-in)</option>
      @ %h(aBuiltinSkin[i].zDesc)</option>
    }
  }
  for(i=1; i<=9; i++){
    char zName[20];
    sqlite3_snprintf(sizeof(zName), zName, "draft%d", i);
  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 = db_get(zFile, "");
    zResult = skin_get(zFile);
  }else if( sqlite3_strglob("draft[1-9]", zLabel)==0 ){
    zResult = db_get_mprintf("", "%s-%s", zLabel, zFile);
  }else{
    while( 1 ){
    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 || fossil_strcmp(zLabel,"default")==0 ) break;
      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 Setup privileges.
** 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;
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
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







-














-
+







    /* 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 *zKey;                 /* CONFIG table key name: "draft%d-%s" */
  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.Setup ){
  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] ){
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
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







-



-
+

-
+





+





-
+




-
-
+
+
+
+



-
-
+
+



-
+

-
+


-
-
+
+

-

+
+
+
-
+



+
-
-
+
+


+
-
+
-









-
+







  }

  /* 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);
  zKey = mprintf("draft%d-%s", iSkin, zFile);
  zTitle = mprintf("%s for Draft%d", aSkinAttr[ii].zTitle, iSkin);
  zBasis = PD("basis","current");
  zDflt = skin_file_content(zBasis, zFile);
  zOrig = db_get(zKey, zDflt);
  zOrig = db_get_mprintf(zDflt, "draft%d-%s",iSkin,zFile);
  zContent = PD(zFile,zOrig);
  if( P("revert")!=0 && cgi_csrf_safe(0) ){
  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="%s(g.zTop)/setup_skinedit" method="post"><div>
  @ <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(0) && strcmp(zOrig,zContent)!=0 ){
    db_set(zKey, zContent, 0);
  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" />
  @ <br>
  @ <input type="submit" name="submit" value="Apply Changes">
  if( isRevert ){
    @ &larr; Press to complete reversion to "%s(zBasis)"
  }else if( fossil_strcmp(zContent,zDflt)!=0 ){
    @ <input type="submit" name="revert" value='Revert To "%s(zBasis)"' />
    @ <input type="submit" name="revert" value='Revert To "%s(zBasis)"'>
  }
  @ <hr />
  @ <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" />
  @ <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 ){
    u64 diffFlags = construct_diff_flags(1) | DIFF_STRIP_EOLCR;
    Blob from, to, out;
    DiffConfig DCfg;
    construct_diff_flags(1, &DCfg);
    DCfg.diffFlags |= DIFF_STRIP_EOLCR;
    if( P("sbsdiff")!=0 ) diffFlags |= DIFF_SIDEBYSIDE;
    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( diffFlags & DIFF_SIDEBYSIDE ){
      text_diff(&from, &to, &out, 0, 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, 0,
      text_diff(&from, &to, &out, &DCfg);
             diffFlags | DIFF_LINENO | DIFF_HTML | DIFF_NOTTOOBIG);
      @ <pre class="udiff">
      @ %s(blob_str(&out))
      @ </pre>
    }
    blob_reset(&from);
    blob_reset(&to);
    blob_reset(&out);
  }
  @ </div></form>
  style_footer();
  style_finish_page();
  db_end_transaction(0);
}

/*
** Try to initialize draft skin iSkin to the built-in or preexisting
** skin named by zTemplate.
*/
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
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







+





+





-
+

+





-
+
+








-
+







    }
  }
  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], zNew, 0);
    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 customize a 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 *azTestPages[] = {
  static const char *const azTestPages[] = {
     "home",
     "timeline",
     "dir?ci=tip",
     "dir?ci=tip&type=tree",
     "brlist",
     "info/trunk",
  };
899
900
901
902
903
904
905
906

907
908
909
910
911
912
913
1141
1142
1143
1144
1145
1146
1147

1148
1149
1150
1151
1152
1153
1154
1155







-
+







  */
  login_check_credentials();
  if( !login_is_individual() ){
    login_needed(0);
    return;
  }
  zAllowedEditors = db_get_mprintf("", "draft%d-users", iSkin);
  if( g.perm.Setup ){
  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);
925
926
927
928
929
930
931

932



933
934
935
936
937



938
939
940
941
942
943
944
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







+

+
+
+




-
+
+
+







  }

  /* 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.<p>
  @ 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.
952
953
954
955
956
957
958

959
960
961
962
963
964
965
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214







+







      @ <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
988
989
990
991
992
993
994

995
996
997
998
999
1000
1001
1002


1003
1004
1005
1006
1007
1008
1009
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251

1252
1253
1254
1255
1256
1257
1258
1259
1260







+







-
+
+







  @ <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", "current", 0);
    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>
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
































































































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







-
+




-
+



















-
+






-
-
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
  @ <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.Setup ){
  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 scan by clicking the acknowledgements and
    @ 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.Setup ){
  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.
  }
  style_load_one_js_file("skin.js");
  style_footer();
  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) &larr; <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 &larr; <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);
}

Changes to src/smtp.c.

17
18
19
20
21
22
23
24
25


26
27
28
29
30
31
32
17
18
19
20
21
22
23


24
25
26
27
28
29
30
31
32







-
-
+
+







**
** 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)
#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>
114
115
116
117
118
119
120
121

122
123
124
125
126
127
128
114
115
116
117
118
119
120

121
122
123
124
125
126
127
128







-
+







    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); 
    pBest = fossil_strdup(pBest);
  }
  DnsRecordListFree(pDnsRecord, DnsFreeRecordListDeep);
  return pBest;
#else
  return 0;
#endif /* defined(FOSSIL_WINDOWS_STYLE_DNS) */
}
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
421
422
423
424
425
426
427

428
429
430
431
432
433
434







-







** 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;
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
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
















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































-
+
+

-
+















-

+









+









+






-
-
+
+
+
+
+
+
+














-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
  }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.
** Return a pointer to just the "domain" part, or 0 if the string
** contains no "@".
*/
static const char *domainOfAddr(const char *z){
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 = domainOfAddr(zFrom);
  zToDomain = domainOfAddr(azTo[0]);
  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);
}

/*****************************************************************************
** Server implementation
*****************************************************************************/

/*
** Schema used by the email processing system.
*/
static const char zEmailSchema[] = 
@ -- bulk storage is in this table.  This table can store either
@ -- the body of email messages or transcripts of an smtp session.
@ CREATE TABLE IF NOT EXISTS repository.emailblob(
@   emailid INTEGER PRIMARY KEY AUTOINCREMENT,  -- numeric idea for the entry
@   enref INT,                    -- Number of references to this blob
@   ets INT,                      -- Corresponding transcript, or NULL
@   etime INT,                    -- insertion time, secs since 1970
@   esz INT,                      -- uncompressed content size
@   etxt TEXT                     -- content of this entry
@ );
@
@ -- One row for each mailbox entry.  All users emails are stored in
@ -- this same table.
@ CREATE TABLE IF NOT EXISTS repository.emailbox(
@   ebid INTEGER PRIMARY KEY,  -- Unique id for each mailbox entry
@   euser TEXT,          -- User who received this email
@   edate INT,           -- Date received.  Seconds since 1970
@   efrom TEXT,          -- Who is the email from
@   emsgid INT,          -- Raw email text
@   estate INT,          -- 0: Unread, 1: read, 2: trash 3: sent
@   esubject TEXT,       -- Subject line for display
@   etags TEXT           -- zero or more tags
@ );
@
@ -- Information on how to deliver incoming email.
@ CREATE TABLE IF NOT EXISTS repository.emailroute(
@   eaddr TEXT PRIMARY KEY,  -- Email address
@   epolicy TEXT             -- How to handle email sent to this address
@ ) WITHOUT ROWID;
@
@ -- Outgoing email queue
@ CREATE TABLE IF NOT EXISTS repository.emailoutq(
@   edomain TEXT,            -- Destination domain.  (ex: "fossil-scm.org")
@   efrom TEXT,              -- Sender email address (envelope "from")
@   eto TEXT,                -- Recipient email address (envelope "to")
@   emsgid INT,              -- Message body in the emailblob table
@   ectime INT,              -- Time enqueued.  Seconds since 1970
@   emtime INT,              -- Time of last send attempt.  Sec since 1970
@   ensend INT,              -- Number of send attempts
@   ets INT                  -- Transcript of last failed attempt
@ );
@
@ -- Triggers to automatically keep the emailblob.enref field up to date
@ -- as entries in the emailblob, emailbox, and emailoutq tables are
@ -- deleted.
@ CREATE TRIGGER IF NOT EXISTS repository.emailblob_d1
@ AFTER DELETE ON emailblob BEGIN
@   UPDATE emailblob SET enref=enref-1 WHERE emailid=old.ets;
@ END;
@ CREATE TRIGGER IF NOT EXISTS repository.emailbox_d1
@ AFTER DELETE ON emailbox BEGIN
@   UPDATE emailblob SET enref=enref-1 WHERE emailid=old.emsgid;
@ END;
@ CREATE TRIGGER IF NOT EXISTS repository.emailoutq_d1
@ AFTER DELETE ON emailoutq BEGIN
@   UPDATE emailblob SET enref=enref-1 WHERE emailid IN (old.ets,old.emsgid);
@ END;
@
@ -- An index on the emailblob entries which are unreferenced.
@ CREATE INDEX IF NOT EXISTS repository.emailblob_nref ON emailblob(enref)
@ WHERE enref<=0;
;

/*
** Code used to delete the email tables.
*/
static const char zEmailDrop[] =
@ DROP TABLE IF EXISTS emailblob;
@ DROP TABLE IF EXISTS emailbox;
@ DROP TABLE IF EXISTS emailroute;
@ DROP TABLE IF EXISTS emailqueue;
;

#if INTERFACE
/*
** Mailbox message states
*/
#define MSG_UNREAD    0
#define MSG_READ      1
#define MSG_TRASH     2
#endif /* INTERFACE */


/*
** Populate the schema of a database.
**
**   eForce==0          Fast
**   eForce==1          Run CREATE TABLE statements every time
**   eForce==2          DROP then rerun CREATE TABLE
*/
void smtp_server_schema(int eForce){
  if( eForce==2 ){
    db_multi_exec(zEmailDrop/*works-like:""*/);
  }
  if( eForce==1 || !db_table_exists("repository","emailblob") ){
    db_multi_exec(zEmailSchema/*works-like:""*/);
  }
}

/*
** WEBPAGE: setup_smtp
**
** Administrative page for configuring and controlling inbound email and
** output email queuing.  This page is available to administrators
** only via the /Admin/EmailServer menu.
*/
void setup_smtp(void){
  Stmt q;
  login_check_credentials();
  if( !g.perm.Setup ){
    login_needed(0);
    return;
  }
  db_begin_transaction();
  style_header("Email Server Setup");
  if( db_table_exists("repository","emailroute") ){
    style_submenu_element("emailblob table", "%R/emailblob");
    style_submenu_element("emailoutq table", "%R/emailoutq");
    db_prepare(&q, "SELECT eaddr, epolicy FROM emailroute ORDER BY 1");
  }else{
    db_prepare(&q, "SELECT null, null WHERE false");
  }
  @ <h1>Email Routing Table</h1>
  @ <table class="emailroutetab" cellpadding="5" border="1" cellspacing="0">
  @ <thead>
  @ <tr>
  @   <th>Email Address
  @   <th>Routing
  @   <th>
  @ </tr>
  @ </thead><tbody>
  while( db_step(&q)==SQLITE_ROW ){
    const char *zEAddr = db_column_text(&q, 0);
    const char *zEPolicy = db_column_text(&q, 1);
    @ <tr>
    @  <td valign="top">%h(zEAddr)</td>
    @  <td valign="top"><span style="white-space:pre;">%h(zEPolicy)</span></td>
    @  <td valign="top"><form method="POST" action="%R/setup_smtp_route">
    @    <input type="hidden" name="oaddr" value="%h(zEAddr)">
    @    <input type="submit" value="Edit">
    @    </form>
  }
  db_finalize(&q);
  @ <tr>
  @   <td colspan="3">
  @   <form method="POST" action="%R/setup_smtp_route">
  @   <input type="submit" value="New">
  @    &larr; Add a new email address
  @   </form>
  @ </table>
  style_footer();
  db_end_transaction(0);
}

/*
** WEBPAGE: setup_smtp_route
**
** Edit a single entry in the emailroute table.
** Query parameters:
**
**    eaddr=ADDR          ADDR is the email address as edited.
**
**    oaddr=ADDR          The original email address prior to editing.
**                        Omit to add a new address.
**
**    epolicy=TXT         The routing policy.
*/
void setup_smtp_route(void){
  char *zEAddr = PT("eaddr");         /* new email address */
  char *zEPolicy = PT("epolicy");     /* new routing policy */
  char *zOAddr = PT("oaddr");         /* original email address */
  char *zErr = 0;
  int iErr = 0;
  login_check_credentials();
  if( !g.perm.Setup ){
    login_needed(0);
    return;
  }
  style_header("Email Route Editor");

  if( P("edit") && cgi_csrf_safe(1) && zEAddr!=0 && zEPolicy!=0 ){
    smtp_server_schema(0);
    if( (zOAddr==0 || fossil_strcmp(zEAddr,zOAddr)!=0) ){
      /* New or changed email address */
      if( db_exists("SELECT 1 FROM emailroute WHERE eaddr=%Q",zEAddr) ){
        iErr = 1;
        zErr = mprintf("email address \"%h(zEAddr)\" already exists",zEAddr);
        goto smtp_route_edit;
      }
      if( zEPolicy[0]==0 ){
        iErr = 2;
        zErr = mprintf("empty route");
        goto smtp_route_edit;
      }
    }
    /* If the email address has changed, or if the new policy is blank,
    ** delete the old address and route information
    */
    db_begin_transaction();
    if( (zOAddr && fossil_strcmp(zEAddr,zOAddr)!=0) || zEPolicy[0]==0 ){
      db_multi_exec("DELETE FROM emailroute WHERE eaddr=%Q", zOAddr);
    }
    if( zEPolicy[0] ){
      /* Insert the new address and route */
      db_multi_exec(
        "REPLACE INTO emailroute(eaddr,epolicy) VALUES(%Q,%Q)",
        zEAddr, zEPolicy
      );
    }
    db_end_transaction(0);
    cgi_redirectf("%R/setup_smtp");
  }
  if( P("cancel")!=0 ){
    cgi_redirectf("%R/setup_smtp");
  }

smtp_route_edit:
  if( zEAddr==0 ) zEAddr = zOAddr;
  if( zEPolicy==0 && db_table_exists("repository","emailroute") ){
    zEPolicy = db_text(0, "SELECT epolicy FROM emailroute WHERE eaddr=%Q",
                      zEAddr);
  }
  if( zEPolicy==0 ) zEPolicy = "";
  @ <form method="POST" action="%R/setup_smtp_route">
  if( zOAddr ){
    @ <input type="hidden" name="oaddr" value="%h(zOAddr)">
  }
  @ <table class="label-value">
  @ <tr>
  @   <th>Email Address:</th>
  @   <td><input type="text" size=30 name="eaddr" value="%h(zEAddr)">
  if( iErr==1 ){
    @ <td><span class="generalError">&larr; %z(zErr)</span>
  }
  @ </tr>
  if( zOAddr && fossil_strcmp(zOAddr,zEAddr)!=0 ){
    @ <tr>
    @   <th>Original Address:</th>
    @   <td>%h(zOAddr)
    @ </tr>
  }
  @ <tr>
  @   <th>Routing:</th>
  @   <td><textarea name="epolicy" rows="3" cols="40">%h(zEPolicy)</textarea>
  if( iErr==2 ){
    @ <td valign="top"><span class="generalError">&larr; %z(zErr)</span>
  }
  @ </tr>
  @ <tr>
  @   <td>&nbsp;
  @   <td><input type="submit" name="edit" value="Apply">
  @       <input type="submit" name="cancel" value="Cancel">
  @ </tr>
  @ </table>
  @ <hr>
  @ <h1>Instructions</h1>
  @ 
  @ <p>The "Routing" field consists of zero or more lines where each
  @ line is an "action" followed by an "argument".  Available actions:
  @ <ul>
  @ <li><p><b>forward</b> <i>email-address</i>
  @ <p>Forward the message to <i>email-address</i>.
  @ <li><p><b>mbox</b> <i>login-name</i>
  @ <p>Store the message in the local mailbox for the user
  @ with USER.LOGIN=<i>login-name</i>.
  @ </ul>
  @ 
  @ <p>To delete a route &rarr; erase all text from the "Routing" field then
  @ press the "Apply" button.
  style_footer();
}

#if LOCAL_INTERFACE
/*
** State information for the server
*/
struct SmtpServer {
  sqlite3_int64 idTranscript; /* Transcript ID number */
  sqlite3_int64 idMsg;        /* Message ID number */
  const char *zIpAddr;        /* Remote IP address */
  char *zEhlo;                /* Client domain on the EHLO line */
  char *zFrom;                /* MAIL FROM: argument */
  int nTo;                    /* Number of RCPT TO: lines seen */
  struct SmtpTo {
    char *z;                    /* Address in each RCPT TO line */
    int okRemote;               /* zTo can be in another domain */
  } *aTo;
  u32 srvrFlags;              /* Control flags */
  int nEts;                   /* Number of references to the transcript */
  int nRef;                   /* Number of references to idMsg */
  Blob msg;                   /* Content following DATA */
  Blob transcript;            /* Session transcript */
};

#define SMTPSRV_CLEAR_MSG    1   /* smtp_server_clear() last message only */
#define SMTPSRV_CLEAR_ALL    2   /* smtp_server_clear() everything */
#define SMTPSRV_LOG       0x001  /* Record a transcript of the interaction */
#define SMTPSRV_STDERR    0x002  /* Transcription written to stderr */
#define SMTPSRV_DRYRUN    0x004  /* Do not record anything in database */

#endif /* LOCAL_INTERFACE */

/*
** Clear the SmtpServer object.  Deallocate resources.
** How much to clear depends on eHowMuch 
*/
static void smtp_server_clear(SmtpServer *p, int eHowMuch){
  int i;
  if( eHowMuch>=SMTPSRV_CLEAR_MSG ){
    fossil_free(p->zFrom);
    p->zFrom = 0;
    for(i=0; i<p->nTo; i++) fossil_free(p->aTo[i].z);
    fossil_free(p->aTo);
    p->aTo = 0;
    p->nTo = 0;
    blob_reset(&p->msg);
    p->idMsg = 0;
  }
  if( eHowMuch>=SMTPSRV_CLEAR_ALL ){
    blob_reset(&p->transcript);
    p->idTranscript = 0;
    fossil_free(p->zEhlo);
    p->zEhlo = 0;
  }
}

/*
** Turn raw memory into an SmtpServer object.
*/
static void smtp_server_init(SmtpServer *p){
  memset(p, 0, sizeof(*p));
  blob_init(&p->msg, 0, 0);
  blob_init(&p->transcript, 0, 0);
}

/*
** Append a new TO entry to the SmtpServer object.  Do not do the
** append if the same entry is already on the list.
**
** The zAddr argument is obtained from fossil_malloc().  This
** routine assumes ownership of the allocation.
*/
static void smtp_append_to(SmtpServer *p, char *zAddr, int okRemote){
  int i;
  for(i=0; zAddr[i]; i++){ zAddr[i] = fossil_tolower(zAddr[i]); }
  for(i=0; i<p->nTo; i++){
    if( strcmp(zAddr, p->aTo[i].z)==0 ){
      fossil_free(zAddr);
      if( p->aTo[i].okRemote==0 ) p->aTo[i].okRemote = okRemote;
      return;
    }
  }
  p->aTo = fossil_realloc(p->aTo, (p->nTo+1)*sizeof(p->aTo[0]));
  p->aTo[p->nTo].z = zAddr;
  p->aTo[p->nTo].okRemote = okRemote;
  p->nTo++;
}

/*
** Send a single line of output from the server to the client.
*/
static void smtp_server_send(SmtpServer *p, const char *zFormat, ...){
  Blob b = empty_blob;
  va_list ap;
  char *z;
  int n;
  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->srvrFlags & SMTPSRV_LOG ){
    blob_appendf(&p->transcript, "S: %.*s\n", n-2, z);
  }
  if( p->srvrFlags & SMTPSRV_STDERR ){
    fprintf(stderr, "S: %.*s\n", n-2, z);
  }
  fwrite(z, n, 1, stdout);
  fflush(stdout);
  blob_reset(&b);
}

/*
** Read a single line from the client.
*/
static int smtp_server_gets(SmtpServer *p, char *aBuf, int nBuf){
  int rc = fgets(aBuf, nBuf, stdin)!=0;
  if( rc ){
    if( (p->srvrFlags & SMTPSRV_LOG)!=0 ){
      blob_appendf(&p->transcript, "C: %s", aBuf);
    }
    if( (p->srvrFlags & SMTPSRV_STDERR)!=0 ){
      fprintf(stderr, "C: %s", aBuf);
    }
  }
  return rc;
}

/*
** RFC-5321 requires certain content be prepended to an email header
** as that email is received.
*/
static void smtp_server_prepend_header_lines(SmtpServer *p){
  blob_appendf(&p->msg, "Received: from %s by Fossil-smtp\r\n", p->zIpAddr);
}

/*
** Capture the incoming email data into the p->msg blob.  Dequote
** lines of "..\r\n" into just ".\r\n".
*/
static void smtp_server_capture_data(SmtpServer *p, char *z, int n){
  int nLine = 0;
  while( fgets(z, n, stdin) ){
    if( strncmp(z, ".\r\n", 3)==0 || strncmp(z, ".\n",2)==0 ) break;
    nLine++;
    if( strncmp(z, "..\r\n", 4)==0 || strncmp(z, "..\n",3)==0 ){
      memmove(z, z+1, 4);
    }
    blob_append(&p->msg, z, -1);
  }
  if( p->srvrFlags & SMTPSRV_LOG ){
    blob_appendf(&p->transcript, "C: # %d lines, %d bytes of content\n",
          nLine, blob_size(&p->msg));
  }
  if( p->srvrFlags & SMTPSRV_STDERR ){
    fprintf(stderr, "C: # %d lines, %d bytes of content\n",
          nLine, blob_size(&p->msg));
  }
}

/*
** Send an email to a single email addess that is registered with
** this system, according to the instructions in emailroute.  If
** zAddr is not in the emailroute table, then this routine is a
** no-op.  Or if zAddr has already been processed, then this
** routine is a no-op.
*/
static void smtp_server_send_one_user(
  SmtpServer *p,         /* The current inbound email */
  const char *zAddr,     /* Who to forward this to */
  int okRemote           /* True if ok to foward to another domain */
){
  char *zPolicy;
  Blob policy, line, token, tail;

  zPolicy = db_text(0, 
    "SELECT epolicy FROM emailroute WHERE eaddr=%Q", zAddr);
  if( zPolicy==0 ){
    if( okRemote ){
      int i;
      for(i=0; zAddr[i] && zAddr[i]!='@'; i++){}
      if( zAddr[i]=='@' && zAddr[i+1]!=0 ){
        db_multi_exec(
          "INSERT INTO emailoutq(edomain,efrom,eto,emsgid,ectime,"
                                "emtime,ensend)"
          "VALUES(%Q,%Q,%Q,%lld,now(),0,0)",
          zAddr+i+1, p->zFrom, zAddr, p->idMsg
        );
        p->nRef++;
      }
    }
    return;
  }
  blob_init(&policy, zPolicy, -1);
  while( blob_line(&policy, &line) ){
    blob_trim(&line);
    blob_token(&line, &token);
    blob_tail(&line, &tail);
    if( blob_size(&tail)==0 ) continue;
    if( blob_eq_str(&token, "mbox", 4) ){
      Blob subj;
      email_header_value(&p->msg, "subject", &subj);
      db_multi_exec(
        "INSERT INTO emailbox(euser,edate,efrom,emsgid,estate,esubject)"
        " VALUES(%Q,now(),%Q,%lld,0,%Q)",
          blob_str(&tail), p->zFrom, p->idMsg,
          blob_str(&subj)
      );
      blob_reset(&subj);
      p->nRef++;
    }
    if( blob_eq_str(&token, "forward", 7) ){
      smtp_append_to(p, fossil_strdup(blob_str(&tail)), 1);
    }
    blob_reset(&tail);
  }
}

/*
** The SmtpServer object contains a complete incoming email.
** Add this email to the database.
*/
static void smtp_server_route_incoming(SmtpServer *p, int bFinish){
  Stmt s;
  int i;
  int nEtsStart = p->nEts;
  if( p->zFrom
   && p->nTo
   && blob_size(&p->msg)
   && (p->srvrFlags & SMTPSRV_DRYRUN)==0
  ){
    db_begin_write();
    if( p->idTranscript==0 ) smtp_server_schema(0);
    p->nRef = 0;
    db_prepare(&s,
      "INSERT INTO emailblob(ets,etime,etxt,enref,esz)"
      " VALUES(:ets,now(),compress(:etxt),0,:esz)"
    );
    p->nEts++;
    if( !bFinish && p->idTranscript==0 ){
      db_bind_null(&s, ":ets");
      db_bind_null(&s, ":etxt");
      db_bind_null(&s, ":esz");
      db_step(&s);
      db_reset(&s);
      p->idTranscript = db_last_insert_rowid();
    }else if( bFinish ){
      if( p->idTranscript ){
        db_multi_exec(
           "UPDATE emailblob SET etxt=compress(%Q), enref=%d, esz=%d"
           " WHERE emailid=%lld",
           blob_str(&p->transcript), p->nEts, blob_size(&p->transcript),
           p->idTranscript);
      }else{
        db_bind_null(&s, ":ets");
        db_bind_str(&s, ":etxt", &p->transcript);
        db_bind_int(&s, ":esz", blob_size(&p->transcript));
        db_step(&s);
        db_reset(&s);
        p->idTranscript = db_last_insert_rowid();
        db_multi_exec(
          "UPDATE emailblob SET enref=%d WHERE emailid=%lld",
          p->nEts, p->idTranscript);
      }
      /* smtp_server_send(p, "221-Transcript id %lld nref %d\r\n",
      **   p->idTranscript, p->nEts); */
    }
    db_bind_int64(&s, ":ets", p->idTranscript);
    db_bind_str(&s, ":etxt", &p->msg);
    db_bind_int(&s, ":esz", blob_size(&p->msg));
    db_step(&s);
    db_finalize(&s);
    p->idMsg = db_last_insert_rowid();

    /* make entries in emailbox and emailoutq */
    for(i=0; i<p->nTo; i++){
      int okRemote = p->aTo[i].okRemote;
      p->aTo[i].okRemote = 1;
      smtp_server_send_one_user(p, p->aTo[i].z, okRemote);
    }

    /* Fix up the emailblob.enref field of the email message body */
    if( p->nRef ){
      db_multi_exec(
        "UPDATE emailblob SET enref=%d WHERE emailid=%lld", 
        p->nRef, p->idMsg
      );
    }else{
      db_multi_exec(
        "DELETE FROM emailblob WHERE emailid=%lld", p->idMsg
      );
      p->nEts = nEtsStart;
    }

    /* Clean out legacy entries */
    if( bFinish ){
      db_multi_exec("DELETE FROM emailblob WHERE enref<=0");
    }

    /* Finish the transaction after all changes are implemented */
    db_commit_transaction();
  }
  smtp_server_clear(p, SMTPSRV_CLEAR_MSG);
}

/*
** Remove stale content from the emailblob table.
*/
void smtp_cleanup(void){
  if( db_table_exists("repository","emailblob") ){
    db_begin_transaction();
    db_multi_exec(
      "UPDATE emailblob SET ets=NULL WHERE enref<=0;"
      "DELETE FROM emailblob WHERE enref<=0;"
    );
    db_end_transaction(0);
  }
}

/*
** COMMAND: test-emailblob-refcheck
**
** Usage: %fossil test-emailblob-refcheck [--repair] [--full] [--clean]
**
** Verify that the emailblob.enref field is correct.  Report any errors.
** Use the --repair command to fix up the enref field.  The --full option
** gives a full report showing the enref value on all entries in the
** emailblob table.  If the --clean flags is used together with --repair,
** then emailblob table entires with enref==0 are removed.
*/
void test_refcheck_emailblob(void){
  int doRepair;
  int fullReport;
  int doClean;
  Blob sql;
  Stmt q;
  int nErr = 0;
  db_find_and_open_repository(0, 0);
  fullReport = find_option("full",0,0)!=0;
  doRepair = find_option("repair",0,0)!=0;
  doClean = find_option("clean",0,0)!=0;
  verify_all_options();
  if( !db_table_exists("repository","emailblob") ){
    fossil_print("emailblob table is not configured - nothing to check\n");
    return;
  }
  db_multi_exec(
    "CREATE TEMP TABLE refcnt(id INTEGER PRIMARY KEY, n);"
    "INSERT INTO refcnt SELECT ets, count(*) FROM ("
    "  SELECT ets FROM emailblob"
    "  UNION ALL"
    "  SELECT emsgid FROM emailbox"
    "  UNION ALL"
    "  SELECT emsgid FROM emailoutq"
    ") WHERE ets IS NOT NULL GROUP BY 1;"
    "INSERT OR IGNORE INTO refcnt(id,n) SELECT emailid, 0 FROM emailblob;"
  );
  if( doRepair ){
    db_multi_exec(
      "UPDATE emailblob SET enref=(SELECT n FROM refcnt WHERE id=emailid)"
    );
    if( doClean ){
      smtp_cleanup();
    }
  }
  blob_init(&sql, 0, 0);
  blob_append_sql(&sql, 
    "SELECT a.emailid, a.enref, b.n"
    "  FROM emailblob AS a JOIN refcnt AS b ON a.emailid=b.id"
  );
  if( !fullReport ){
    blob_append_sql(&sql, " WHERE a.enref!=b.n");
  }
  db_prepare_blob(&q, &sql);
  blob_reset(&sql);
  while( db_step(&q)==SQLITE_ROW ){
    sqlite3_int64 id = db_column_int64(&q,0);
    int n1 = db_column_int(&q, 1);
    int n2 = db_column_int(&q, 2);
    if( n1!=n2 ) nErr++;
    fossil_print("%12lld %4d %4d%s\n", id, n1, n2, n1!=n2 ? " ERROR" : "");
  }
  db_finalize(&q);
  if( nErr ){
    fossil_print("Number of incorrect emailblob.enref values: %d\n",nErr);
  }
}


/*
** COMMAND: smtpd*
**
** Usage: %fossil smtpd [OPTIONS] REPOSITORY
**
** Begin a SMTP conversation with a client using stdin/stdout.  The
** received email is stored in REPOSITORY.
**
** Options:
**
**      --dryrun          Do not record any emails in the database
**
**      --trace           Print a transcript of the conversation on stderr
**                        for debugging and analysis
**
**      --ipaddr ADDR     The SMTP connection originates at ADDR.  Or if ADDR
**                        is the name of an environment variable, the address
**                        is taken from that environment variable.
*/
void smtp_server(void){
  char *zDbName;
  const char *zDomain;
  SmtpServer x;
  char z[5000];

  smtp_server_init(&x);
  zDomain = find_option("domain",0,1);
  if( zDomain==0 ) zDomain = "";
  x.srvrFlags = SMTPSRV_LOG;
  if( find_option("trace",0,0)!=0 ) x.srvrFlags |= SMTPSRV_STDERR;
  if( find_option("dryrun",0,0)!=0 ) x.srvrFlags |= SMTPSRV_DRYRUN;
  x.zIpAddr = find_option("ipaddr",0,1);
  if( x.zIpAddr ){
    const char *zNew = fossil_getenv(x.zIpAddr);
    if( zNew && zNew[0] ) x.zIpAddr = zNew;
  }
  if( x.zIpAddr==0 ){
    x.zIpAddr = cgi_remote_ip(0);
    if( x.zIpAddr==0 ) x.zIpAddr = "?.?.?.?";
  }
  verify_all_options();
  if( g.argc!=3 ) usage("DBNAME");
  zDbName = g.argv[2];
  zDbName = enter_chroot_jail(zDbName, 0);
  db_open_repository(zDbName);
  add_content_sql_commands(g.db);
  smtp_server_send(&x, "220 %s ESMTP https://fossil-scm.org/ %s\r\n",
                   zDomain, MANIFEST_VERSION);
  while( smtp_server_gets(&x, z, sizeof(z)) ){
    if( strncmp(z, "EHLO", 4)==0  && fossil_isspace(z[4]) ){
      smtp_server_send(&x, "250 ok\r\n");
    }else
    if( strncmp(z, "HELO", 4)==0  && fossil_isspace(z[4]) ){
      smtp_server_send(&x, "250 ok\r\n");
    }else
    if( strncmp(z, "MAIL FROM:<", 11)==0 ){
      smtp_server_route_incoming(&x, 0);
      smtp_server_clear(&x, SMTPSRV_CLEAR_MSG);
      x.zFrom = email_copy_addr(z+11,'>');
      if( x.zFrom==0 ){
        smtp_server_send(&x, "500 unacceptable email address\r\n");
      }else{
        smtp_server_send(&x, "250 ok\r\n");
      }
    }else
    if( strncmp(z, "RCPT TO:<", 9)==0 ){
      char *zAddr;
      if( x.zFrom==0 ){
        smtp_server_send(&x, "500 missing MAIL FROM\r\n");
        continue;
      }
      zAddr = email_copy_addr(z+9, '>');
      if( zAddr==0 ){
        smtp_server_send(&x, "505 no such user\r\n");
        continue;
      }
      smtp_append_to(&x, zAddr, 0);
      if( x.nTo>=100 ){
        smtp_server_send(&x, "452 too many recipients\r\n");
        continue;
      }
      smtp_server_send(&x, "250 ok\r\n");
    }else
    if( strncmp(z, "DATA", 4)==0 && fossil_isspace(z[4]) ){
      if( x.zFrom==0 || x.nTo==0 ){
        smtp_server_send(&x, "500 missing RCPT TO\r\n");
        continue;
      }
      smtp_server_send(&x, "354 ready\r\n");
      smtp_server_prepend_header_lines(&x);
      smtp_server_capture_data(&x, z, sizeof(z));
      smtp_server_send(&x, "250 ok\r\n");
    }else
    if( strncmp(z, "QUIT", 4)==0 && fossil_isspace(z[4]) ){
      smtp_server_route_incoming(&x, 1);
      smtp_server_send(&x, "221 closing connection\r\n");
      break;
    }else
    {
      smtp_server_send(&x, "500 unknown command\r\n");
    }
  }
  smtp_server_clear(&x, SMTPSRV_CLEAR_ALL);
}

/*
** Zero-terminate the argument.  Return a pointer the start of the
** next argument, or to NULL if there are no more arguments.
*/
static char *pop3d_arg(char *z){
  if( z[0]==0 || fossil_isspace(z[0]) ){
    return 0;
  }
  z++;
  while( z[0] && !fossil_isspace(z[0]) ){ z++; }
  if( z[0]==0 ) return 0;
  z[0] = 0;
  z++;
  if( z[0]==0 || fossil_isspace(z[0]) ) return 0;
  return z;
}

/*
** Write formatted output back to the pop3 client, and also to the
** log file, if there is a log file.
*/
static void pop3_print(FILE *pLog, const char *zFormat, ...){
  va_list ap;
  char zLine[500];
  va_start(ap, zFormat);
  sqlite3_vsnprintf(sizeof(zLine),zLine,zFormat,ap);
  va_end(ap);
  printf("%s\r\n", zLine);
  fflush(stdout);
  if( pLog ) fprintf(pLog, "S: %s\n", zLine);
}

/*
** Try to log in for zUser and zPass.
**
** zUser can either point to a Fossil user name or to an email address
** found in the user table's info field, in angle brackets.
*/
static int pop3_login(const char *zUser, char *zPass){
  return login_search_uid(&zUser, zPass) != 0;
}

/*
** COMMAND: pop3d*
**
** Usage: %fossil pop3d [OPTIONS] REPOSITORY
**
** Begin a POP3 conversation with a client using stdin/stdout using
** the mailboxes stored in REPOSITORY.
**
** If launched as root, the process first enters a chroot jail using
** the directory of REPOSITORY as root, then drops all privileges and
** assumes the user and group of REPOSITORY before reading any content
** off of the wire.
**
**   --logdir  DIR        Each pop3d session creates a new logfile
**                        in the directory DIR and records a transcript
**                        of the session there.  The logfile is opened
**                        before entering the chroot jail.
*/
void pop3d_command(void){
  char *zDbName;
  char *zA1, *zA2, *zCmd, *z;
  int inAuth = 1;
  int i;
  FILE *pLog = 0;
  const char *zDir;
  Stmt q;
  char zIn[1000];
  char zUser[100];
  zDir = find_option("logdir",0,1);
  if( zDir ){
    char *zFile = file_time_tempname(zDir, ".txt");
    pLog = fossil_fopen(zFile, "w");
    fossil_free(zFile);
  }
  verify_all_options();
  if( g.argc!=3 ) usage("DBNAME");
  zDbName = g.argv[2];
  zDbName = enter_chroot_jail(zDbName, 0);
  db_open_repository(zDbName);
  add_content_sql_commands(g.db);
  pop3_print(pLog, "+OK POP3 server ready");
  while( fgets(zIn, sizeof(zIn), stdin) ){
    if( pLog ) fprintf(pLog, "C: %s", zIn);
    zCmd = zIn;
    zA1 = pop3d_arg(zCmd);
    zA2 = zA1 ? pop3d_arg(zA1) : 0;
    for(i=0; zCmd[i]; i++){ zCmd[i] = fossil_tolower(zCmd[i]); }
    if( strcmp(zCmd,"quit")==0 ){
      if( !inAuth ){
        db_multi_exec(
          "UPDATE emailbox SET estate=2"
          " WHERE estate<2 AND ebid IN (SELECT ebid FROM pop3 WHERE isDel);"
        );
      }
      pop3_print(pLog, "+OK");
      break;
    }
    if( strcmp(zCmd,"capa")==0 ){
      static const char *azCap[] = {
          "TOP", "USER", "UIDL",
      };
      int i;
      pop3_print(pLog, "+OK");
      for(i=0; i<sizeof(azCap)/sizeof(azCap[0]); i++){
        pop3_print(pLog, azCap[i]);
      }
      pop3_print(pLog, ".");
      continue;
    }
    if( inAuth ){
      if( strcmp(zCmd,"user")==0 ){
        if( zA1==0 || zA2!=0 ) goto cmd_error;
        sqlite3_snprintf(sizeof(zUser),zUser,"%s",zA1);
        goto cmd_ok;
      }
      if( strcmp(zCmd,"pass")==0 ){
        if( zA1==0 || zA2!=0 ) goto cmd_error;
        if( pop3_login(zUser,zA1)==0 ){
          goto cmd_error;
        }else{
          inAuth = 0;
          db_multi_exec(
            "CREATE TEMP TABLE pop3("
            "  id INTEGER PRIMARY KEY,"
            "  emailid INT,"
            "  ebid INT,"
            "  isDel INT,"
            "  esz INT"
            ");"
            "INSERT INTO pop3(id,emailid,ebid,isDel,esz)"
            "  SELECT NULL, emailid, ebid, 0, esz FROM emailblob, emailbox"
            "  WHERE emailid=emsgid AND euser=%Q AND estate<=1"
            "  ORDER BY edate;",
            zUser
          );
          goto cmd_ok;
        }   
      }
      /* Fossil cannot process APOP since the users clear-text password is
      ** unknown. */
      goto cmd_error;
    }else{
      if( strcmp(zCmd,"stat")==0 ){
        db_prepare(&q, "SELECT count(*), sum(esz) FROM pop3 WHERE NOT isDel");
        if( db_step(&q)==SQLITE_ROW ){
          pop3_print(pLog, "+OK %d %d",
                     db_column_int(&q,0), db_column_int(&q,1));
        }else{
          pop3_print(pLog,"-ERR");
        }
        db_finalize(&q);
        continue;
      }
      if( strcmp(zCmd,"list")==0 ){
        if( zA1 ){
          db_prepare(&q, "SELECT id, esz FROM pop3"
                         " WHERE id=%d AND NOT isDel", atoi(zA1));
          if( db_step(&q)==SQLITE_ROW ){
            pop3_print(pLog, "+OK %d %d",
                       db_column_int(&q,0), db_column_int(&q,1));
          }else{
            pop3_print(pLog, "-ERR");
          }
        }else{
          pop3_print(pLog, "+OK");
          db_prepare(&q, "SELECT id, esz FROM pop3 WHERE NOT isDel");
          while( db_step(&q)==SQLITE_ROW ){
            pop3_print(pLog, "%d %d",
                       db_column_int(&q,0), db_column_int(&q,1));
          }
          pop3_print(pLog, ".");
        }
        db_finalize(&q);
        continue;
      }
      if( strcmp(zCmd,"retr")==0 || strcmp(zCmd,"top")==0 ){
        Blob all, line;
        int nLine = 0;
        int iLimit;
        int hdrPending = 1;
        if( zA1==0 ) goto cmd_error;
        iLimit = zA2 ? atoi(zA2) : 2147483647;
        if( iLimit<0 ) goto cmd_error;
        z = db_text(0, "SELECT decompress(emailblob.etxt) "
                       "  FROM emailblob, pop3"
                       " WHERE emailblob.emailid=pop3.emailid"
                       "   AND pop3.id=%d AND NOT pop3.isDel",
                       atoi(zA1));
        if( z==0 ) goto cmd_error;
        pop3_print(pLog, "+OK");
        blob_init(&all, z, -1);
        while( (hdrPending || iLimit>0) && blob_line(&all, &line) ){
          char c = blob_buffer(&line)[0];
          if( c=='.' ){
            fputc('.', stdout);
          }else if( c=='\r' || c=='\n' ){
            hdrPending = 0;
          }
          fwrite(blob_buffer(&line), 1, blob_size(&line), stdout);
          nLine++;
          if( !hdrPending ) iLimit--;
        }
        if( pLog ) fprintf(pLog, "S: # %d lines of content\n", nLine);
        pop3_print(pLog, ".");
        fossil_free(z);
        blob_reset(&all);
        blob_reset(&line);
        fflush(stdout);
        continue;
      }
      if( strcmp(zCmd,"dele")==0 ){
        if( zA1==0 ) goto cmd_error;
        db_multi_exec("UPDATE pop3 SET isDel=1 WHERE id=%d",atoi(zA1));
        goto cmd_ok;
      }
      if( strcmp(zCmd,"rset")==0 ){
        db_multi_exec("UPDATE pop3 SET isDel=0");
        goto cmd_ok;
      }
      if( strcmp(zCmd,"uidl")==0 ){
        if( zA1 ){
          db_prepare(&q, "SELECT id, emailid FROM pop3"
                         " WHERE id=%d AND NOT isDel", atoi(zA1));
          if( db_step(&q)==SQLITE_ROW ){
            pop3_print(pLog, "+OK %d %d",
                       db_column_int(&q,0), db_column_int(&q,1));
          }else{
            pop3_print(pLog,"-ERR");
          }
        }else{
          pop3_print(pLog, "+OK");
          db_prepare(&q, "SELECT id, emailid FROM pop3 WHERE NOT isDel");
          while( db_step(&q)==SQLITE_ROW ){
            pop3_print(pLog, "%d %d",
                       db_column_int(&q,0), db_column_int(&q,1));
          }
          pop3_print(pLog, ".");
        }
        db_finalize(&q);
        continue;
      }
      if( strcmp(zCmd,"noop")==0 ){
        goto cmd_ok;
      }
      /* Else, fall through into cmd_error */
    }
  cmd_error:
    pop3_print(pLog, "-ERR");
    continue;
  cmd_ok:
    pop3_print(pLog, "+OK");
    continue;
  }
  if( pLog ) fclose(pLog);
}

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

Changes to src/sqlcmd.c.

19
20
21
22
23
24
25
26
27
28
29
30

31
32
33
34
35
36





37
38
39
40
41
42
43
19
20
21
22
23
24
25





26


27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42







-
-
-
-
-
+
-
-




+
+
+
+
+







** 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() */
#if defined(FOSSIL_ENABLE_MINIZ)
#  define MINIZ_HEADER_FILE_ONLY
#  include "miniz.c"
#else
#  include <zlib.h>
#include <zlib.h>
#endif

#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,
82
83
84
85
86
87
88



89
90
91
92
93
94
95
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97







+
+
+







  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);
  }
}

/*
105
106
107
108
109
110
111

112

113
114
115
116
117



118
119
120
121
122
123
124













125
126


127
128
129
130
131
132
133
134


135
136












































137
138
139
140
141
142
143
144
145
146
147

148
149
150
151
152



153
154

155
156
157
158
159
160
161
162
163
164
165
166
167














168
169
170
171
172
173
174
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144


145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259







+

+





+
+
+







+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+








+
+


+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+











+





+
+
+


+













+
+
+
+
+
+
+
+
+
+
+
+
+
+







  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(), and decompress() SQL functions to
** database connection db.
** 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.
*/
193
194
195
196
197
198
199
200

201
202
203
204
205
206
207
208
209


210
211
212

213
214
215
216
217
218
219
220

221
222
223
224
225
226
227
278
279
280
281
282
283
284

285
286
287
288
289
290

291


292
293

294

295
296
297
298
299
300
301
302

303
304
305
306
307
308
309
310







-
+





-

-
-
+
+
-

-
+







-
+







  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 SQLite.
** 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();
  size_t nByte;

  if( zSavedKey==0 || savedKeySize==0 ) return;
  nByte = savedKeySize * sizeof(char);
  if( !db_is_valid_saved_encryption_key(zSavedKey, savedKeySize) ) return;
  zKey = (char*)malloc( savedKeySize );
  zKey = sqlite3_malloc( (int)nByte );
  if( zKey ){
    memcpy(zKey, zSavedKey, nByte);
    memcpy(zKey, zSavedKey, savedKeySize);
    *pzKey = zKey;
    if( fossil_getenv("FOSSIL_USE_SEE_TEXTKEY")==0 ){
      *pnKey = (int)strlen(zKey);
    }else{
      *pnKey = -1;
    }
  }else{
    fossil_panic("failed to allocate %u bytes for key", nByte);
    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.
236
237
238
239
240
241
242
243
244
245



246
247
248

249
250
251
252
253
254
255
256


257
258
259
260
261








262

263

264




265
266


267
268
269
270



271
272







273

274

275

276
277




278




279
280


















281

282
283
284
285

286
287
288
289
290

291
292
293
294
295

296
297
298
299
300
301
302
303
304
305
306
307
319
320
321
322
323
324
325



326
327
328



329
330




331
332

333
334
335




336
337
338
339
340
341
342
343
344
345

346
347
348
349
350
351


352
353
354
355
356
357
358
359
360


361
362
363
364
365
366
367
368
369

370
371
372


373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401

402
403
404
405

406
407
408
409
410
411
412
413
414
415
416

417
418
419
420
421
422
423
424
425
426
427
428
429







-
-
-
+
+
+
-
-
-
+

-
-
-
-


-
+
+

-
-
-
-
+
+
+
+
+
+
+
+

+
-
+

+
+
+
+
-
-
+
+




+
+
+
-
-
+
+
+
+
+
+
+

+
-
+

+
-
-
+
+
+
+

+
+
+
+


+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+



-
+





+




-
+













/*
** COMMAND: sql
** COMMAND: sqlite3*
**
** Usage: %fossil sql ?OPTIONS?
**
** Run the standalone sqlite3 command-line shell on DATABASE with SHELL_OPTS.
** If DATABASE is omitted, then the repository that serves the working
** directory is opened.  See https://www.sqlite.org/cli.html for additional
** 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
** information.
**
** Options:
** the sqlite3 command-line shell.
**
**    --no-repository           Skip opening the repository database.
**
**    -R REPOSITORY             Use REPOSITORY as the repository database
**
** 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.
** running any SQL commands that modify the repository database.  Use the
** --readonly option to prevent accidental damage to the repository.
**
** The following extensions to the usual SQLite commands are provided:
**
**    content(X)                Return the content of artifact X.  X can be an
**                              artifact hash or prefix or a tag.
** 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
**    compress(X)               Compress text X.
** 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
**    decompress(X)             Decompress text X.  Undoes the work of
**                              compress(X).
**                              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.
**
**    symbolic_name_to_rid(X)   Return the BLOB.RID corresponding to symbolic
**                              name X.
**    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
**    now()                     Return the number of seconds since 1970.
**                              the result.
**
**    delta_create(X,Y)         Create and return a delta that will convert
**    REGEXP                    The REGEXP operator works, unlike in
**                              standard SQLite.
**                              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
**                                SELECT * FROM files_of_checkin('trunk');
**                              name X.
*/
void cmd_sqlite3(void){
  int noRepository;
  const char *zConfigDb;
  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 = g.zConfigDbName;
  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.

more than 10,000 changes

Changes to src/stash.c.

19
20
21
22
23
24
25













26
27
28
29
30


31
32
33
34
35
36
37
38
39
40


41
42
43

44
45
46
47


































































































48
49
50
51
52
53
54
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

43
44
45
46
47
48
49

50
51
52
53
54
55
56
57

58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167







+
+
+
+
+
+
+
+
+
+
+
+
+




-
+
+





-




+
+


-
+




+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







#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,                     -- The baseline checkout for this stash
@   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
@   rid INTEGER,                       -- Baseline content in BLOB table or 0.
@   isAdded BOOLEAN,                   -- True if this is an added file
@   isRemoved BOOLEAN,                 -- True if this file is deleted
@   isExec BOOLEAN,                    -- True if file is executable
@   isLink BOOLEAN,                    -- True if file is a symlink
@   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. Content if rid=0
@   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){
75
76
77
78
79
80
81
82
83
84




85
86
87
88
89
90
91
188
189
190
191
192
193
194



195
196
197
198
199
200
201
202
203
204
205







-
-
-
+
+
+
+







            "  OR pathname=%Q OR origname=%Q)",
      zTreename, zTreename, zTreename, zTreename
    );
  }
  db_prepare(&q, "%s", blob_sql_text(&sql));
  blob_reset(&sql);
  db_prepare(&ins,
     "INSERT INTO stashfile(stashid, rid, isAdded, isRemoved, isExec, isLink,"
                           "origname, newname, delta)"
     "VALUES(%d,:rid,:isadd,:isrm,:isexe,:islink,:orig,:new,:content)",
     "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);
140
141
142
143
144
145
146
147

148
149
150
151
152
153
154
254
255
256
257
258
259
260

261
262
263
264
265
266
267
268







-
+







**
** 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 checkout */
  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__)
169
170
171
172
173
174
175
176
177
178



179
180
181
182
183
184
185
186
187
188
189
190
191
192

193
194
195
196
197
198
199
200





201
202
203
204
205
206
207
283
284
285
286
287
288
289



290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305

306
307
308
309
310
311



312
313
314
315
316
317
318
319
320
321
322
323







-
-
-
+
+
+













-
+





-
-
-
+
+
+
+
+







    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,comment,ctime)"
    "VALUES(%d,%d,%Q,julianday('now'))",
    stashid, vid, zComment
    "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 checkout.
** Apply a stash to the current check-out.
*/
static void stash_apply(int stashid, int nConflict){
  int vid;
  Stmt q;
  db_prepare(&q,
     "SELECT rid, isRemoved, isExec, isLink, origname, newname, delta"
     "  FROM stashfile WHERE stashid=%d",
     stashid
     "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);
218
219
220
221
222
223
224


225
226
227
228
229
230
231
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349







+
+







      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);
243
244
245
246
247
248
249
250

251
252
253
254
255
256
257
361
362
363
364
365
366
367

368
369
370
371
372
373
374
375







-
+







      }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, 0);
          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++;
284
285
286
287
288
289
290
291
292
293
294
295

296
297
298

299

300
301
302
303





304
305
306
307
308
309
310
311
312
313

314
315
316
317



318
319
320

321
322
323
324



325
326
327
328
329

330
331
332
333
334
335
336
337

338
339
340


341
342
343
344
345
346
347
348

349
350
351
352
353
354



355
356
357
358
359
360
361

362

363
364
365
366
367
368
369
402
403
404
405
406
407
408


409


410
411
412
413
414
415
416
417



418
419
420
421
422
423
424
425
426
427

428
429
430
431
432
433
434


435
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







-
-

-
-
+



+

+

-
-
-
+
+
+
+
+





-




+


-
-
+
+
+
-
-
-
+
-

-
-
+
+
+
-


-
-
+
-
-





-
+

-
-
+
+




-
-

-
+
-

-
-
-
-
+
+
+






-
+

+







}

/*
** Show the diffs associate with a single stash.
*/
static void stash_diff(
  int stashid,             /* The stash entry to diff */
  const char *zDiffCmd,    /* Command used for diffing */
  const char *zBinGlob,    /* GLOB pattern to determine binary files */
  int fBaseline,           /* Diff against original baseline check-in if true */
  int fIncludeBinary,      /* Do diffs against binary files */
  u64 diffFlags            /* Other diff flags */
  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 rid, isRemoved, isExec, isLink, origname, newname, delta"
     "  FROM stashfile WHERE stashid=%d",
     stashid
     "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);
    int isBin1, isBin2;
    const char *zOrig = db_column_text(&q, 4);
    const char *zNew = db_column_text(&q, 5);
    char *zOPath = mprintf("%s%s", g.zLocalRoot, zOrig);
    Blob a, b;
    pCfg->diffFlags &= (~DIFF_FILE_MASK);
    if( rid==0 ){
      db_ephemeral_blob(&q, 6, &a);
      fossil_print("ADDED %s\n", zNew);
      diff_print_index(zNew, diffFlags);
      if( !bWebpage ) fossil_print("ADDED %s\n", zNew);
      pCfg->diffFlags |= DIFF_FILE_ADDED;
      diff_print_index(zNew, pCfg, 0);
      isBin1 = 0;
      isBin2 = fIncludeBinary ? 0 : looks_like_binary(&a);
      diff_file_mem(&empty, &a, isBin1, isBin2, zNew, zDiffCmd,
      diff_file_mem(&empty, &a, zNew, pCfg);
                    zBinGlob, fIncludeBinary, diffFlags);
    }else if( isRemoved ){
      fossil_print("DELETE %s\n", zOrig);
      diff_print_index(zNew, diffFlags);
      if( !bWebpage) fossil_print("DELETE %s\n", zOrig);
      pCfg->diffFlags |= DIFF_FILE_DELETED;
      diff_print_index(zNew, pCfg, 0);
      isBin2 = 0;
      if( fBaseline ){
        content_get(rid, &a);
        isBin1 = fIncludeBinary ? 0 : looks_like_binary(&a);
        diff_file_mem(&a, &empty, isBin1, isBin2, zOrig, zDiffCmd,
        diff_file_mem(&a, &empty, zOrig, pCfg);
                      zBinGlob, fIncludeBinary, diffFlags);
      }else{
      }
    }else{
      Blob delta;
      int isOrigLink = file_islink(zOPath);
      db_ephemeral_blob(&q, 6, &delta);
      fossil_print("CHANGED %s\n", zNew);
      if( !bWebpage ) fossil_print("CHANGED %s\n", zNew);
      if( !isOrigLink != !isLink ){
        diff_print_index(zNew, diffFlags);
        diff_print_filenames(zOrig, zNew, diffFlags);
        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);
        isBin1 = fIncludeBinary ? 0 : looks_like_binary(&a);
        isBin2 = fIncludeBinary ? 0 : looks_like_binary(&b);
        if( fBaseline ){
          diff_file_mem(&a, &b, isBin1, isBin2, zNew,
          diff_file_mem(&a, &b, zNew, pCfg);
                        zDiffCmd, zBinGlob, fIncludeBinary, diffFlags);
        }else{
          /*Diff with file on disk using fSwapDiff=1 to show the diff in the
            same direction as if fBaseline=1.*/
          diff_file(&b, isBin2, zOPath, zNew, zDiffCmd,
              zBinGlob, fIncludeBinary, diffFlags, 1);
          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(
394
395
396
397
398
399
400
401
402
403



404
405
406
407
408
409
410






411
412

413
414
415


416
417
418


419
420
421
422



423
424
425


426
427
428
429
430




431
432

433
434
435
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
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







-
-
-
+
+
+

-
-
-
-
-
-
+
+
+
+
+
+

-
+

-
-
+
+

-
-
+
+

-
-
-
+
+
+

-
-
+
+

-
-
-
-
+
+
+
+

-
+

-
-
-
+
+
+

-
+

-
-
-
+
+
+

-
-
+
+

-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-





-




-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-






-
+
+
+
+



















+
+
+


+
+

-
+


-
+


















-
-
+


















-
+







}

/*
** COMMAND: stash
**
** Usage: %fossil stash SUBCOMMAND ARGS...
**
**  fossil stash
**  fossil stash save ?-m|--comment COMMENT? ?FILES...?
**  fossil stash snapshot ?-m|--comment COMMENT? ?FILES...?
** > 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 checkout unchanged.
**      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>?
** > 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.
**      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?
** > 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.
**      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?
** > fossil stash pop
** > fossil stash apply ?STASHID?
**
**     Apply STASHID or the most recently create stash to the current
**     working checkout.  The "pop" command deletes that changeset from
**     the stash after applying it but the "apply" command retains the
**     changeset.
**      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?
** > fossil stash goto ?STASHID?
**
**     Update to the baseline checkout for STASHID then apply the
**     changes of STASHID.  Keep STASHID so that it can be reused
**     This command is undoable.
**      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?
** > 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.
**      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?
** > 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.
**      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.
**
** SUMMARY:
**  fossil stash
**  fossil stash save ?-m|--comment COMMENT? ?FILES...?
**  fossil stash snapshot ?-m|--comment COMMENT? ?FILES...?
**  fossil stash list|ls ?-v|--verbose? ?-W|--width <num>?
**  fossil stash show|cat ?STASHID? ?DIFF-OPTIONS?
**  fossil stash gshow|gcat ?STASHID? ?DIFF-OPTIONS?
**  fossil stash pop
**  fossil stash apply|goto ?STASHID?
**  fossil stash drop|rm ?STASHID? ?-a|--all?
**  fossil stash diff ?STASHID? ?DIFF-OPTIONS?
**  fossil stash gdiff ?STASHID? ?DIFF-OPTIONS?
*/
void stash_cmd(void){
  const char *zCmd;
  int nCmd;
  int stashid = 0;
  int rc;
  undo_capture_command_line();
  db_must_be_within_tree();
  db_open_config(0, 0);
  db_begin_transaction();
  db_multi_exec(zStashInit /*works-like:""*/);
  rc = db_exists("SELECT 1 FROM sqlite_master"
                 " WHERE name='stashfile'"
  stash_tables_exist_and_current();
                 "   AND sql GLOB '* PRIMARY KEY(origname, stashid)*'");
  if( rc!=0 ){
    db_multi_exec(
      "CREATE TABLE localdb.stashfile_tmp AS SELECT * FROM stashfile;"
      "DROP TABLE stashfile;"
    );
    db_multi_exec(zStashInit /*works-like:""*/);
    db_multi_exec(
      "INSERT INTO stashfile SELECT * FROM stashfile_tmp;"
      "DROP TABLE stashfile_tmp;"
    );
  }
  if( g.argc<=2 ){
    zCmd = "save";
  }else{
    zCmd = g.argv[2];
  }
  nCmd = strlen(zCmd);
  if( memcmp(zCmd, "save", nCmd)==0 ){
  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( memcmp(zCmd, "snapshot", nCmd)==0 ){
  if( strncmp(zCmd, "snapshot", nCmd)==0 ){
    stash_create();
  }else
  if( memcmp(zCmd, "list", nCmd)==0 || memcmp(zCmd, "ls", nCmd)==0 ){
  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, (SELECT uuid FROM blob WHERE rid=vid),"
       "       comment, datetime(ctime) FROM stash"
       "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, g.comFmtFlags);
        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);
578
579
580
581
582
583
584
585

586
587
588
589
590
591
592
668
669
670
671
672
673
674

675
676
677
678
679
680
681
682







-
+







        db_reset(&q2);
      }
    }
    db_finalize(&q);
    if( verboseFlag ) db_finalize(&q2);
    if( n==0 ) fossil_print("empty stash\n");
  }else
  if( memcmp(zCmd, "drop", nCmd)==0 || memcmp(zCmd, "rm", nCmd)==0 ){
  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' ){
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
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







-
-
-
+
+
+
+
+
+
-
-
-
-
-
-
+
-
-
-
+
+
+
+
+
+
+


+
+
+
+
+
+
+
+
+
+

+

-
+





-
+
+







-
-
-
-
-
-
+
+
+
+
+
+

-
-
-

-
+









-
-
-
-
+
-
-
-
-
-
-

-
+
-

-
+










    }else{
      undo_begin();
      undo_save_stash(0);
      stash_drop(stashid);
      undo_finish();
    }
  }else
  if( memcmp(zCmd, "pop", nCmd)==0 ){
    if( g.argc>3 ) usage("pop");
    stashid = stash_get_id(0);
  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);
    undo_begin();
    stash_apply(stashid, 0);
    undo_save_stash(stashid);
    undo_finish();
    stash_drop(stashid);
  }else
    }else{
  if( memcmp(zCmd, "apply", nCmd)==0 ){
    if( g.argc>4 ) usage("apply STASHID");
    stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
      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( memcmp(zCmd, "goto", nCmd)==0 ){
  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 vid FROM stash WHERE stashid=%d", stashid);
    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( memcmp(zCmd, "diff", nCmd)==0
   || memcmp(zCmd, "gdiff", nCmd)==0
   || memcmp(zCmd, "show", nCmd)==0
   || memcmp(zCmd, "gshow", nCmd)==0
   || memcmp(zCmd, "cat", nCmd)==0
   || memcmp(zCmd, "gcat", nCmd)==0
  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
  ){
    const char *zDiffCmd = 0;
    const char *zBinGlob = 0;
    int fIncludeBinary = 0;
    int fBaseline = 0;
    u64 diffFlags;
    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;
    }
    if( find_option("internal","i",0)==0 ){
      zDiffCmd = diff_command_external(zCmd[0]=='g');
    }
    diffFlags = diff_options();
    diff_options(&DCfg, zCmd[0]=='g', 0);
    if( find_option("verbose","v",0)!=0 ) diffFlags |= DIFF_VERBOSE;
    if( g.argc>4 ) usage(mprintf("%s ?STASHID? ?DIFF-OPTIONS?", zCmd));
    if( zDiffCmd ){
      zBinGlob = diff_get_binary_glob();
      fIncludeBinary = diff_include_binary_files();
    }
    stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
    stash_diff(stashid, zDiffCmd, zBinGlob, fBaseline, fIncludeBinary,
    stash_diff(stashid, fBaseline, &DCfg);
               diffFlags);
  }else
  if( memcmp(zCmd, "help", nCmd)==0 ){
  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.

57
58
59
60
61
62
63


64
65
66
67
68
69
70
71
72
73
74
75
76

77
78
79
80
81
82
83
84
85




86
87
88
89
90
91
92
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99







+
+













+









+
+
+
+







/*
** 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&nbsp;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 ){
      @ &larr; 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)
102
103
104
105
106
107
108



109


110

111
112


113
114










115
116
117
118
119
120
121
122
123
124
125
126
127


128
129
130
131
132
133
134
109
110
111
112
113
114
115
116
117
118

119
120
121
122
123

124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159







+
+
+
-
+
+

+

-
+
+


+
+
+
+
+
+
+
+
+
+













+
+







  @ </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&nbsp;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>
    @ <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 length(ssub)>1");
                   " 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 ){
147
148
149
150
151
152
153
154

155
156

157
158
159
160
161
162
163
164
165
166

167
168
169
170
171
172
173
172
173
174
175
176
177
178

179
180

181
182
183
184
185
186
187
188
189
190

191
192
193
194
195
196
197
198







-
+

-
+









-
+







  }
  @ <table class="label-value">
  fsize = file_size(g.zRepositoryName, ExtFILE);
  @ <tr><th>Repository&nbsp;Size:</th><td>%,lld(fsize) bytes</td>
  @ </td></tr>
  if( !brief ){
    @ <tr><th>Number&nbsp;Of&nbsp;Artifacts:</th><td>
    n = db_int(0, "SELECT count(*) FROM blob");
    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)
    @ %,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&nbsp;Artifact&nbsp;Size:</th><td>
      db_prepare(&q, "SELECT total(size), avg(size), max(size)"
                     " FROM blob WHERE size>0 /*scan*/");
                     " 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>
182
183
184
185
186
187
188
189

190
191
192
193
194
195
196
207
208
209
210
211
212
213

214
215
216
217
218
219
220
221







-
+







      @ %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(length(content))"
        "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;
211
212
213
214
215
216
217








218



219
220

221
222
223
224
225
226
227






















228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246








247
248
249
250
251
252
253
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250

251
252
253
254
255
256







257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312







+
+
+
+
+
+
+
+
-
+
+
+


+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



















+
+
+
+
+
+
+
+







    @ %,d(n)
    @ </td></tr>
    @ <tr><th>Number&nbsp;Of&nbsp;Wiki&nbsp;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&nbsp;Of&nbsp;Tickets:</th><td>
      @ <tr><th>Number&nbsp;Of&nbsp;Chat&nbsp;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 ){
    @ %,d(n)
    @ </td></tr>
  }
  @ <tr><th>Duration&nbsp;Of&nbsp;Project:</th><td>
  n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)"
                " + 0.99");
  @ %,d(n) days or approximately %.2f(n/365.2425) years.
      @ <tr><th>Number&nbsp;Of&nbsp;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&nbsp;Of&nbsp;Forum&nbsp;Posts:</th>
        @ <td>%,d(n) on %d(nThread) threads</td></tr>
      }
    }
  }
  @ <tr><th>Project&nbsp;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&nbsp;ID:</th>
    @     <td>%h(p) %h(db_get("project-name",""))</td></tr>
  }
  p = db_get("parent-project-code", 0);
  if( p ){
    @ <tr><th>Parent&nbsp;Project&nbsp;ID:</th>
    @      <td>%h(p) %h(db_get("parent-project-name",""))</td></tr>
  }
  /* @ <tr><th>Server&nbsp;ID:</th><td>%h(db_get("server-code",""))</td></tr> */
  @ <tr><th>Fossil&nbsp;Version:</th><td>
  @ %h(MANIFEST_DATE) %h(MANIFEST_VERSION)
  @ (%h(RELEASE_VERSION)) <a href='version?verbose'>(details)</a>
  @ </td></tr>
  @ <tr><th>SQLite&nbsp;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&nbsp;Version:</th>
    @     <td>%z(fossil_openssl_version())</td></tr>
    if( zCgi ){
      @ <tr><th>Web&nbsp;Server:</th><td>%s(zCgi)</td></tr>
    }
  }
  if( g.eHashPolicy!=HPOLICY_AUTO ){
    @ <tr><th>Schema&nbsp;Version:</th><td>%h(g.zAuxSchema),
    @ %s(hpolicy_name())</td></tr>
  }else{
    @ <tr><th>Schema&nbsp;Version:</th><td>%h(g.zAuxSchema)</td></tr>
  }
  @ <tr><th>Repository Rebuilt:</th><td>
273
274
275
276
277
278
279
280

281
282
283
284

285
286
287
288


289
290
291
292
293





294
295
296
297
298
299
300
301
302
303
304
305
306
307
308

309
310
311
312
313
314
315
316
317
318
319
320
321
322

323
324
325
326
327
328
329
332
333
334
335
336
337
338

339
340
341
342

343
344
345
346

347
348
349
350



351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384

385
386
387
388
389
390
391
392







-
+



-
+



-
+
+


-
-
-
+
+
+
+
+















+













-
+







    @ <td>Last run: %z(backoffice_last_run())</td></tr>
  }
  if( g.perm.Admin && alert_enabled() ){
    stats_for_email();
  }

  @ </table>
  style_footer();
  style_finish_page();
}

/*
** COMMAND: dbstat*
** COMMAND: dbstat
**
** Usage: %fossil dbstat OPTIONS
**
** Shows statistics and global information about the repository.
** Shows statistics and global information about the repository and/or
** verify the integrity of a repository.
**
** Options:
**
**   --brief|-b           Only show essential elements
**   --db-check           Run a PRAGMA quick_check on the repository database
**   -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");
    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;
347
348
349
350
351
352
353
354
355






356
357
358
359
360
361
362
363









364
365
366
367
368
369
370
371
372
373
374
375

376
377
378




379
380
381
382
383
384
385
410
411
412
413
414
415
416


417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
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







-
-
+
+
+
+
+
+








+
+
+
+
+
+
+
+
+












+
-
-
-
+
+
+
+







      a = t/fsize;
      fossil_print("%*s%d:%d\n", colWidth, "compression-ratio:", a, b);
    }
    n = db_int(0, "SELECT COUNT(*) FROM event e WHERE e.type='ci'");
    fossil_print("%*s%,d\n", colWidth, "check-ins:", n);
    n = db_int(0, "SELECT count(*) FROM filename /*scan*/");
    fossil_print("%*s%,d across all branches\n", colWidth, "files:", n);
    n = db_int(0, "SELECT count(*) FROM tag  /*scan*/"
                  " WHERE tagname GLOB 'wiki-*'");
    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);
    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 ){
397
398
399
400
401
402
403


404
405






























406

407

408
409
410
411
412
413
414
415
416

417
418
419
420


421
422
423

424
425
426
427
428
429
430


431
432
433
434
435




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
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







+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+

+









+



-
+
+



+





-
-
+
+





+
+
+
+









-
+
-
-
+


+
-
-
-
+
+
+
+
+
+
+
+
+






+
+
+
+


+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


-
-
+
+
+

-
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+

-
+














+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+








-
+







               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:",
                 db_text(0, "PRAGMA 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 *zRemote;
  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')");
  @ <div class="section">URLs</div>
  @ <table border="0" width='100%%'>


  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( cnt==0 ){

    @ <tr><td>(none)</td>
  }else if( nOmitted ){
  if( nOmitted ){
    @ <tr><td><a href="urllist?all"><i>Show %d(nOmitted) more...</i></a>
  }
  if( cnt ){
  @ </table>
  @ <div class="section">Checkouts</div>
  @ <table border="0" width='100%%'>
    @ </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>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>
    @ <td width='90%%'><a href='%h(zUrl)'>%h(zUrl)</a></td>
    if( zType ){
      @ <td>&nbsp;(%h(zType))&nbsp;</td>
    }else{
      @ <td>&nbsp;</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>&nbsp;&nbsp;</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==0 ){
    @ <tr><td>(none)</td>
  if( cnt ){
    @ </table>
    total += cnt;
  }
  @ </table>

  zRemote = db_text(0, "SELECT value FROM config WHERE name='last-sync-url'");
  if( zRemote ){
    @ <div class="section">Last Sync URL</div>
    if( sqlite3_strlike("http%", zRemote, 0)==0 ){
      UrlData x;
      url_parse_local(zRemote, URL_OMIT_USER, &x);
      @ <p><a href='%h(x.canonical)'>%h(zRemote)</a>
    }else{
      @ <p>%h(zRemote)</p>
    }
    @ </div>

  if( total==0 ){
    @ <p>No record of any URLs or checkouts</p>
  }
  style_footer();
  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_master WHERE sql IS NOT NULL", -1);
    "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);
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
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







+
+
+
+
+
+
+
-
+








+


+

+
+
+
+
+
+
+
+
+
+




+





+
+
+
+
-
+
+





+
+
+
+
-
-
-
+
+
+
+
+
+
+
+


+
+
+
+
+
+
+
+
+
+
-
+














+
+









-
+


-
+

-
+




















-
+


-
+

-
+
















-
+







-
-
+
+









-
-
-
+
+
+


-
+

-
-
-
-
+
+
+
+
+

-
+




-
+




-
+




-
+




-
-
+
+


-
+


-
+







      }
      @ </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_footer();
  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>
      @ <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 \
      @ INSERT INTO sqlite_stat1 VALUES('%z(zUrl)%h(zTab)</a>','%h(zIdx)','%h(zStat)');
    }
    @ </pre>
        @ 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_footer();
  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_master;"
    "   SELECT name, tbl_name FROM repository.sqlite_schema;"
    "CREATE TEMP TABLE piechart(amt REAL, label TEXT);"
    "INSERT INTO piechart(amt,label)"
    "  SELECT count(*), "
    "  SELECT sum(pageno),"
    "  coalesce((SELECT tabname FROM trans WHERE trans.name=dbstat.name),name)"
    "    FROM dbstat('repository')"
    "    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_master;"
      "   SELECT name, tbl_name FROM localdb.sqlite_schema;"
      "DELETE FROM piechart;"
      "INSERT INTO piechart(amt,label)"
      "  SELECT count(*), "
      "  SELECT sum(pageno), "
      " coalesce((SELECT tabname FROM trans WHERE trans.name=dbstat.name),name)"
      "    FROM dbstat('localdb')"
      "    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_footer();
  style_finish_page();
}

/*
** Gather statistics on artifact types, counts, and sizes.
**
** Only populate the artstat.atype field if the bWithTypes parameter is true.
*/
static void gather_artifact_stats(int bWithTypes){
  static const char zSql[] = 
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,
    @           EXISTS(SELECT 1 FROM delta WHERE delta.rid=blob.rid),
    @           size, length(content)
    @      FROM blob
    @           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[] = 
  static const char zSql2[] =
    @ UPDATE artstat SET atype='file'
    @  WHERE id IN (SELECT fid FROM mlink)
    @    AND atype IS NULL;
    @ UPDATE artstat SET atype='manifest'
    @  WHERE id IN (SELECT objid FROM event WHERE type='ci') AND atype IS NULL;
    @  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 
    @  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 
    @  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 
    @  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 
    @  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 
    @  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 
    @  WHERE atype IS NULL
    @    AND id IN (SELECT srcid FROM tagxref);
    @ UPDATE artstat SET atype='tag'
    @  WHERE atype IS NULL 
    @  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*/);
  }
749
750
751
752
753
754
755
756
757


758
759


760

761
762
763
764
765
766
767
1037
1038
1039
1040
1041
1042
1043


1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058







-
-
+
+


+
+

+








  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 ){
    login_needed(g.anon.Admin);
  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),"
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
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







-
+











-
-
-
-
+
+
+
+
+
+
+







  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_footer();
    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 = r;
    if( n==(nTotal+9)/10 ) sz10pct = r;
    if( n==(nTotal+4)/5 ) sz25pct = r;
    if( n==(nTotal+1)/2 ){ sz50pct = r; medCmpr = db_column_int(&q,0); }
    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>
830
831
832
833
834
835
836
837

838
839
840
841
842
843
844
1124
1125
1126
1127
1128
1129
1130

1131
1132
1133
1134
1135
1136
1137
1138







-
+







  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>
  @ <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.
879
880
881
882
883
884
885
886
887


888
889
890
891
892
893
894
1173
1174
1175
1176
1177
1178
1179


1180
1181
1182
1183
1184
1185
1186
1187
1188







-
-
+
+







    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='%016x(szCmpr)' align='right'>%,lld(szCmpr)</td>
    @ <td data-sortkey='%016x(szExp)' align='right'>%,lld(szExp)</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,
920
921
922
923
924
925
926
927

928
1214
1215
1216
1217
1218
1219
1220

1221
1222







-
+

      @ <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_footer();
  style_finish_page();
}

Changes to src/statrep.c.

48
49
50
51
52
53
54
55

56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75





76
77
78
79
80










81
82
83
84
85
86
87
48
49
50
51
52
53
54

55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102







-
+




















+
+
+
+
+





+
+
+
+
+
+
+
+
+
+







** no 'y' is specified), "*" is assumed (that is also the default for
** invalid/unknown filter values). That 'y' filter is the one used for
** the event list. Note that a filter of "*" or "all" is equivalent to
** querying against the full event table. The view, however, adds an
** abstraction level to simplify the implementation code for the
** various /reports pages.
**
** Returns one of: 'c', 'w', 'g', 't', 'e', representing the type of
** 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':
96
97
98
99
100
101
102
103





104
105
106
107
108

109
110
111







112
113
114
115
116
117
118
119
120
121
122
123
124
125
126




127
128


129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178




179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197




198
199
200




201
202
203
204
205
206
207
208

209
210
211
212
213
214
215

216
217
218
219




220
221




222
223
224
225
226
227
228
229
230
231
232
233
234






235
236
237
238
239
240
241
242
243
244

245
246
247
248
249
250
251
111
112
113
114
115
116
117

118
119
120
121
122
123
124
125
126
127
128



129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170





























171
172
173
174
175
176



177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196



197
198
199
200



201
202
203
204
205
206
207
208
209
210
211

212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246

247
248
249
250
251
252
253
254
255
256
257
258
259
260
261

262
263
264
265
266
267
268
269







-
+
+
+
+
+





+
-
-
-
+
+
+
+
+
+
+















+
+
+
+


+
+












-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-






-
-
-
+
+
+
+
















-
-
-
+
+
+
+
-
-
-
+
+
+
+







-
+







+




+
+
+
+


+
+
+
+












-
+
+
+
+
+
+









-
+







  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){
  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 = "a";
    db_multi_exec("CREATE TEMP VIEW v_reports AS "
                  "SELECT * FROM event WHERE %s", zTimeSpan/*safe-for-%s*/);
    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";
  }
}


/*
** Helper for stats_report_by_month_year(), which generates a list of
** week numbers. zTimeframe should be either a timeframe in the form YYYY
** or YYYY-MM.
*/
static void stats_report_output_week_links(const char *zTimeframe){
  Stmt stWeek = empty_Stmt;
  char yearPart[5] = {0,0,0,0,0};
  memcpy(yearPart, zTimeframe, 4);
  db_prepare(&stWeek,
             "SELECT DISTINCT strftime('%%W',mtime) AS wk, "
             "count(*) AS n, "
             "substr(date(mtime),1,%d) AS ym "
             "FROM v_reports "
             "WHERE ym=%Q AND mtime < current_timestamp "
             "GROUP BY wk ORDER BY wk",
             strlen(zTimeframe),
             zTimeframe);
  while( SQLITE_ROW == db_step(&stWeek) ){
    const char *zWeek = db_column_text(&stWeek,0);
    const int nCount = db_column_int(&stWeek,1);
    cgi_printf("<a href='%R/timeline?"
               "yw=%t-%t&n=%d&y=%s'>%s</a>",
               yearPart, zWeek,
               nCount, statsReportTimelineYFlag, zWeek);
  }
  db_finalize(&stWeek);
}

/*
** Implements the "byyear" and "bymonth" reports for /reports.
** If includeMonth is true then it generates the "bymonth" report,
** else the "byyear" report. If zUserName is not NULL then the report is
** restricted to events created by the named user account.
*/
static void stats_report_by_month_year(char includeMonth,
                                       char includeWeeks,
                                       const char *zUserName){
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 */
  Blob userFilter = empty_blob;      /* Optional user=johndoe query string */
  stats_report_init_view();
  if( zUserName ){

  char *zCurrentTF;                  /* The timeframe in which 'now' lives */
  double rNowFraction;               /* Fraction of 'now' timeframe that has
                                        passed */
    blob_appendf(&userFilter, "user=%s", zUserName);
  }
  blob_reset(&userFilter);
  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",
             includeMonth ? 7 : 4, zUserName);
             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) ){
    const int nCount = db_column_int(&query, 1);
    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
    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 ||
289
290
291
292
293
294
295
296
297
298



















299
300

301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331


332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358

359
360
361
362
363
364
365
307
308
309
310
311
312
313



314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333

334











335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352


353
354
355
356
357
358
359
360
361
362
363
364

365
366
367
368
369
370
371
372
373
374
375
376
377
378
379

380
381
382
383
384
385
386
387







-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+
-
-
-
-
-
-
-
-
-
-
-


















-
-
+
+










-















-
+







                 zTimeframe, (char)statsReportType);
      if( zUserName ){
        cgi_printf("&u=%t", zUserName);
      }
      cgi_printf("'>%s</a>", zTimeframe);
    }
    @ </td><td>%d(nCount)</td>
    @ <td>
    @ <div class='statistics-report-graph-line'
    @  style='width:%d(nSize)%%;'>&nbsp;</div>
    @ <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)%%;'>&nbsp;</span>\
      @ <span class='statistics-report-graph-extra' \
      @  style='display:inline-block;min-width:%d(nXSize)%%;'>&nbsp;</span>\
    }else{
      @ <div class='statistics-report-graph-line' \
      @  style='width:%d(nSize)%%;'>&nbsp;</div> \
    }
    @ </td>
    @</tr>
    @ </tr>
    if(includeWeeks){
      /* This part works fine for months but it terribly slow (4.5s on my PC),
         so it's only shown for by-year for now. Suggestions/patches for
         a better/faster layout are welcomed. */
      @ <tr class='row%d(rowClass)'>
      @ <td colspan='2' class='statistics-report-week-number-label'>Week #:</td>
      @ <td class='statistics-report-week-of-year-list'>
      stats_report_output_week_links(zTimeframe);
      @ </td></tr>
    }

    /*
      Potential improvement: calculate the min/max event counts and
      use percent-based graph bars.
    */
  }
  db_finalize(&query);
  if(includeMonth && !showYearTotal && *zPrevYear){
    /* Add final year total separator. */
    rowClass = ++nRowNumber % 2;
    @ <tr class='row%d(rowClass)'>
    @ <td></td>
    @ <td colspan='2'>Yearly total: %d(nEventsPerYear)</td>
    @</tr>
  }
  @ </tbody></table>
  if(nEventTotal){
    const char *zAvgLabel = includeMonth ? "month" : "year";
    int nAvg = iterations ? (nEventTotal/iterations) : 0;
    @ <br /><div>Total events: %d(nEventTotal)
    @ <br />Average per active %s(zAvgLabel): %d(nAvg)
    @ <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 nEventTotal = 0;               /* Total event count */
  int rowClass = 0;                  /* counter for alternating
                                        row colors */
  int nMaxEvents = 1;                /* max number of events for
                                        all rows. */
  stats_report_init_view();
  @ <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 />
    @ </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>
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
405
406
407
408
409
410
411

412
413
414
415
416
417
418







-







    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;
    nEventTotal += nCount;
    @ <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)%%;'>&nbsp;</div>
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
487
488
489
490
491
492
493

494
495
496
497

498
499
500
501
502
503



504
505
506
507
508
509
510







-




-






-
-
-







/*
** 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 nEventTotal = 0;               /* Total event count */
  int rowClass = 0;                  /* counter for alternating
                                        row colors */
  int nMaxEvents = 1;                /* max number of events for
                                        all rows. */
  Blob userFilter = empty_blob;      /* Optional user=johndoe query string */
  static const char *const daysOfWeek[] = {
  "Sunday", "Monday", "Tuesday", "Wednesday",
  "Thursday", "Friday", "Saturday"
  };

  stats_report_init_view();
  if( zUserName ){
    blob_appendf(&userFilter, "user=%s", zUserName);
  }
  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
513
514
515
516
517
518
519
520

521
522
523
524
525
526
527
529
530
531
532
533
534
535

536
537
538
539
540
541
542
543







-
+







    "  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 />
    @ </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>
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
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







-














+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


-
-
+
+










+
+
+











-
+














+
+
+
+
+
+
+
-
+


-
+




-
-
+
+

-
+
+
+
+
+
+
+









-
+














-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+





-
+







    const int nCount = db_column_int(&query, 1);
    int nSize = nCount
      ? (int)(100 * nCount / nMaxEvents)
      : 0;
    if(!nCount) continue /* arguable! Possible? */;
    else if(!nSize) nSize = 1;
    rowClass = ++nRowNumber % 2;
    nEventTotal += nCount;
    @<tr class='row%d(rowClass)'>
    @ <td>%d(dayNum)</td>
    @ <td>%s(daysOfWeek[dayNum])</td>
    @ <td>%d(nCount)</td>
    @ <td>
    @ <div class='statistics-report-graph-line'
    @  style='width:%d(nSize)%%;'>&nbsp;</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)%%;'>&nbsp;</div>
    @ </td>
    @</tr>
  }
  @ </tbody></table>
  db_finalize(&query);
}


/*
** Helper for stats_report_by_month_year(), which generates a list of
** week numbers. zTimeframe should be either a timeframe in the form YYYY
** or YYYY-MM. If zUserName is not NULL then the report is restricted to events
** 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 />");
  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();
    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'>");
             "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>"
             "<tbody>");
             "</tr></thead>\n"
             "<tbody>\n");
  while( SQLITE_ROW == db_step(&q) ){
    const int nCount = db_column_int(&q, 1);
    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
    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>");
    if(nCount){
      cgi_printf("<div class='statistics-report-graph-line'"
                 "style='width:%d%%;'>&nbsp;</div>",
    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)%%;'>&nbsp;</span>\
        @ <span class='statistics-report-graph-extra' \
        @  style='display:inline-block;min-width:%d(nXSize)%%;'>&nbsp;</span>\
      }else{
        @ <div class='statistics-report-graph-line' \
        @  style='width:%d(nSize)%%;'>&nbsp;</div> \
                 nSize);
    }
    cgi_printf("</td></tr>");
      }
    }
    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 />"
    cgi_printf("<br><div>Total events: %d<br>"
               "Average per active week: %d</div>",
               total, nAvg);
  }
}


/*
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
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







+









-
+
+
+
+
+
+
+
+
+


+
-
+
+
+
+
+
+
+







#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 values: bymonth, byyear, byuser
**   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), w (wiki), t (ticket), g (tag)
**                        * 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
744
745
746
747
748
749
750

751
752
753
754



755
756
757
758
759
760
761
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902







+




+
+
+







     {  "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();
769
770
771
772
773
774
775

776
777
778
779
780
781
782
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924







+







  }
  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 ){
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
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







-
+


-
+














+
+
+




-
+

      );
    }
  }
  style_submenu_element("Stats", "%R/stat");
  style_header("Activity Reports");
  switch( eType ){
    case RPT_BYYEAR:
      stats_report_by_month_year(0, 0, zUserName);
      stats_report_by_month_year(0, zUserName);
      break;
    case RPT_BYMONTH:
      stats_report_by_month_year(1, 0, zUserName);
      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_footer();
  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.

31
32
33
34
35
36
37
38

39
40
41
42
43
44
45
31
32
33
34
35
36
37

38
39
40
41
42
43
44
45







-
+







**      style_submenu_element()
**      style_submenu_entry()
**      style_submenu_checkbox()
**      style_submenu_binary()
**      style_submenu_multichoice()
**      style_submenu_sql()
**
** prior to calling style_footer().  The style_footer() routine
** 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];
77
78
79
80
81
82
83











84
85
86
87

88
89
90
91
92
93
94
95
96
97

98
99
100
101
102
103
104
105
106















107
108
109


110
111
112
113
114
115



116












117



118
119

120
121
122
123
124
125
126
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97

98


99
100
101
102
103
104
105

106
107
108
109
110





111
112
113
114
115
116
117
118
119
120
121
122
123
124
125



126
127
128
129
130



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146

147
148
149
150

151
152
153
154
155
156
157
158







+
+
+
+
+
+
+
+
+
+
+



-
+
-
-







-
+




-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+



-
-
-
+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+

-
+







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 */
static int needHrefJs = 0;      /* href.js */
static int needSortJs = 0;   /* sorttable.js */
static int needGraphJs = 0;  /* graph.js */

/*
** Extra JS added to the end of the file.
*/
static Blob blobOnLoad = BLOB_INITIALIZER;

/*
** Generate and return a anchor tag like this:
** 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.javascriptHyperlink
** variable.  The href="URL" form is used if g.javascriptHyperlink is false.
** If g.javascriptHyperlink is true then the
** id="ID" form is used and javascript is generated in the footer to cause
** href values to be inserted after the page has loaded.  If
** 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
** g.perm.History is false, then the <a id="ID"> form is still
** generated but the javascript is not generated so the links never
** activate.
** 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.javascriptHyperlink is set to 1.  The g.javascriptHyperlink defaults
** to 0 and only changes to one if the user lacks the Hyperlink (h) property
** and the "auto-hyperlink" setting is enabled.
** 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)
** Filling in the href="URL" using javascript is a defense against bots.
**        1             0             <a href="URL">
**        0             1             <a data-href="URL">
**        1             1             <a href="URL">
**
** The name of this routine is deliberately kept short so that can be
** 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.
135
136
137
138
139
140
141

142
143
144
145
146







147
148
149
150



151
152



153
154
155
156

157
158
159
160
161


162
163
164
165
166
167
168
169
170
171

172
173
174
175

176
177
178
179
180
181
182
183
184
185
186
187



















188
189
190
191
192
193
194
195
196

197
198
199
200
201
202

203
204
205
206
207
208
209
167
168
169
170
171
172
173
174
175
176
177


178
179
180
181
182
183
184
185
186
187
188
189
190
191


192
193
194
195
196
197
198
199
200
201
202


203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218

219
220
221
222
223
224
225
226
227
228
229


230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256

257
258
259
260
261
262
263
264
265
266
267
268
269
270
271







+



-
-
+
+
+
+
+
+
+




+
+
+
-
-
+
+
+




+



-
-
+
+










+



-
+










-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+








-
+






+







** 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.perm.Hyperlink && !g.javascriptHyperlink ){
    char *zHUrl = mprintf("<a %s href=\"%h\">", zExtra, zUrl);
  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);
    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.perm.Hyperlink && !g.javascriptHyperlink ){
    char *zHUrl = mprintf("<a %s href=\"%h\">", zExtra, zUrl);
  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.perm.Hyperlink && !g.javascriptHyperlink ){
  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 inserted
** by javascript.
** 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 && !g.javascriptHyperlink ){
  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,
305
306
307
308
309
310
311







312
313
314
315
316
317
318
319
320
321
322
323


324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344








345
346
347
348
349








350
351
352
353
354
355































356
357
358
359
360

361



362
363



364
365
366
367
368




























































































369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
































































386
387
388
389
390

391
392

393
394


395
396
397
398
399
400
401
402


403
404


405
406





















































































407
408
409
410
411











412

413
414
415
416
417

418
419
420
421









422
423
424
425
426

427

428
429
430
431








432
433
434
435
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
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390


391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411


412
413
414
415
416
417
418
419
420




421
422
423
424
425
426
427
428






429
430
431
432
433
434
435
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







+
+
+
+
+
+
+










-
-
+
+



















-
-
+
+
+
+
+
+
+
+

-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-



-
+

+
+
+


+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+




-
+


+
-
-
+
+
-
-
-



-
-
+
+
-

+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





+
+
+
+
+
+
+
+
+
+
+
-
+




-
+




+
+
+
+
+
+
+
+
+





+
-
+




+
+
+
+
+
+
+
+




















-
+






-
+

-
+







-
+







    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.
/* 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 specified config resource.
** The resulting variable name will be of the form $[zVarPrefix]_url.
** 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 url_var(
  const char *zVarPrefix,
  const char *zConfigName,
  const char *zPageName
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");

){
  char *zVarName = mprintf("%s_url", zVarPrefix);
  char *zUrl = mprintf("%R/%s?id=%x", zPageName,
                       skin_id(zConfigName));
  Th_Store(zVarName, zUrl);
  free(zUrl);
  /* 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);
  free(zVarName);
}

/*
** Create a TH1 variable containing the URL for the specified config image.
** 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 */
  char *zVarPrefix = mprintf("%s_image", zImageName);
  char *zConfigName = mprintf("%s-image", zImageName);
  url_var(zVarPrefix, zConfigName, zImageName);
  free(zVarPrefix);
  free(zConfigName);

  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 char zDfltHeader[] = 
static const char zDfltHeader[] =
@ <html>
@ <head>
@ <meta charset="UTF-8">
@ <base href="$baseurl/$current_page" />
@ <meta http-equiv="Content-Security-Policy" \
@ <base href="$baseurl/$current_page">
@ <meta http-equiv="Content-Security-Policy" content="$default_csp">
@  content="default-src 'self' data: ; \
@  script-src 'self' 'nonce-$<nonce>' ;\
@  style-src 'self' 'unsafe-inline'" />
@ <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" \
@  href="$home/timeline.rss">
@ <link rel="stylesheet" href="$stylesheet_url" type="text/css">
@  media="screen" />
@ </head>
@ <body class="$current_feature rpage-$requested_page cpage-$canonical_page">
;
@ <body>
;

/*
** 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", style_nonce());
  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", login_wants_https_redirect()? g.zHttpsURL: 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());
  url_var("stylesheet", "css", "style.css");
  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);
  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);
  if( g.thTrace ) Th_Trace("BEGIN_HEADER_SCRIPT<br>\n", -1);
  Th_Render(zHeader);
  if( g.thTrace ) Th_Trace("END_HEADER<br />\n", -1);
  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);
    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 */
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
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







-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-






-
-
+

-
-
-
-
-
+
+
+


-
-
-
+
+
+
+
+
+
+
-
-
-
+

-
-
-
-
-
-






+



+
+
+
+
+
-
+

-
-
-
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
-
+

-
+











-
+

+



+






+
+
+
+
+
+

-
+

-
+



+









+


-
+














-
+




-
+







  return 0;
}

/*
** Indicate that the table-sorting javascript is needed.
*/
void style_table_sorter(void){
  needSortJs = 1;
}

  builtin_request_js("sorttable.js");
/*
** Indicate that the table-sorting javascript is needed.
*/
void style_graph_generator(void){
  needGraphJs = 1;
}

/*
** Generate code to load a single javascript file
*/
void style_load_one_js_file(const char *zFile){
  @ <script src='%R/builtin/%s(zFile)?id=%S(MANIFEST_UUID)'></script>
}

/*
** All extra JS files to load.
*/
static const char *azJsToLoad[4];
static int nJsToLoad = 0;

/*
** Register a new JS file to load at the end of the document.
*/
void style_load_js(const char *zName){
  int i;
  for(i=0; i<nJsToLoad; i++){
    if( fossil_strcmp(zName, azJsToLoad[i])==0 ) return;
  }
  if( nJsToLoad>=sizeof(azJsToLoad)/sizeof(azJsToLoad[0]) ){
    fossil_panic("too many JS files");
  }
  azJsToLoad[nJsToLoad++] = zName;
}

/*
** Generate code to load all required javascript files.
*/
static void style_load_all_js_files(void){
  int i;
  if( needHrefJs ){
  if( needHrefJs && g.perm.Hyperlink ){
    int nDelay = db_get_int("auto-hyperlink-delay",0);
    int bMouseover;
    /* Load up the page data */
    bMouseover = (!g.isHuman || db_get_boolean("auto-hyperlink-ishuman",0))
                 && db_get_boolean("auto-hyperlink-mouseover",0);
    @ <script id='href-data' type='application/json'>\
    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())">
  if( needHrefJs ){
    cgi_append_content(builtin_text("href.js"),-1);
  @ <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 */
  }
  if( needSortJs ){
    cgi_append_content(builtin_text("sorttable.js"),-1);
    cgi_append_content(builtin_text("href.js"),-1);
  }
  if( needGraphJs ){
    cgi_append_content(builtin_text("graph.js"),-1);
  }
  for(i=0; i<nJsToLoad; i++){
    cgi_append_content(builtin_text(azJsToLoad[i]),-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.
** Extra JS to run after all content is loaded.
** Result is always terminated with null.
*/
void style_js_onload(const char *zFormat, ...){
  va_list ap;
  va_start(ap, zFormat);
void style_derive_classname(const char *zIn, char *zOut, int szOut){
  assert(  zOut );
  assert( szOut>0 );
  blob_vappendf(&blobOnLoad, zFormat, ap);
  va_end(ap);
}

  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.
**
** Draw the footer at the bottom of the page.
**   *  Appends the footer.
*/
void style_footer(void){
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( nSubmenu+nSubmenuCtrl>0 ){
  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">%h(p->zLabel)</span>
          @ <span class="label sml-%s(zClass)">%h(p->zLabel)</span>
        }else{
          @ <a class="label" href="%h(p->zLink)">%h(p->zLabel)</a>
          @ <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)'>\
          @ <span class='submenuctrl%s(zXtraClass) %s(zClass)'>\
          @ &nbsp;%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)'>
            @ <span class='%s(zXtraClass+1) %s(zClass)'>
          }
          if( aSubmenuCtrl[i].zLabel ){
            @ &nbsp;%h(aSubmenuCtrl[i].zLabel)\
          }
          @ <select class='submenuctrl' size='1' name='%s(zQPN)' \
          @ <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\
            }
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
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







-
+



















-
+









-
-
-
-
-
-
+
+
+
+
+
-
-
+
+



-
-
-
-




-
+
-




-
+

-
+



-
+










+
+



















-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-







            @  selected\
          }
          @ >%h(aSubmenuCtrl[i].zFalse)</option>
          @ </select>
          break;
        }
        case FF_CHECKBOX: {
          @ <label class='submenuctrl submenuckbox%s(zXtraClass)'>\
          @ <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>
      style_load_one_js_file("menu.js");
      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>
    }
  }else if( zAd ){
    @ <div class="adunit_banner">
    cgi_append_content(zAd, -1);
    @ </div>
  }
    @ <div class="content">
  }

  @ <div class="content"><span id="debugMsg"></span>
  cgi_destination(CGI_BODY);

  if( sideboxUsed ){
    /* Put the footer at the bottom of the page.
    ** the additional clear/both is needed to extend the content
    ** part to the end of an optional sidebox.
    */
    @ <div class="endContent"></div>
  }
  @ </div>


  /* 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);
  if( g.thTrace ) Th_Trace("BEGIN_FOOTER<br>\n", -1);
  Th_Render(zFooter);
  if( g.thTrace ) Th_Trace("END_FOOTER<br />\n", -1);
  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("<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>
}

/*
** Insert the cssDefaultList[] table, generated from default_css.txt
** using the mkcss.c program.
*/
#include "default_css.h"

/*
** Append all of the default CSS to the CGI output.
*/
void cgi_append_default_css(void) {
  int i;

  cgi_printf("%s", builtin_text("skins/default/css.txt"));
  for( i=0; cssDefaultList[i].elementClass; i++ ){
    if( cssDefaultList[i].elementClass[0] ){
      cgi_printf("%s {\n%s\n}\n\n",
                 cssDefaultList[i].elementClass,
                 cssDefaultList[i].value
                );
    }
  }
}

/*
** 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;
841
842
843
844
845
846
847





848
849
850
851
852
853
854
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205







+
+
+
+
+








/*
** 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);
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
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







-
+





+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+

-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


-
+

+
-
+


-
-
-
-
-
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
+
+
+
-
-
-
-
-
-
-
+




-
+










-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-


-
+







  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("application/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
** WEBPAGE: style.css loadavg-exempt
**
** Return the style sheet.
** 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;
  Blob css = empty_blob;
  int i;
  const char * zDefaults;
  int isInit = 0;
  const char *zSkin;

  cgi_set_content_type("text/css");
  blob_init(&css,skin_get("css"),-1);

  /* add special missing definitions */
  for(i=1; cssDefaultList[i].elementClass; i++){
    char *z = blob_str(&css);
  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);
    if( !containsSelector(z, cssDefaultList[i].elementClass) ){
      if( !isInit ){
        isInit = 1;
        blob_append(&css,
          "\n/***********************************************************\n"
  zSkin = skin_in_use();
  if( zSkin==0 ) zSkin = "this repository";
  blob_appendf(&css,
     "\n/***********************************************************\n"
          "** All CSS above is supplied by the repository \"skin\".\n"
          "** That which follows is generated automatically by Fossil\n"
          "** to fill in needed selectors that are missing from the\n"
          "** \"skin\" CSS.\n"
          "***********************************************************/\n",
          -1);
     "** Skin-specific CSS for %s\n"
     "***********************************************************/\n",
     zSkin);
      }
      blob_appendf(&css, "%s {\n%s}\n",
          cssDefaultList[i].elementClass,
          cssDefaultList[i].value);
    }
  }

  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", login_wants_https_redirect()? g.zHttpsURL: 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;
}

/*
** WEBPAGE: builtin
** URL:  builtin/FILENAME
**
** Return the built-in text given by FILENAME.  This is used internally 
** by many Fossil web pages to load built-in javascript files.
**
** 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 page_builtin_text(void){
  Blob out;
  const char *zName = P("name");
  const char *zTxt = 0;
  const char *zId = P("id");
  int nId;
  if( zName ) zTxt = builtin_text(zName);
  if( zTxt==0 ){
    cgi_set_status(404, "Not Found");
    @ File "%h(zName)" not found
    return;
  }
  if( sqlite3_strglob("*.js", zName)==0 ){
    cgi_set_content_type("application/javascript");
  }else{
    cgi_set_content_type("text/plain");
  }
  if( zId && (nId = (int)strlen(zId))>=8 && strncmp(zId,MANIFEST_UUID,nId)==0 ){
    g.isConst = 1;
  }else{
    etag_check(0,0);
  }
  blob_init(&out, zTxt, -1);
  cgi_set_content(&out);
}

/*
** All possible capabilities
*/
static const char allCap[] = 
static const char allCap[] =
  "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKL";

/*
** Compute the current login capabilities
*/
static char *find_capabilities(char *zCap){
  int i, j;
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
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







-
-
+
+
+
+
+
+
+
+
+
+
+
+
+














-
-
+



-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-





-
+
+







}

/*
** WEBPAGE: honeypot
** This page is a honeypot for spiders and bots.
*/
void honeypot_page(void){
  cgi_set_status(403, "Forbidden");
  @ <p>Please enable javascript or log in to see this content</p>
  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 i;
  int showAll;
  int showAll = 0;
  char *zErr = 0;
  int isAuth = 0;
  char zCap[100];
  static const char *const azCgiVars[] = {
    "COMSPEC", "DOCUMENT_ROOT", "GATEWAY_INTERFACE",
    "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_URI", "SCRIPT_FILENAME", "SCRIPT_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"
  };

  login_check_credentials();
  if( g.perm.Admin || g.perm.Setup  || db_get_boolean("test_env_enable",0) ){
    isAuth = 1;
  }
  for(i=0; i<count(azCgiVars); i++) (void)P(azCgiVars[i]);
  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>
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


















































































































































































































































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







-
+

-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+


-
+

-
+

-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
-
+

+
-
+
+
+
+
+

-
+





-
-
+
+


+
+



















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
    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 />
    @ 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.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 />
      @ g.nRequest = %d(g.nRequest)<br>
    }
    if( g.nPendingRequest>1 ){
      @ g.nPendingRequest = %d(g.nPendingRequest)<br />
      @ g.nPendingRequest = %d(g.nPendingRequest)<br>
    }
    @ capabilities = %s(find_capabilities(zCap))<br />
    @ capabilities = %s(find_capabilities(zCap))<br>
    if( zCap[0] ){
      @ anonymous-adds = %s(find_anon_capabilities(zCap))<br />
      @ 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);
      }
    }
    @ g.zRepositoryName = %h(g.zRepositoryName)<br />
    @ load_average() = %f(load_average())<br />
    @ cgi_csrf_safe(0) = %d(cgi_csrf_safe(0))<br />
    @ <hr />
    @ <hr>
    P("HTTP_USER_AGENT");
    P("SERVER_SOFTWARE");
    cgi_print_all(showAll, 0);
    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 />
      @ <hr>
      @ <pre>
      @ %h(blob_str(&g.httpHeader))
      @ </pre>
    }
  }
  style_footer();
  if( zErr ){
  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.

16
17
18
19
20
21
22
23
24


























































































































25
26
27






28
29








30
31

32
33
34
35
36
37
38
39
40
41
42


43
44
45
46
47



48
49
50



51

52

53
54
55
56
57
58
59



60

61
62
63
64
65
66
67
68

69
70
71
72
73
74
75
76



77
78
79
80
81
82


83
84
85
86
87
88

89
90

91
92
93
94
95

96

97
98
99
100
101
102
103
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147


148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164

165
166
167
168
169
170
171





172
173





174
175
176



177
178
179
180
181

182
183
184
185
186
187
188
189
190
191
192

193








194








195
196
197
198
199
200
201
202

203
204
205
206
207
208
209

210
211
212
213
214
215
216
217
218
219

220
221
222
223
224
225
226
227









+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
+
+
+
+
+
+


+
+
+
+
+
+
+
+

-
+






-
-
-
-
-
+
+
-
-
-
-
-
+
+
+
-
-
-
+
+
+

+
-
+







+
+
+
-
+
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
+
+
+





-
+
+





-
+


+





+
-
+







*******************************************************************************
**
** 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.  This will be a pull if the argument is true or a push
** if the argument is false.
** 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.
*/
int autosync(int flags){
static int autosync(int flags, const char *zSubsys){
  const char *zAutosync;
  int rc;
  int configSync = 0;       /* configuration changes transferred */
  if( g.fNoSync ){
    return 0;
  }
  if( flags==SYNC_PUSH && db_get_boolean("dont-push",0) ){
    return 0;
  }
  zAutosync = db_get("autosync", 0);
  if( zAutosync ){
  zAutosync = db_get_for_subsystem("autosync", zSubsys);
  if( zAutosync==0 ) zAutosync = "on";  /* defend against misconfig */
    if( (flags & SYNC_PUSH)!=0 && fossil_strncmp(zAutosync,"pull",4)==0 ){
      return 0;   /* Do not auto-push when autosync=pullonly */
    }
    if( is_false(zAutosync) ){
      return 0;   /* Autosync is completely off */
  if( is_false(zAutosync) ) return 0;
  if( db_get_boolean("dont-push",0)
   || sqlite3_strglob("*pull*", zAutosync)==0
    }
  }else{
    /* Autosync defaults on.  To make it default off, "return" here. */
  ){
    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_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();
    url_remember();
#if 0 /* Disabled for now */
  if( (flags & AUTOSYNC_PULL)!=0 && db_get_boolean("auto-shun",1) ){
    /* When doing an automatic pull, also automatically pull shuns from
    ** the server if pull_shuns is enabled.
    **
    ** TODO:  What happens if the shun list gets really big?
    ** Maybe the shunning list should only be pulled on every 10th
    ** autosync, or something?
    sync_explain(flags);
    */
    configSync = CONFIGSET_SHUN;
  }
#endif
  if( find_option("verbose","v",0)!=0 ) flags |= SYNC_VERBOSE;
  fossil_print("Autosync:  %s\n", g.url.canonical);
  url_enable_proxy("via proxy: ");
  rc = client_sync(flags, configSync, 0);
    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.
** 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 nTries, int doPrompt){
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)) ){
  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.");
      }
119
120
121
122
123
124
125
126


127
128
129
130
131
132
133
134
135
136
137
138
139

140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155


156




































157
158
159
160

161
162

163
164




165
166
167
168
169
170

171
172
173
174

175
176
177
178
179
180
181
182
183


184
185
186
187




188
189
190
191
192
193

194
195
196
197
198
199
200
201
202




203
204
205
206

207
208
209
210

211
212
213
214

215
216
217


218
219
220


221


222
223
224
225

226
227
228
229


230
231
232

233

234
235
236
237
238

239
240
241
242
243
244
245
246
247
248
249





250
251
252

253
254
255
256

257
258
259

260
261
262
263
264
265


266


267
268
269
270

271
272
273
274
275

276
277
278
279
280
281
282
283

284
285
286
287
288
289
290
291
292
293
294

295
296
297

298
299
300
301

302
303
304

305
306
307
308
309
310


311
312


313
314
315
316

317
318
319
320
321
322
323
324

325
326
327
328
329
330
331
332
333

334
335
336
337
338
339
340
341
342
343

344
345

346
347
348

349

350
351

352



353
354




355



356
357
358
359






































360







361

362



363














364
365
366



367

368
369
370
371





















372
373

























374





















































































375










































































































376
377
378
379
380

381
382


383
384
385
386









































387

388
389
390
391






243
244
245
246
247
248
249

250
251
252
253
254
255
256
257
258
259
260
261
262
263

264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282

283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321

322
323

324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340

341
342

343
344
345
346
347


348
349




350
351
352
353

354
355


356
357
358
359
360
361
362
363



364
365
366
367
368
369
370

371
372
373
374

375
376
377
378
379
380
381

382
383
384
385
386
387
388
389

390
391
392
393
394

395
396
397
398
399
400
401
402
403
404
405

406
407
408
409
410

411
412
413
414
415
416
417
418




419
420
421
422
423
424
425

426
427
428
429

430
431
432
433
434
435
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







-
+
+












-
+
















+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



-
+

-
+


+
+
+
+






+



-
+

-





-
-
+
+
-
-
-
-
+
+
+
+
-


-
-

+






-
-
-
+
+
+
+



-
+



-
+




+

-

+
+



+
+
-
+
+



-
+




+
+



+
-
+




-
+







-
-
-
-
+
+
+
+
+


-
+



-
+



+






+
+
-
+
+



-
+




-
+







-
+










-
+


-
+



-
+



+






+
+

-
+
+



-
+







-
+





-



+









-
+

-
+



+
-
+

-
+

+
+
+
-
-
+
+
+
+

+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
-
+

+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+


-
+
+
+

+




+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


-
-
-
+
-
-
+
+
-
-
-

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
-
-
-
+
+
+
+
+
+
** 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 */
  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_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, 0);
  db_open_config(0, 1);
  if( g.argc==2 ){
    if( db_get_boolean("auto-shun",1) ) configSync = CONFIGSET_SHUN;
    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_parse(zUrl, urlFlags|URL_USE_CONFIG);
  remember_or_get_http_auth(zHttpAuth, urlFlags & URL_REMEMBER, zUrl);
  url_remember();
  if( g.url.protocol==0 ){
    if( urlOptional ) fossil_exit(0);
    usage("URL");
  }
  user_select();
  if( g.argc==2 ){
    if( ((*pSyncFlags) & (SYNC_PUSH|SYNC_PULL))==(SYNC_PUSH|SYNC_PULL) ){
  url_enable_proxy("via proxy: ");
  *pConfigFlags |= configSync;
      fossil_print("Sync with %s\n", g.url.canonical);
    }else if( (*pSyncFlags) & SYNC_PUSH ){
      fossil_print("Push to %s\n", g.url.canonical);
    }else if( (*pSyncFlags) & SYNC_PULL ){
  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;
      fossil_print("Pull from %s\n", g.url.canonical);
    }
  }
  url_enable_proxy("via proxy: ");
  *pConfigFlags |= configSync;
}


/*
** 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, and wiki, ticket, and tech-note
** edits.  Add the --private option to pull private branches.  Use the
** 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-url, or sync command is used.  See "fossil help clone" for
** 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
**   --proxy PROXY              Use the specified HTTP proxy
**   --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
**   -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 pull, push, remote-url, sync
** 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);
  process_sync_args(&configFlags, &syncFlags, 0, urlOmitFlags);

  /* We should be done with options.. */
  verify_all_options();

  client_sync(syncFlags, configFlags, 0);
  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, and wiki, ticket, and tech-note
** edits.  Use --private to also push private branches.  Use the
** "configuration push" command to push website configuration details.
** 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-url, or sync command is used.  See "fossil help clone" for
** 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
**   -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 push, pull, remote-url, sync
** See also: [[clone]], [[config]], [[pull]], [[remote]], [[sync]]
*/
void push_cmd(void){
  unsigned configFlags = 0;
  unsigned syncFlags = SYNC_PUSH;
  process_sync_args(&configFlags, &syncFlags, 0);
  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(syncFlags, 0, 0);
  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, and technical notes.
** 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-url, or sync command is used.  See "fossil help clone" for
** 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
**   -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-url
** 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);
  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;
  client_sync(syncFlags, configFlags, 0);
  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);
  process_sync_args(&configFlags, &syncFlags, 1, 0);
  verify_all_options();
  client_sync(syncFlags, 0, 0);
  client_sync(syncFlags, 0, 0, 0, 0);
}

/*
** COMMAND: remote
** COMMAND: remote-url
** COMMAND: remote-url*
**
** Usage: %fossil remote-url ?URL|off?
** 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
** Query and/or change the default server URL used by the "pull", "push",
** and "sync" commands.
** 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".
**
** The remote-url is set automatically by a "clone" command or by any
** "sync", "push", or "pull" command that specifies an explicit URL.
** The default remote-url is used by auto-syncing and by "sync", "push",
** "pull" that omit the server URL.
** > 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
**
** See "fossil help clone" for further information about URL formats
** > 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.
** See also: clone, push, pull, sync
**
** > 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;
  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 && g.argc!=3 ){
    usage("?URL|off?");
  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 ){
    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);
    db_unset("http-auth", 0);
    if( is_false(g.argv[2]) ) return;
    url_parse(g.argv[2], URL_REMEMBER|URL_PROMPT_PW|URL_ASK_REMEMBER_PW);
    url_parse(g.argv[2], URL_REMEMBER|URL_PROMPT_PW|
  }
  url_remember();
                         URL_USE_CONFIG|URL_ASK_REMEMBER_PW);
    url_remember();
  zUrl = db_get("last-sync-url", 0);
  if( zUrl==0 ){
    fossil_print("off\n");
    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{
    }else{
    url_parse(zUrl, 0);
    fossil_print("%s\n", g.url.canonical);
  }
}
      fossil_fatal("backup \"%s\" already exists", zDest);
    }
  }
  db_unprotect(PROTECT_ALL);
  db_multi_exec("VACUUM repository INTO %Q", zDest);
}

Changes to src/tag.c.

42
43
44
45
46
47
48
49

50
51
52
53
54
55
56
42
43
44
45
46
47
48

49
50
51
52
53
54
55
56







-
+







  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, 0);
  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,"
77
78
79
80
81
82
83
84

85
86
87
88
89
90
91

92
93
94
95
96
97
98
77
78
79
80
81
82
83

84
85
86
87
88
89
90

91
92
93
94
95
96
97
98







-
+






-
+







    );
  }
  if( tagid==TAG_BGCOLOR ){
    db_prepare(&eventupdate,
      "UPDATE event SET bgcolor=%Q WHERE objid=:rid", zValue
    );
  }
  while( (pid = pqueuex_extract(&queue, 0))!=0 ){
  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, 0);
        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);
218
219
220
221
222
223
224
225

226
227
228
229
230
231
232
218
219
220
221
222
223
224

225
226
227
228
229
230
231
232







-
+







    }
  }
  if( zCol ){
    db_multi_exec("UPDATE event SET \"%w\"=%Q WHERE objid=%d",
                  zCol, zValue, rid);
    if( tagid==TAG_COMMENT ){
      char *zCopy = mprintf("%s", zValue);
      wiki_extract_links(zCopy, rid, 0, mtime, 1, WIKI_INLINE);
      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)"
280
281
282
283
284
285
286
287

288
289
290
291
292
293
294
280
281
282
283
284
285
286

287
288
289
290
291
292
293
294







-
+







  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 --dryrun options and debugging.
** 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
357
358
359
360
361
362
363

364























365
366
367
368
369
370
371
372
373
374

375
376
377
378
379






380
381


382
383
384



385
386
387


388
389
390
391
392




393

394
395
396
397





398








399

400
401

402
403
404
405

406
407
408








409
410

411
412
413



















414
415
416
417
418
419
420
357
358
359
360
361
362
363
364

365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396

397
398




399
400
401
402
403
404
405
406
407
408



409
410
411



412
413
414
415
416
417
418
419
420
421
422

423
424



425
426
427
428
429
430
431
432
433
434
435
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







+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+









-
+

-
-
-
-
+
+
+
+
+
+


+
+
-
-
-
+
+
+
-
-
-
+
+





+
+
+
+
-
+

-
-
-
+
+
+
+
+

+
+
+
+
+
+
+
+
-
+

-
+
-
-


+
-
-
-
+
+
+
+
+
+
+
+

-
+

-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







    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);
    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 CHECK-IN ?VALUE?
** > fossil tag add ?OPTIONS? TAGNAME ARTIFACT-ID ?VALUE?
**
**         Add a new tag or property to CHECK-IN. The tag will
**         be usable instead of a CHECK-IN in commands such as
**         update and merge.  If the --propagate flag is present,
**         the tag value propagates to all descendants of CHECK-IN
**         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
**           --raw                       Raw tag name.
**           --propagate                 Propagating tag.
**           --date-override DATETIME    Set date and time added.
**                                      actually insert it into the database
**           --propagate                Propagating tag
**           --raw                      Raw tag name. Ignored for
**           --user-override USER        Name USER when adding the tag.
**           --dryrun|-n                 Display the tag text, but do not
**                                       actually insert it into the database.
**                                      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 CHECK-IN
** > fossil tag cancel ?--raw? TAGNAME ARTIFACT-ID
**
**         Remove the tag TAGNAME from CHECK-IN, and also remove
**         the propagation of the tag to any descendants.  Use the
**         the --dryrun or -n options to see what would have happened.
**         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
** > fossil tag find ?OPTIONS? TAGNAME
**
**         List all objects that use TAGNAME.  TYPE can be "ci" for
**         List all objects that use TAGNAME.
**         check-ins or "e" for events. The limit option limits the number
**         of results to the given value.
**
**         Options:
**           -n|--limit N    Limit to N results
**           --raw           Raw tag name.
**           -t|--type TYPE  One of "ci", or "e".
**           -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 ?--raw? ?CHECK-IN?
** > fossil tag list|ls ?OPTIONS? ?ARTIFACT-ID?
**
**         List all tags, or if CHECK-IN is supplied, list
**         all tags and their values for CHECK-IN.
**         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.
**
430
431
432
433
434
435
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
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







-
-
-
-
-













+
+
+


+
+
+
-
+

-
+

+
+
+

+
+
+
+
+
+
+
+
+
+

-
+











+
+
+
+
+
+
+
-
+

-
+

+
+
+
+
+
+
+
+
+
+
+
+
+

-
+
+





+
+
+







**   fossil update tag:decaf
**
** will assume that "decaf" is a tag/branch name.
**
*/
void tag_cmd(void){
  int n;
  int fRaw = find_option("raw","",0)!=0;
  int fPropagate = find_option("propagate","",0)!=0;
  const char *zPrefix = fRaw ? "" : "sym-";
  const char *zFindLimit = find_option("limit","n",1);
  const int nFindLimit = zFindLimit ? atoi(zFindLimit) : -2000;

  db_find_and_open_repository(0, 0);
  if( g.argc<3 ){
    goto tag_cmd_usage;
  }
  n = strlen(g.argv[2]);
  if( n==0 ){
    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("dryrun","n",0)!=0 ) dryRun = TAG_ADD_DRYRUN;
    if( find_option("dry-run","n",0)!=0 ) dryRun = TAG_ADD_DRYRUN;
    if( g.argc!=5 && g.argc!=6 ){
      usage("add ?options? TAGNAME CHECK-IN ?VALUE?");
      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, g.argv[3], g.argv[4], zValue,
    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("dryrun","n",0)!=0 ) dryRun = TAG_ADD_DRYRUN;
    if( find_option("dry-run","n",0)!=0 ) dryRun = TAG_ADD_DRYRUN;
    if( g.argc!=5 ){
      usage("cancel ?options? TAGNAME CHECK-IN");
      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, g.argv[3], g.argv[4], 0, dryRun, 0, 0);
    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 ){
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
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







-
+
+
+
+














-
+







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+




-
-
+
+
+
+
+





+
+
+






+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



-
-
+
+

+
-
+




-
-
-
+
+









-
+







      db_prepare(&q, "%s", blob_sql_text(&sql));
      blob_reset(&sql);
      while( db_step(&q)==SQLITE_ROW ){
        fossil_print("%s\n", db_column_text(&q, 0));
      }
      db_finalize(&q);
    }else{
      int tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname='sym-%q'",
      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);
        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>0)"
        " ORDER BY tagname"
        "                 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];
      int rid = name_to_rid(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>%d"
        " ORDER BY tagname",
        "   AND tagtype%s%d"
        " ORDER BY tagname COLLATE uintnocase",
        rid,
        zTagType!=0 ? (fInverse!=0?"<>":"=") : ">"/*safe-for-%s*/,
        fRaw ? -1 : 0
        nTagType
      );
      while( db_step(&q)==SQLITE_ROW ){
        const char *zName = db_column_text(&q, 0);
        const char *zValue = db_column_text(&q, 1);
        if( fRaw==0 ){
          if( strncmp(zName, "sym-", 4)!=0 ) continue;
          zName += 4;
        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 ?CHECK-IN?");
      usage("list ?OPTIONS? ?CHECK-IN?");
    }
  }else
  {
    goto tag_cmd_usage;
  }

  /* Cleanup */
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













































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







-
+

+
+








+
+

-
+
+
+


















-
+
+
















+












-
+





-
+







-
+







+
+
+
+
+
+
+
+


+

+
+
+







+

+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+

-
-
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
**
** 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.
**    --dryrun | -n    Print the tag that would have been created but do not
**    -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("dryrun","n",0)!=0 ) dryRun = TAG_ADD_DRYRUN;
  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,0,0);
    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"
    " 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&n=200",zName))
      @ <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_footer();
  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);
  db_prepare(&q,
    "%s AND blob.rid IN (SELECT rid FROM tagxref"
    "                     WHERE tagtype=1 AND srcid>0"
    "                       AND tagid IN (SELECT tagid FROM tag "
    "                                      WHERE tagname GLOB 'sym-*'))"
    " ORDER BY event.mtime DESC /*sort*/",
  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));
    timeline_query_for_www()
  );
  www_print_timeline(&q, 0, 0, 0, 0, 0);
  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_footer();
  @ <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;
}

Changes to src/tar.c.

15
16
17
18
19
20
21
22
23
24
25
26

27
28
29
30
31
32
33
34
15
16
17
18
19
20
21





22

23
24
25
26
27
28
29







-
-
-
-
-
+
-







**
*******************************************************************************
**
** This file contains code used to generate tarballs.
*/
#include "config.h"
#include <assert.h>
#if defined(FOSSIL_ENABLE_MINIZ)
#  define MINIZ_HEADER_FILE_ONLY
#  include "miniz.c"
#else
#  include <zlib.h>
#include <zlib.h>
#endif
#include "tar.h"

/*
** State information for the tarball builder.
*/
static struct tarball_t {
  unsigned char *aHdr;      /* Space for building headers */
247
248
249
250
251
252
253
254


255
256

257
258
259
260
261
262
263
242
243
244
245
246
247
248

249
250
251

252
253
254
255
256
257
258
259







-
+
+

-
+







    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);
  blob_appendf(&tball.pax, "%d %s=%*.*s\n",
               blen, zField, nValue, nValue, zValue);
  /* this _must_ be right */
  if(blob_size(&tball.pax) != blen){
  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
423
424
425
426
427
428
429
430

431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448

449
450
451
452
453
454
455
419
420
421
422
423
424
425

426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443

444
445
446
447
448
449
450
451







-
+

















-
+








/*
** 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; archive the files they point to.
**   -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,0), file_mtime(0,0));
    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]);
}

/*
464
465
466
467
468
469
470
471

472
473
474
475

476
477
478
479


480
481
482
483
484
485
486
460
461
462
463
464
465
466

467
468
469
470

471
472
473
474

475
476
477
478
479
480
481
482
483







-
+



-
+



-
+
+







** 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 a UUID or "ProjectName".
** with source files. For example, pass an artifact hash or "ProjectName".
**
*/
void tarball_of_checkin(
  int rid,             /* The RID of the checkin from which to form a tarball */
  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 */
  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;
498
499
500
501
502
503
504
505
506


507
508
509
510
511
512
513
495
496
497
498
499
500
501


502
503
504
505
506
507
508
509
510







-
-
+
+







    blob_appendf(&filename, "%s/", zDir);
  }
  nPrefix = blob_size(&filename);

  pManifest = manifest_get(rid, CFTYPE_MANIFEST, 0);
  if( pManifest ){
    int flg, eflg = 0;
    mTime = (pManifest->rDate - 2440587.5)*86400.0;
    tar_begin(mTime);
    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;
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
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







-
-
-
-
+
+
+
+




-



+
+
+
-
-
+
+
+

-
-
-



+
+
+
+
+
-
-
+
+
+









-



+
+
+
-
-
+
+
+





+
+
-
-
-
+
+
+
+





-
+


















+
+
+
+
+
+



+











+
+






+













+
+
+
+












-
+
+


+
+
-
-
+
+
+





-
+







        eflg |= MFESTFLG_TAGS;
      }

      if( eflg & (MFESTFLG_RAW|MFESTFLG_UUID) ){
        if( eflg & MFESTFLG_RAW ){
          blob_append(&filename, "manifest", -1);
          zName = blob_str(&filename);
        }
        if( eflg & MFESTFLG_RAW ) {
          sterilize_manifest(&mfile);
          tar_add_file(zName, &mfile, 0, mTime);
          if( listFlag ) fossil_print("%s\n", zName);
          if( pTar ){
            tar_add_file(zName, &mfile, 0, mTime);
          }
        }
      }
      blob_reset(&mfile);
      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( pTar ){
          blob_append(&hash, "\n", 1);
        tar_add_file(zName, &hash, 0, mTime);
      }
          tar_add_file(zName, &hash, 0, mTime);
        }
      }
      if( eflg & MFESTFLG_TAGS ){
        Blob tagslist;
        blob_zero(&tagslist);
        get_checkin_taglist(rid, &tagslist);
        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);
          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 ){
        content_get(fid, &file);
        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);
          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);
      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);
  tar_finish(pTar);
  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, &tarball, zName, pInclude, pExclude);
  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, g.argv[3]);
  blob_reset(&tarball);
    blob_write_to_file(&tarball, zOut);
    blob_reset(&tarball);
  }
}

/*
** Check to see if the input string is of the form:
**
**        checkin-name/filename.ext
**        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.
**
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
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







-
+

-
-
-
+
+
+

-
-
+
+








-
-
-
-
+
+
+
+
+
+
+

-
-
-
+
+
+
-
-
-
-
-
+
+
+
+
+







  zName[n] = 0;
  *pzName = fossil_strdup(&zName[n+1]);
  return zName;
}

/*
** WEBPAGE: tarball
** URL: /tarball
** URL: /tarball/[VERSION/]NAME.tar.gz
**
** Generate a compressed tarball for the check-in specified by the "r"
** query parameter.  Return that compressed tarball as the HTTP reply
** content.
** 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 r= and name= query parameters can be specified as extensions to the
** URI.  Example, the following URIs are all equivalent:
** 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=NAME[.tar.gz]  The base name of the output file.  The default
**                       value is a configuration parameter in the project
**                       settings.  A prefix of the name, omitting the
**                       extension, is used as the top-most directory name.
**   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               The check-in that is turned into a compressed tarball.
**                       Defaults to "trunk".  This query parameter used to
**                       be called "uuid" and "uuid" is still accepted for
**   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
**                       backwards compatibility.  If the name= query parameter
**                       contains one "/" character then the part before the /
**                       is the TAG and the part after the / is the true name.
**                       If no TAG is specified by any of the above means, then
**                       "trunk" is used as the default.
**                       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 '..'.
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
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







-
+











+
+
+







  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; }
  load_control();
  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
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
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







-
-
+
+

-
+


-
+


-
+








-
+

-
+


+


-
+






+




  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 />
    @ zName = "%h(zName)"<br>
    @ rid = %d(rid)<br>
    if( zInclude ){
      @ zInclude = "%h(zInclude)"<br />
      @ zInclude = "%h(zInclude)"<br>
    }
    if( zExclude ){
      @ zExclude = "%h(zExclude)"<br />
      @ zExclude = "%h(zExclude)"<br>
    }
    @ zKey = "%h(zKey)"
    style_footer();
    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" />
    @ <input type="submit" value="Download">
    @ </form>
    style_footer();
    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);
    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.

197
198
199
200
201
202
203
204
205
206








207
208
209
210
211
212





















213

214
215
216
217
218

219
220
221
222
223
224
225







226
227
228
229

230
231
232
233
234
235
236
237
238
239
240
241















242
243
244
245
246
247
248
249
250
251
197
198
199
200
201
202
203

204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240

241
242
243
244
245
246
247







248
249
250
251
252
253
254




255












256
257
258
259
260
261
262
263
264
265
266
267
268
269
270

271

272
273
274
275
276
277
278







-


+
+
+
+
+
+
+
+






+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+





+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-

-







*/
struct Buffer {
  char *zBuf;
  int nBuf;
  int nBufAlloc;
};
typedef struct Buffer Buffer;
static int  thBufferWrite(Th_Interp *interp, Buffer *, const char *, int);
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 int thBufferWrite(
static void thBufferWriteFast(
  Th_Interp *interp,
  Buffer *pBuffer,
  const char *zAdd,
  int nAdd
){
  if( pBuffer->nBuf+nAdd > pBuffer->nBufAlloc ){
  int nReq;

  if( nAdd<0 ){
    nAdd = th_strlen(zAdd);
  }
  nReq = pBuffer->nBuf+nAdd+1;

    thBufferWriteResize(interp, pBuffer, zAdd, nAdd);
  }else{
    if( pBuffer->zBuf ){
      memcpy(pBuffer->zBuf + pBuffer->nBuf, zAdd, nAdd);
    }
    pBuffer->nBuf += nAdd;
  }
  if( nReq>pBuffer->nBufAlloc ){
    char *zNew;
    int nNew;

}
    nNew = nReq*2;
    zNew = (char *)Th_Malloc(interp, nNew);
    memcpy(zNew, pBuffer->zBuf, pBuffer->nBuf);
    Th_Free(interp, pBuffer->zBuf);
    pBuffer->nBufAlloc = nNew;
    pBuffer->zBuf = zNew;
  }

  memcpy(&pBuffer->zBuf[pBuffer->nBuf], zAdd, nAdd);
  pBuffer->nBuf += nAdd;
  pBuffer->zBuf[pBuffer->nBuf] = '\0';

#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;
  }
  return TH_OK;
}
#define thBufferWrite(a,b,c,d) thBufferWrite(a,b,(const char *)c,d)

/*
** Initialize the Buffer structure pointed to by pBuffer.
*/
static void thBufferInit(Buffer *pBuffer){
  memset(pBuffer, 0, sizeof(Buffer));
}
617
618
619
620
621
622
623
624

625
626
627
628
629
630
631
644
645
646
647
648
649
650

651
652
653
654
655
656
657
658







-
+







      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);
      thBufferWrite(interp, &varname, ")", 1);
      thBufferAddChar(interp, &varname, ')');
      rc = Th_GetVar(interp, varname.zBuf, varname.nBuf);
      thBufferFree(interp, &varname);
      return rc;
    }
  }
  return Th_GetVar(interp, &zWord[1], nWord-1);
}
679
680
681
682
683
684
685
686

687
688
689
690
691
692
693
706
707
708
709
710
711
712

713
714
715
716
717
718
719
720







-
+







  int rc = TH_OK;
  Buffer output;
  int i;

  thBufferInit(&output);

  if( nWord>1 && (zWord[0]=='{' && zWord[nWord-1]=='}') ){
    rc = thBufferWrite(interp, &output, &zWord[1], nWord-2);
    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;
    }
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
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







-
+












-
+







          }
        case '$':
          if( !interp->isListMode ){
            xGet = thNextVarname; xSubst = thSubstVarname;
            break;
          }
        default: {
          thBufferWrite(interp, &output, &zWord[i], 1);
          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);
        rc = thBufferWrite(interp, &output, zRes, nRes);
        thBufferWrite(interp, &output, zRes, nRes);
        i += (nGet-1);
      }
    }
  }

  if( rc==TH_OK ){
    Th_SetResult(interp, output.zBuf, output.nBuf);
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
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







-
+




-
+













-
-
+
+







      goto finish;
    }
    zInput = &zInput[nWord];
    nInput = nList-(zInput-zList);
    if( nWord>0 ){
      zWord = Th_GetResult(interp, &nWord);
      thBufferWrite(interp, &strbuf, zWord, nWord);
      thBufferWrite(interp, &strbuf, "\0", 1);
      thBufferAddChar(interp, &strbuf, 0);
      thBufferWrite(interp, &lenbuf, &nWord, sizeof(int));
      nCount++;
    }
  }
  assert((lenbuf.nBuf/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];
    memcpy(anElem, lenbuf.zBuf, lenbuf.nBuf);
    memcpy(zElem, strbuf.zBuf, strbuf.nBuf);
    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;
  }
1124
1125
1126
1127
1128
1129
1130
1131

1132
1133
1134
1135
1136
1137
1138
1139

1140
1141
1142
1143
1144
1145
1146
1151
1152
1153
1154
1155
1156
1157

1158
1159
1160
1161
1162
1163
1164
1165

1166
1167
1168
1169
1170
1171
1172
1173







-
+







-
+







**
** 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.
** 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 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;
1241
1242
1243
1244
1245
1246
1247





















1248
1249
1250
1251
1252
1253
1254
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







  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);
1291
1292
1293
1294
1295
1296
1297
1298

1299
1300
1301
1302
1303
1304
1305
1339
1340
1341
1342
1343
1344
1345

1346
1347
1348
1349
1350
1351
1352
1353







-
+







    Th_Free(interp, pValue->zData);
    pValue->zData = 0;
  }

  assert(zValue || nValue==0);
  pValue->zData = Th_Malloc(interp, nValue+1);
  pValue->zData[nValue] = '\0';
  memcpy(pValue->zData, zValue, nValue);
  th_memcpy(pValue->zData, zValue, nValue);
  pValue->nData = nValue;

  return TH_OK;
}

/*
** Create a variable link so that accessing variable (zLocal, nLocal) is
1410
1411
1412
1413
1414
1415
1416
1417

1418
1419
1420
1421
1422
1423
1424
1458
1459
1460
1461
1462
1463
1464

1465
1466
1467
1468
1469
1470
1471
1472







-
+







*/
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);
  memcpy(zRes, z, n);
  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
1470
1471
1472
1473
1474
1475
1476
1477

1478
1479
1480
1481
1482
1483
1484
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);
    memcpy(zResult, z, n);
    th_memcpy(zResult, z, n);
    zResult[n] = '\0';
    pInterp->zResult = zResult;
    pInterp->nResult = n;
  }

  return TH_OK;
}
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
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







-
+



-
-
-
-
+
+
+
+
+
+
+
+



-
+

+
+
-
-
-
+
+
+
+
+
+
+







    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_Malloc(Th_Interp *pInterp, int nByte){
  void *p = pInterp->pVtab->xMalloc(nByte);
  if( p ){
    memset(p, 0, nByte);
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_Free(Th_Interp *pInterp, void *z){
void Th_DbgFree(Th_Interp *pInterp, void *z){
  if( z ){
    Th_Vtab *pVtab = pInterp->pVtab;
    if( pVtab ){
    pInterp->pVtab->xFree(z);
  }
}
      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(
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
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







-
-
+
+










-
+





-
+

-
+
+
+
+
+
+
+
+
+
+



-
+

-
+



-
-
+
+







  int *pnList,                 /* IN/OUT: Current length of *pzList */
  const char *zElem,           /* Data to append */
  int nElem                    /* Length of nElem */
){
  Buffer output;
  int i;

  int hasSpecialChar = 0;
  int hasEscapeChar = 0;
  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 ){
    thBufferWrite(interp, &output, " ", 1);
    thBufferAddChar(interp, &output, ' ');
  }

  for(i=0; i<nElem; i++){
    char c = zElem[i];
    if( th_isspecial(c) ) hasSpecialChar = 1;
    if( c=='\\' ) hasEscapeChar = 1;
    if( c=='\\' ){ hasEscapeChar = 1; break; }
    if( c=='{' ) nBrace++;
    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) ){
    thBufferWrite(interp, &output, "{", 1);
    thBufferAddChar(interp, &output, '{');
    thBufferWrite(interp, &output, zElem, nElem);
    thBufferWrite(interp, &output, "}", 1);
    thBufferAddChar(interp, &output, '}');
  }else{
    for(i=0; i<nElem; i++){
      char c = zElem[i];
      if( th_isspecial(c) ) thBufferWrite(interp, &output, "\\", 1);
      thBufferWrite(interp, &output, &c, 1);
      if( th_isspecial(c) ) thBufferAddChar(interp, &output, '\\');
      thBufferAddChar(interp, &output, c);
    }
  }

  *pzList = output.zBuf;
  *pnList = output.nBuf;

  return TH_OK;
1773
1774
1775
1776
1777
1778
1779
1780
1781


1782
1783
1784
1785
1786
1787
1788
1840
1841
1842
1843
1844
1845
1846


1847
1848
1849
1850
1851
1852
1853
1854
1855







-
-
+
+








  if( nElem<0 ){
    nElem = th_strlen(zElem);
  }

  nNew = *pnStr + nElem;
  zNew = Th_Malloc(interp, nNew);
  memcpy(zNew, *pzStr, *pnStr);
  memcpy(&zNew[*pnStr], zElem, nElem);
  th_memcpy(zNew, *pzStr, *pnStr);
  th_memcpy(&zNew[*pnStr], zElem, nElem);

  Th_Free(interp, *pzStr);
  *pzStr = zNew;
  *pnStr = nNew;

  return TH_OK;
}
1820
1821
1822
1823
1824
1825
1826

1827
1828
1829


1830
1831
1832







1833
1834
1835
1836
1837
1838
1839
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







+



+
+
-
-
-
+
+
+
+
+
+
+







  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(sizeof(Th_Interp) + sizeof(Th_Frame));
  memset(p, 0, sizeof(Th_Interp));
  p->pVtab = 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;
}

2333
2334
2335
2336
2337
2338
2339
2340

2341
2342
2343
2344
2345
2346
2347

2348
2349
2350
2351
2352
2353
2354
2407
2408
2409
2410
2411
2412
2413

2414
2415
2416
2417
2418
2419
2420

2421
2422
2423
2424
2425
2426
2427
2428







-
+






-
+







      }

      if( pNew->pOp || pNew->nValue ){
        if( pNew->nValue ){
          /* A terminal. Copy the string value. */
          assert( !pNew->pOp );
          pNew->zValue = Th_Malloc(interp, pNew->nValue);
          memcpy(pNew->zValue, z, pNew->nValue);
          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));
          memcpy(apToken, apTokenOld, sizeof(Expr *)*nToken);
          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);
2511
2512
2513
2514
2515
2516
2517
2518

2519
2520
2521
2522
2523
2524
2525
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;
    memcpy(pRet->zKey, zKey, nKey);
    th_memcpy(pRet->zKey, zKey, nKey);
    pRet->pNext = pHash->a[iKey];
    pHash->a[iKey] = pRet;
  }

  return pRet;
}

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
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







+





-
+


-
-
-
+
+
+








/*
** 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;
    iVal = iVal * -1;
    uVal = iVal * -1;
  }
  *(--z) = '\0';
  *(--z) = (char)(48+((unsigned)iVal%10));
  while( (iVal = ((unsigned)iVal/10))>0 ){
    *(--z) = (char)(48+((unsigned)iVal%10));
  *(--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);

Changes to src/th.h.

19
20
21
22
23
24
25
26

27
28
29
30
31
32
33
19
20
21
22
23
24
25

26
27
28
29
30
31
32
33







-
+







** Opaque handle for interpeter.
*/
typedef struct Th_Interp Th_Interp;

/*
** Create and delete interpreters.
*/
Th_Interp * Th_CreateInterp(Th_Vtab *pVtab);
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.
54
55
56
57
58
59
60













61
62
63
64
65
66
67
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80







+
+
+
+
+
+
+
+
+
+
+
+
+







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,
119
120
121
122
123
124
125

126
127




















128
129
130
131
132
133
134
132
133
134
135
136
137
138
139


140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166







+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







*/
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_Malloc(Th_Interp *, int);
void Th_Free(Th_Interp *, void *);
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 *);

Changes to src/th_lang.c.

160
161
162
163
164
165
166








































167
168
169
170
171
172
173
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+








  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,
183
184
185
186
187
188
189







































190
191
192
193
194
195
196
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







  }

  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(
843
844
845
846
847
848
849
























850
851
852
853
854
855
856
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







    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(
  Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
){
  int iStart;
1113
1114
1115
1116
1117
1118
1119

1120
1121
1122
1123
1124
1125
1126
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230







+







  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 }
  };
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
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







-
-
+
+


















+


+







static int breakpoint_command(
  Th_Interp *interp,
  void *ctx,
  int argc,
  const char **argv,
  int *argl
){
  int cnt = 0;
  cnt++;
  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},

Changes to src/th_main.c.

28
29
30
31
32
33
34



35

36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70




71
72
73
74
75
76
77
28
29
30
31
32
33
34
35
36
37

38
39
40
41
42
43
44
45
46
47
48
49
50
51



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84







+
+
+
-
+













-
-
-
+
+
+



















+
+
+
+







** 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)0x0000000F) /* All possible init flags. */
#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)0x00000010) /* We opened the config. */
#define TH_STATE_REPOSITORY ((u32)0x00000020) /* We opened the repository. */
#define TH_STATE_MASK       ((u32)0x00000030) /* All possible state flags. */
#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;

95
96
97
98
99
100
101

102
103
104
105
106
107
108
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116







+








/*
** 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);
125
126
127
128
129
130
131

132
133
134
135
136
137
138
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147







+







/*
** 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.
146
147
148
149
150
151
152
153

154
155
156
157
158
159
160
155
156
157
158
159
160
161

162
163
164
165
166
167
168
169







-
+







}

/*
** - 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 checkin identifier in zRev and an optiona glob pattern in zGLOB
** 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,
265
266
267
268
269
270
271
272

273
274
275
276
277
278
279
280
281
282
283
284
285
286
287









































288
289
290
291
292
293
294
274
275
276
277
278
279
280

281
282
283
284
285
286
287
288
289
290
291
292
293
294
295

296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343







-
+














-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







** True if output is enabled.  False if disabled.
*/
static int enableOutput = 1;

/*
** TH1 command: enable_output BOOLEAN
**
** Enable or disable the puts and wiki commands.
** 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);
    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.
*/
305
306
307
308
309
310
311


312













313
314
315








316
317







318
319
320
321
322
323


324

325
326
327
328
329
330
331
332
333



334

335
336
337
338

339
340
341
342



343
344
345
346
347
348
349
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376



377
378
379
380
381
382
383
384
385

386
387
388
389
390
391
392
393
394
395
396
397
398
399
400

401
402
403
404
405
406
407
408
409
410
411
412
413

414
415
416
417

418
419



420
421
422
423
424
425
426
427
428
429







+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+

-
+
+
+
+
+
+
+






+
+
-
+









+
+
+
-
+



-
+

-
-
-
+
+
+







    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:  Either to the console
** or to the CGI reply buffer.  Escape all characters with special
** meaning to HTML if the encode parameter is true.
** 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(const char *z, int n, int encode){
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);
    if( g.cgiOutput ){
    }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(const char *z, int n, int forceCgi){
static void sendError(Blob * pOut, const char *z, int n, int forceCgi){
  int savedEnable = enableOutput;
  enableOutput = 1;
  if( forceCgi || g.cgiOutput ){
    sendText("<hr /><p class=\"thmainError\">", -1, 0);
    sendText(pOut, "<hr><p class=\"thmainError\">", -1, 0);
  }
  sendText("ERROR: ", -1, 0);
  sendText((char*)z, n, 1);
  sendText(forceCgi || g.cgiOutput ? "</p>" : "\n", -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".
409
410
411
412
413
414
415




















416
417
418
419
420
421

422
423
424
425
426
427
428
429
430
431
432
433

434
435
436
437
438
439
440
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





-
+











-
+







      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 (html) or unchanged (puts).
** 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((char*)argv[1], argl[1], *(unsigned int*)pConvert);
  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.
504
505
506
507
508
509
510





511

























512
513
514
515
516
517
518
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







+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







  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;
}
  login_verify_csrf_secret();

/*
** 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.
535
536
537
538
539
540
541

542
543
544
545
546
547
548
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678







+







  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
**
662
663
664
665
666
667
668
669

670
671
672
673
674

























































675
676
677
678
679
680
681
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







-
+





+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







  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_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.
722
723
724
725
726
727
728
729

730
731
732
733
734
735
736
909
910
911
912
913
914
915

916
917
918
919
920
921
922
923







-
+







        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_Trace("[searchable %#h] => %d<br>\n", argl[1], argv[1], rc);
  }
  Th_SetResultInt(interp, rc);
  return TH_OK;
}

/*
** TH1 command: hasfeature STRING
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
961
962
963
964
965
966
967

968
969
970

971
972
973
974
975
976
977







-



-







    /* placeholder for following ifdefs... */
  }
#if defined(FOSSIL_ENABLE_SSL)
  else if( 0 == fossil_strnicmp( zArg, "ssl\0", 4 ) ){
    rc = 1;
  }
#endif
#if defined(FOSSIL_ENABLE_LEGACY_MV_RM)
  else if( 0 == fossil_strnicmp( zArg, "legacyMvRm\0", 11 ) ){
    rc = 1;
  }
#endif
#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 ) ){
843
844
845
846
847
848
849
850

851
852
853
854
855
856
857
1028
1029
1030
1031
1032
1033
1034

1035
1036
1037
1038
1039
1040
1041
1042







-
+







    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_Trace("[hasfeature %#h] => %d<br>\n", argl[1], zArg, rc);
  }
  Th_SetResultInt(interp, rc);
  return TH_OK;
}


/*
874
875
876
877
878
879
880
881

882
883
884
885
886
887
888
1059
1060
1061
1062
1063
1064
1065

1066
1067
1068
1069
1070
1071
1072
1073







-
+







  }
#if defined(FOSSIL_ENABLE_TCL)
  if( g.tcl.interp ){
    rc = 1;
  }
#endif
  if( g.thTrace ){
    Th_Trace("[tclReady] => %d<br />\n", rc);
    Th_Trace("[tclReady] => %d<br>\n", rc);
  }
  Th_SetResultInt(interp, rc);
  return TH_OK;
}


/*
903
904
905
906
907
908
909
910

911
912
913
914
915
916
917
1088
1089
1090
1091
1092
1093
1094

1095
1096
1097
1098
1099
1100
1101
1102







-
+







  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_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
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
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







-
+












-
+


-
+




+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







    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(z, -1, 0);
    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(z, -1, 0);
      sendText(0,z, -1, 0);
      free(z);
    }
    sendText("</select>", -1, 0);
    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], &copylength) ) 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.
*/
1035
1036
1037
1038
1039
1040
1041
1042

1043
1044

1045
1046
1047
1048
1049
1050
1051
1268
1269
1270
1271
1272
1273
1274

1275
1276

1277
1278
1279
1280
1281
1282
1283
1284







-
+

-
+







  Th_SetResult(interp, g.zRepositoryName, -1);
  return TH_OK;
}

/*
** TH1 command: checkout ?BOOLEAN?
**
** Return the fully qualified directory name of the current checkout or an
** 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 checkout, opening the configuration ("user") database and the
** 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,
1090
1091
1092
1093
1094
1095
1096
1097

1098
1099
1100
1101
1102
1103
1104
1323
1324
1325
1326
1327
1328
1329

1330
1331
1332
1333
1334
1335
1336
1337







-
+







/*
** TH1 command: globalState NAME ?DEFAULT?
**
** Returns a string containing the value of the specified global state
** variable -OR- the specified default value.  Currently, the supported
** items are:
**
** "checkout"        = The active local checkout directory, if any.
** "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.
1248
1249
1250
1251
1252
1253
1254



















1255
1256
1257
1258
1259
1260
1261
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







  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(
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
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







-
+









-
+

-
+
+
+
+
+
+
+








-
-
+
+


-
+
+
+
+
+
+










+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







  const char **argv,
  int *argl
){
  if( argc!=1 ){
    return Th_WrongNumArgs(interp, "styleFooter");
  }
  if( Th_IsRepositoryOpen() ){
    style_footer();
    style_finish_page();
    Th_SetResult(interp, 0, 0);
    return TH_OK;
  }else{
    Th_SetResult(interp, "repository unavailable", -1);
    return TH_ERROR;
  }
}

/*
** TH1 command: styleScript
** TH1 command: styleScript ?BUILTIN-FILENAME?
**
** Render the configured JavaScript for the selected skin.
** 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 ){
    return Th_WrongNumArgs(interp, "styleScript");
  if( argc!=1 && argc!=2 ){
    return Th_WrongNumArgs(interp, "styleScript ?BUILTIN_NAME?");
  }
  if( Th_IsRepositoryOpen() ){
    const char *zScript = skin_get("js");
    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.
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
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+




















-
+







      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 ){
    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{
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
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







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
















-
+




















-
+







    { "content", unversionedContentCmd },
    { "list",    unversionedListCmd    },
    { 0, 0 }
  };
  return Th_CallSubCommand(interp, p, argc, argv, argl, aSub);
}

#ifdef _WIN32
# include <windows.h>
#else
# include <sys/time.h>
# include <sys/resource.h>
#endif

/*
** Get user and kernel times in microseconds.
*/
static void getCpuTimes(sqlite3_uint64 *piUser, sqlite3_uint64 *piKernel){
#ifdef _WIN32
  FILETIME not_used;
  FILETIME kernel_time;
  FILETIME user_time;
  GetProcessTimes(GetCurrentProcess(), &not_used, &not_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
}

/*
** TH1 command: utime
**
** Return the number of microseconds of CPU time consumed by the current
** process in user space.
*/
static int utimeCmd(
  Th_Interp *interp,
  void *p,
  int argc,
  const char **argv,
  int *argl
){
  sqlite3_uint64 x;
  char zUTime[50];
  getCpuTimes(&x, 0);
  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];
  getCpuTimes(0, &x);
  fossil_cpu_times(0, &x);
  sqlite3_snprintf(sizeof(zUTime), zUTime, "%llu", x);
  Th_SetResult(interp, zUTime, -1);
  return TH_OK;
}


/*
1552
1553
1554
1555
1556
1557
1558
1559

1560
1561
1562
1563
1564
1565
1566
1850
1851
1852
1853
1854
1855
1856

1857
1858
1859
1860
1861
1862
1863
1864







-
+







    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>sizeof(aRand) ) n = sizeof(aRand);
    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;
1655
1656
1657
1658
1659
1660
1661



1662






1663
1664
1665
1666
1667
1668
1669
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976







+
+
+

+
+
+
+
+
+







      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;
1707
1708
1709
1710
1711
1712
1713
1714

1715
1716
1717
1718
1719
1720
1721
2014
2015
2016
2017
2018
2019
2020

2021
2022
2023
2024
2025
2026
2027
2028







-
+







    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 " : "",
    Th_Trace("[setting %s%#h] => %d<br>\n", strict ? "strict " : "",
             argl[nArg], argv[nArg], rc);
  }
  return rc;
}

/*
** TH1 command: glob_match ?-one? ?--? patternList string
1914
1915
1916
1917
1918
1919
1920


































1921
1922
1923
1924
1925
1926
1927
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







  }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
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
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







+
-
+










+
+
+
+


+


+

+














+














+






+




-
+








-
+





+
+
+
+
+
+
+
+
+
-
+







*/
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 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);
    Th_Trace("th1-init 0x%x => 0x%x<br>\n", g.th1Flags, flags);
  }
  if( needConfig ){
    /*
    ** This function uses several settings which may be defined in the
    ** repository and/or the global configuration.  Since the caller
    ** passed a non-zero value for the needConfig parameter, make sure
    ** the necessary database connections are open prior to continuing.
    */
    Th_OpenConfig(1);
    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(&vtab);
      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 ||
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
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







-
+



-
+






+
+
+
+
+
+
+
+
+
+
+
+
+
+








-
+







      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(zResult, nResult, 0);
        sendError(0,zResult, nResult, 0);
      }
    }
    if( g.thTrace ){
      Th_Trace("th1-setup {%h} => %h<br />\n", g.th1Setup,
      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_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
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
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







-
+

















-
+







    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_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_Trace("set %h {%h}<br>\n", zName, zValue);
  }
  Th_SetVar(g.interp, zName, -1, zValue, strlen(zValue));
  blob_reset(&value);
}

/*
** Unset a variable.
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
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







-
+
















-
+







    int nResult = 0;
    char *zResult = (char*)Th_GetResult(g.interp, &nResult);
    /*
    ** Make sure that the TH1 script error was not caused by a "missing"
    ** command hook handler as that is not actually an error condition.
    */
    if( memcmp(zResult, NO_COMMAND_HOOK_ERROR, nResult)!=0 ){
      sendError(zResult, nResult, 0);
      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_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.
2331
2332
2333
2334
2335
2336
2337
2338

2339
2340
2341
2342
2343
2344
2345
2706
2707
2708
2709
2710
2711
2712

2713
2714
2715
2716
2717
2718
2719
2720







-
+







  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_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.
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
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







-
+
















-
+







    int nResult = 0;
    char *zResult = (char*)Th_GetResult(g.interp, &nResult);
    /*
    ** Make sure that the TH1 script error was not caused by a "missing"
    ** webpage hook handler as that is not actually an error condition.
    */
    if( memcmp(zResult, NO_WEBPAGE_HOOK_ERROR, nResult)!=0 ){
      sendError(zResult, nResult, 1);
      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_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.
2418
2419
2420
2421
2422
2423
2424
2425

2426
2427
2428
2429
2430
2431
2432
2793
2794
2795
2796
2797
2798
2799

2800
2801
2802
2803
2804
2805
2806
2807







-
+







  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_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.
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
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







+

-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+




+
+
+
-
+

+
-
+



-
+














-
+

-
+



-
+


+
+
+
+
+
+










-
+

-
+

+


+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+











-







    return 1;
  }
  return db_get_boolean("th1-docs", 0);
}
#endif


#if INTERFACE
/*
** The z[] input contains text mixed with TH1 scripts.
** Flags for use with Th_RenderToBlob. These must not overlap with
** The TH1 scripts are contained within <th1>...</th1>.
** TH1 variables are $aaa or $<aaa>.  The first form of
** variable is literal.  The second is run through htmlize
** before being inserted.
**
** This routine processes the template and writes the results
** on either stdout or into CGI.
** 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_Render(const char *z){
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(TH_INIT_DEFAULT);
  Th_FossilInit(mFlags & TH_INIT_MASK);
  while( z[i] ){
    if( 0==(TH_R2B_NO_VARS & mFlags)
    if( z[i]=='$' && (n = validVarName(&z[i+1]))>0 ){
        && z[i]=='$' && (n = validVarName(&z[i+1]))>0 ){
      const char *zVar;
      int nVar;
      int encode = 1;
      sendText(z, i, 0);
      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((char*)zResult, n, encode);
      sendText(pOut,(char*)zResult, n, encode);
    }else if( z[i]=='<' && isBeginScriptTag(&z[i]) ){
      sendText(z, i, 0);
      sendText(pOut,z, i, 0);
      z += i+5;
      for(i=0; z[i] && (z[i]!='<' || !isEndScriptTag(&z[i])); i++){}
      if( g.thTrace ){
        Th_Trace("eval {<pre>%#h</pre>}<br />", i, z);
        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(zResult, n, 1);
    sendError(pOut,zResult, n, 1);
  }else{
    sendText(z, i, 0);
    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)
*/
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
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







-
+
+


-










+

+








/*
** 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 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);
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
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








+
+
+
+
+
+

-
+



+













-






+
+




-
+




+







    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, g.argv[2], -1);
  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;
  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");
2659
2660
2661
2662
2663
2664
2665

2666



2667
2668
2669
2670
2671
2672
2673
3091
3092
3093
3094
3095
3096
3097
3098

3099
3100
3101
3102
3103
3104
3105
3106
3107
3108







+
-
+
+
+







    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));
    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
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
3131
3132
3133
3134
3135
3136
3137

3138
3139
3140
3141
3142
3143
3144







-







**                          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;
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738



2739
2740
2741


2742
2743

2744
2745
2746
2747
3163
3164
3165
3166
3167
3168
3169



3170
3171
3172
3173


3174
3175
3176

3177
3178
3179
3180
3181







-
-
-
+
+
+

-
-
+
+

-
+




    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("RESULT (", -1, 0);
  sendText(Th_ReturnCodeName(rc, 0), -1, 0);
  sendText(")", -1, 0);
  sendText(0,"RESULT (", -1, 0);
  sendText(0,Th_ReturnCodeName(rc, 0), -1, 0);
  sendText(0,")", -1, 0);
  if( zResult && nResult>0 ){
    sendText(": ", -1, 0);
    sendText(zResult, nResult, 0);
    sendText(0,": ", -1, 0);
    sendText(0,zResult, nResult, 0);
  }
  sendText("\n", -1, 0);
  sendText(0,"\n", -1, 0);
  Th_PrintTraceLog();
  if( forceCgi ) cgi_reply();
}
#endif

Changes to src/th_tcl.c.

96
97
98
99
100
101
102
103

104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124

125
126
127
128
129
130
131

132
133
134
135
136
137
138

139
140
141
142
143
144
145

146
147
148
149
150
151
152
96
97
98
99
100
101
102

103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123

124
125
126
127
128
129
130

131
132
133
134
135
136
137

138
139
140
141
142
143
144

145
146
147
148
149
150
151
152







-
+




















-
+






-
+






-
+






-
+







#      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 "tcl86.dll\0"
#      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.6.dll\0"
#        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.6.dylib\0"
#        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 "libtcl86.so\0"
#        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.6.so\0"
#        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
970
971
972
973
974
975
976
977

978
979
980
981
982
983
984
970
971
972
973
974
975
976

977
978
979
980
981
982
983
984







-
+







      *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.6, 8.5, or 8.4 shared library \"",
      "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;
1008
1009
1010
1011
1012
1013
1014
1015

1016
1017
1018
1019
1020
1021
1022
1008
1009
1010
1011
1012
1013
1014

1015
1016
1017
1018
1019
1020
1021
1022







-
+







  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_NewIntObj(argc - 1);
  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;
  }
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
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







-
+
+






-
+
+




















-
+

-
+
+







    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_GetStringResult(tclInterp), -1);
        "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_GetStringResult(tclInterp), -1);
        "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_Eval(tclInterp, setup)!=TCL_OK ){
  if( setup && Tcl_EvalEx(tclInterp, setup, -1, 0)!=TCL_OK ){
    Th_ErrorMessage(interp,
        "Tcl setup script error:", Tcl_GetStringResult(tclInterp), -1);
        "Tcl setup script error:",
        Tcl_GetString(Tcl_GetObjResult(tclInterp)), -1);
    Tcl_DeleteInterp(tclInterp);
    tclContext->interp = tclInterp = 0;
    return TH_ERROR;
  }
  return TH_OK;
}

Changes to src/timeline.c.

33
34
35
36
37
38
39







40
41
42
43
44
45
46

47
48
49
50
51
52
53

54
55

56
57

58
59
60
61
62
63
64
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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 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);
    cgi_printf(" %s", UNPUB_TAG);
  }
}

/*
** Generate a hyperlink to a version.
*/
void hyperlink_to_uuid(const char *zUuid){
void hyperlink_to_version(const char *zVerHash){
  if( g.perm.Hyperlink ){
    @ %z(chref("timelineHistLink","%R/info/%!S",zUuid))[%S(zUuid)]</a>
    @ %z(chref("timelineHistLink","%R/info/%!S",zVerHash))[%S(zVerHash)]</a>
  }else{
    @ <span class="timelineHistDsp">[%S(zUuid)]</span>
    @ <span class="timelineHistDsp">[%S(zVerHash)]</span>
  }
}

/*
** Generate a hyperlink to a date & time.
*/
void hyperlink_to_date(const char *zDate, const char *zSuffix){
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109














110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157



158
159
160
161
162
163
164







165
166
167
168
169
170
171
172
173
174

175
176
177
178
179
180
181
182
183
184
185
186

187
188
189
190
191
192

193
194
195
196
197
198
199


200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221


































222
223
224
225
226
227
228

229
230
231
232
233
234

235
236
237
238
239
240
241
242
243
244
245








246
247
248
249
250
251
252
253
254
255
256
257
258

259
260
261
262
263


264
265
266
267
268
269
270
271

272
273
274
275
276
277
278
96
97
98
99
100
101
102














103
104
105
106
107
108
109
110
111
112
113
114
115
116
















































117
118
119







120
121
122
123
124
125
126










127












128






129







130
131














132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179

180
181
182
183
184
185

186
187
188
189
190
191






192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211

212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235







-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
+
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-








+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+






-
+





-
+





-
-
-
-
-
-
+
+
+
+
+
+
+
+












-
+





+
+








+







  }
}

/*
** Allowed flags for the tmFlags argument to www_print_timeline
*/
#if INTERFACE
#define TIMELINE_ARTID    0x000001  /* Show artifact IDs on non-check-in lines */
#define TIMELINE_LEAFONLY 0x000002  /* Show "Leaf" but not "Merge", "Fork" etc */
#define TIMELINE_BRIEF    0x000004  /* Combine adjacent elements of same obj */
#define TIMELINE_GRAPH    0x000008  /* Compute a graph */
#define TIMELINE_DISJOINT 0x000010  /* Elements are not contiguous */
#define TIMELINE_FCHANGES 0x000020  /* Detail file changes */
#define TIMELINE_BRCOLOR  0x000040  /* Background color by branch name */
#define TIMELINE_UCOLOR   0x000080  /* Background color by user */
#define TIMELINE_FRENAMES 0x000100  /* Detail only file name changes */
#define TIMELINE_UNHIDE   0x000200  /* Unhide check-ins with "hidden" tag */
#define TIMELINE_SHOWRID  0x000400  /* Show RID values in addition to UUIDs */
#define TIMELINE_BISECT   0x000800  /* Show supplimental bisect information */
#define TIMELINE_COMPACT  0x001000  /* Use the "compact" view style */
#define TIMELINE_VERBOSE  0x002000  /* Use the "detailed" view style */
#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   0x004000  /* Use the "modern" view style */
#define TIMELINE_COLUMNAR 0x008000  /* Use the "columns" view style */
#define TIMELINE_CLASSIC  0x010000  /* Use the "classic" view style */
#define TIMELINE_VIEWS    0x01f000  /* Mask for all of the view styles */
#define TIMELINE_NOSCROLL 0x100000  /* Don't scroll to the selection */
#define TIMELINE_FILEDIFF 0x200000  /* Show File differences, not ckin diffs */
#endif

/*
** Hash a string and use the hash to determine a background color.
*/
char *hash_color(const char *z){
  int i;                       /* Loop counter */
  unsigned int h = 0;          /* Hash on the branch name */
  int r, g, b;                 /* Values for red, green, and blue */
  int h1, h2, h3, h4;          /* Elements of the hash value */
  int mx, mn;                  /* Components of HSV */
  static char zColor[10];      /* The resulting color */
  static int ix[2] = {0,0};    /* Color chooser parameters */

  if( ix[0]==0 ){
    if( skin_detail_boolean("white-foreground") ){
      ix[0] = 140;
      ix[1] = 40;
    }else{
      ix[0] = 216;
      ix[1] = 16;
    }
  }
  for(i=0; z[i]; i++ ){
    h = (h<<11) ^ (h<<1) ^ (h>>3) ^ z[i];
  }
  h1 = h % 6;  h /= 6;
  h3 = h % 30; h /= 30;
  h4 = h % 40; h /= 40;
  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;
#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 */
}

/*
** COMMAND: test-hash-color
**
** Usage: %fossil test-hash-color TAG ...
**
#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 */
** 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]));
  }
}

#define TIMELINE_FORUMTXT 0x4000000 /* Render all forum messages */
/*
** 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();

#define TIMELINE_REFS     0x8000000 /* Output intended for References tab */
  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));'>
#define TIMELINE_DELTA   0x10000000 /* Background color shows delta manifests */
      @ %h(zBr) - %s(hash_color(zBr)) -
      @ Omnes nos quasi oves erravimus unusquisque in viam
      @ suam declinavit.</p>
      cnt++;
    }
  }
  if( cnt ){
#define TIMELINE_NOCOLOR 0x20000000 /* No colors except for highlights */
#endif
    @ <hr />
  }
  @ <form method="post" action="%s(g.zTop)/hash-color-test">
  @ <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">
  @ </form>
  style_footer();
}

/*
** 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.  UUID
**    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", "div")
**    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 */
  int selectedRid,       /* Highlight the line with this RID value */
  void (*xExtra)(int)    /* Routine to call on each line of display */
  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 checkout version */
  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";
288
289
290
291
292
293
294




295
296



297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315

316
317
318
319
320
321
322
245
246
247
248
249
250
251
252
253
254
255


256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285







+
+
+
+
-
-
+
+
+



















+







  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">
  }
  @ <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 ){
338
339
340
341
342
343
344
345

346
347
348
349
350
351
352
301
302
303
304
305
306
307

308
309
310
311
312
313
314
315







-
+







    }
    if( pendingEndTr ){
      @ </td></tr>
      pendingEndTr = 0;
    }
    if( fossil_strcmp(zType,"div")==0 ){
      if( !prevWasDivider ){
        @ <tr><td colspan="3"><hr class="timelineMarker" /></td></tr>
        @ <tr><td colspan="3"><hr class="timelineMarker"></td></tr>
      }
      prevWasDivider = 1;
      continue;
    }
    prevWasDivider = 0;
    /* Date format codes:
    **   (0)  HH:MM
381
382
383
384
385
386
387



388
389
390
391
392
393













394

395
396
397
398
399








400

401




402
403
404
405
406

407



















408
409

410
411
412
413
414
415
416
417
418

419

420
421
422
423
424
425
426
427

428

429
430
431
432
433
434
435
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
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374





375
376
377
378
379
380
381
382
383
384

385
386
387
388
389
390
391
392
393
394

395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414

415
416
417
418
419
420
421
422
423
424
425

426
427
428
429
430
431
432
433
434
435

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







+
+
+






+
+
+
+
+
+
+
+
+
+
+
+
+

+
-
-
-
-
-
+
+
+
+
+
+
+
+

+
-
+
+
+
+





+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+









+
-
+








+
-
+











+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+






-
+




-
+

-
+

-
-
+
+
+
+
+
+







-
+
















-
+

-
+
-
-








-
+




-
+

-
+






+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+














-
+







    }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);
        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);
        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 ? hash_color(zUser) : 0;
      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)!=0)
    && (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) ){
        if( zBr==0 || strcmp(zBr,"trunk")==0 ){
        }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;
      int aParent[GR_MAX_RAIL];
      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, aParent, zBr, zBgClr,
                           zUuid, isLeaf);
      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, zBr, zBgClr, zUuid, 0);
      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" id='mc%d(gidx)'>
      @ <td class="timeline%s(zStyle)Cell%s(zExtraClass)" id='mc%d(gidx)'>
    }else{
      @ <td class="timeline%s(zStyle)Cell">
      @ <td class="timeline%s(zStyle)Cell%s(zExtraClass)">
    }
    if( pGraph && zType[0]!='c' ){
      @ &bull;
    if( pGraph ){
      if( zType[0]=='e' ){
        @ <b>Note:</b>
      }else if( zType[0]!='c' ){
        @ &bull;
      }
    }
    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");
          "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_uuid(zUuid);
        hyperlink_to_version(zUuid);
        if( isLeaf ){
          if( db_exists("SELECT 1 FROM tagxref"
          if( has_closed_tag(rid) ){
                        " WHERE rid=%d AND tagid=%d AND tagtype>0",
                        rid, TAG_CLOSED) ){
            @ <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_uuid(zUuid);
        hyperlink_to_version(zUuid);
      }
      if( tmFlags & TIMELINE_SHOWRID ){
        int srcId = delta_source_rid(rid);
        if( srcId ){
          @ (%d(rid)&larr;%d(srcId))
          @ (%z(href("%R/deltachain/%d",rid))%d(rid)&larr;%d(srcId)</a>)
        }else{
          @ (%d(rid))
          @ (%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_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 && blob_size(&comment)>mxWikiLen ){
      }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;
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
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







-
+

-
+













-
+
-
-












-
+
+




+
+
-
+
+
+
+







    */
    if( drawDetailEllipsis ){
      @ <span class='timelineEllipsis' id='ellipsis-%d(rid)' \
      @ data-id='%d(rid)'>...</span>
    }
    if( tmFlags & TIMELINE_COLUMNAR ){
      if( !isSelectedOrCurrent ){
        @ <td class="timelineDetailCell" id='md%d(gidx)'>
        @ <td class="timelineDetailCell%s(zExtraClass)" id='md%d(gidx)'>
      }else{
        @ <td class="timelineDetailCell">
        @ <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( db_exists("SELECT 1 FROM tagxref"
          if( has_closed_tag(rid) ){
                        " WHERE rid=%d AND tagid=%d AND tagtype>0",
                        rid, TAG_CLOSED) ){
            @ <span class='timelineLeaf'>Closed-Leaf</span>
          }else{
            @ <span class='timelineLeaf'>Leaf</span>
          }
        }
        cgi_printf("check-in:&nbsp;%z%S</a> ",href("%R/info/%!S",zUuid),zUuid);
      }else if( zType[0]=='e' && tagid ){
        cgi_printf("technote:&nbsp;");
        hyperlink_to_event_tagid(tagid<0?-tagid:tagid);
      }else{
        cgi_printf("artifact:&nbsp;%z%S</a> ",href("%R/info/%!S",zUuid),zUuid);
      }
    }else if( zType[0]=='g' || zType[0]=='w' || zType[0]=='t' || zType[0]=='f'){
    }else if( zType[0]=='g' || zType[0]=='w' || zType[0]=='t'
              || zType[0]=='n' || zType[0]=='f'){
      cgi_printf("artifact:&nbsp;%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 ){
      char *zLink = mprintf("%R/timeline?u=%h&c=%t&y=a", zDispUser, zDate);
        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:&nbsp;%z%h</a>", href("%z",zLink), zDispUser);
    }else{
      cgi_printf("user:&nbsp;%h", zDispUser);
    }

    /* Generate the "tags: TAGLIST" at the end of the comment, together
    ** with hyperlinks to the tag list.
617
618
619
620
621
622
623
624


625
626


627
628
629
630
631
632
633
674
675
676
677
678
679
680

681
682
683

684
685
686
687
688
689
690
691
692







-
+
+

-
+
+







        cgi_printf(" tags:&nbsp;%h", zTagList);
      }
    }

    if( tmFlags & TIMELINE_SHOWRID ){
      int srcId = delta_source_rid(rid);
      if( srcId ){
        cgi_printf(" id:&nbsp;%d&larr;%d", rid, srcId);
        cgi_printf(" id:&nbsp;%z%d&larr;%d</a>",
                   href("%R/deltachain/%d",rid), rid, srcId);
      }else{
        cgi_printf(" id:&nbsp;%d", rid);
        cgi_printf(" id:&nbsp;%z%d</a>",
                   href("%R/deltachain/%d",rid), rid);
      }
    }
    tag_private_status(rid);
    if( xExtra ){
      xExtra(rid);
    }
    /* End timelineDetail */
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
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







-
+







+
-
+

-
+
+


-
+







        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[40];
        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&larr;%d</a>) ",
            sqlite3_snprintf(sizeof(zId), zId, " (%d&larr;%d) ", fid, srcId);
                          href("%R/deltachain/%d", fid), fid, srcId);
          }else{
            sqlite3_snprintf(sizeof(zId), zId, " (%d) ", fid);
            zId = mprintf(" (%z%d</a>) ",
                          href("%R/deltachain/%d", fid), fid);
          }
        }else{
          zId[0] = 0;
          zId = fossil_strdup("");
        }
        if( (tmFlags & TIMELINE_FRENAMES)!=0 ){
          if( !isNew && !isDel && zOldName!=0 ){
            @ <li> %h(zOldName) &rarr; %h(zFilename)%s(zId)
          }
          continue;
        }
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
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







+






+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+










-
+




-
+
+







            @ <li>%h(zOldName) &rarr; %s(zA)%h(zFilename)%s(zId)</a> %s(zUnpub)
          }else{
            @ <li>%s(zA)%h(zFilename)</a>%s(zId) &nbsp; %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, (tmFlags & TIMELINE_DISJOINT)!=0);
    graph_finish(pGraph, zLeftBranch, tmFlags);
    if( pGraph->nErr ){
      graph_free(pGraph);
      pGraph = 0;
    }else{
      @ <tr class="timelineBottom"><td></td><td></td><td></td></tr>
      @ <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);
}

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
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







+
+
+









+
+













+
+
+
+















-
-
-
-
-
-
+
+
+
+
+
+
+
+

+
+
+


-
-
+
+
+
+
+
+
+



-
+
+


-
-
-
+
+
+
+
+

+

+

+


-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+


+
+
+
+
+
-
+



-
-
+
+
+




+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+




-
-
+
+
+



-
+
+







    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:  True if there is a "descender" - an arrow coming from the bottom
    **        of the page straight up to this node.
    **   mo:  "merge-out".  If non-negative, this is the rail position
    **        for the upward portion of a merge arrow.  The merge arrow goes up
    **        to the row identified by mu:.  If this value is negative then
    **        node has no merge children and no merge-out line is drawn.
    **    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, -1 if there is no thick-line riser.
    **    f:  0x01: a leaf node.
    **        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.
    **        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 the absolute value of mi[]
    **        and a thin merge-arrow descender is drawn to the bottom of
    **        the screen.
    **        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);
      cgi_printf("\"d\":%d,",       pRow->bDescender);
      cgi_printf("\"mo\":%d,",      pRow->mergeOut);
      cgi_printf("\"mu\":%d,",      pRow->mergeUpto);
      cgi_printf("\"u\":%d,",       pRow->aiRiser[pRow->iRail]);
      cgi_printf("\"f\":%d,",       pRow->isLeaf ? 1 : 0);
      cgi_printf("\"au\":");
      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);
      cSep = '[';
      for(i=0; i<GR_MAX_RAIL; i++){
      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, i, pRow->aiRiser[i]);
          cgi_printf("%c%d,%d", cSep, aiMap[i], pRow->aiRiser[i]);
          cSep = ',';
        }
      }
      if( cSep=='[' ) cgi_printf("[");
      cgi_printf("],");
      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 = '[';
      for(i=0; i<GR_MAX_RAIL; i++){
        if( pRow->mergeIn[i] ){
          int mi = i;
          if( (pRow->mergeDown >> i) & 1 ) mi = -mi;
            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( cSep=='[' ) cgi_printf("[");
      cgi_printf("],\"h\":\"%!S\"}%s",
      if( k ) cgi_printf("],");
      cgi_printf("\"br\":\"%j\",", pRow->zBranch ? pRow->zBranch : "");
      cgi_printf("\"h\":\"%!S\"}%s",
                 pRow->zUuid, pRow->pNext ? ",\n" : "]\n");
    }
    @ }</script>
    style_graph_generator();
    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.
*/
959
960
961
962
963
964
965
966

967
968

969
970
971
972









973
974
975
976
977
978
979
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







-
+


+




+
+
+
+
+
+
+
+
+







  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){
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"
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
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







-
+

















+
+















+
+
+
+
+
+
+
+
+
+
+







+
+
-
+





-
+


+
+
+
+
+
+
+
+
+
+
+
+
+
+








-
-
-
-
-
-
-
-
-
-
+
+
+
+

















-
+

















-
+
+
+







/*
** 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[14];
  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( cookie_value("ss","m")[0] ){
  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){
  static const char *azViewStyles[] = {
     "m", "Modern View",
     "j", "Columnar View",
     "c", "Compact View",
     "v", "Verbose View",
     "x", "Classic View",
  };
  cookie_link_parameter("ss","ss","m");
  style_submenu_multichoice("ss", sizeof(azViewStyles)/(2*sizeof(azViewStyles[0])),
                            azViewStyles, 0);
  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", zChng));
      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_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(
1167
1168
1169
1170
1171
1172
1173


1174

1175
1176
1177
1178
1179
1180
1181
1345
1346
1347
1348
1349
1350
1351
1352
1353

1354
1355
1356
1357
1358
1359
1360
1361







+
+
-
+







  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.
**
** MS_LIKE, and MS_REGEXP match styles.  For MS_EXACT, the returned expression
** 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.
**
1229
1230
1231
1232
1233
1234
1235
1236

1237
1238
1239
1240
1241
1242







1243
1244
1245
1246
1247
1248
1249
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







-
+






+
+
+
+
+
+
+







  }else if( matchStyle==MS_LIKE ){
    zStart = "(";
    zDelimiter = " OR ";
    zEnd = ")";
    zPrefix = "tagname LIKE 'sym-";
    zSuffix = "'";
    zIntro = "SQL LIKE pattern ";
  }else/* if( matchStyle==MS_REGEXP )*/{
  }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 ){
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
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+






-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+

+
-
+
+
+
+
+
+
+
+


+
+


+


-
-
+
+
+


+

+
+

+


-
-
+
+
+
+
+
+

+
+

-
-
-
+
+
+
+
+


+



-
+
+
+
+

-
+
+

+







+
+
+
+









-
-
-
+
+
+




















+






-
+
+
+





+
-
+







+

+
-
+
+



+
+
+

+
+
+
+
+
+
+

+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+






-
-
+
+



-


+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+


+
+
+
+
+
+









-
+




+
+
+






-
+
+
+
+
+
+


-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


-
+



+









+







+
+



+







    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     After this event
**    b=TIMEORTAG     Before this event
**    c=TIMEORTAG     "Circa" this event
**    m=TIMEORTAG     Mark this event
**    n=COUNT         Maximum number of events.  "all" for no limit
**    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      The same as d=CHECKIN&p=CHECKIN
**    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', 'e', 'f', or 'all'.
**    ss=VIEWSTYLE    c: "Compact"  v: "Verbose"   m: "Modern"  j: "Columnar"
**    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
**    shortest          ... show only the shortest path
**                       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 from branch name
**    ubg             Background color from user
**                       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
**    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
**    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 = name_to_typed_rid(P("p"),"ci");  /* artifact p and its parents */
  int d_rid = name_to_typed_rid(P("d"),"ci");  /* artifact d and descendants */
  int f_rid = name_to_typed_rid(P("f"),"ci");  /* artifact f and close family */
  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 bisectOnly = PB("bisect");     /* Show the check-ins of the bisect */
  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_to_typed_rid(P("to"),"ci");    /* to= for path timelines */
  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 = -9999999;         /* Show a highlight on this RID */
  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);
    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 = "10";
        nEntry = 10;
        z = "50";
        nEntry = 50;
      }
    }
  }else{
    z = "50";
    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("n",z);
  cookie_write_parameter("n","n",0);
  tmFlags |= timeline_ss_submenu();  
    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)
   || (bisectOnly && !g.perm.Setup)
   || (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' || zType[0]=='c' ){
  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);
  }
  cookie_render();
  url_initialize(&url, "timeline");
  cgi_query_parameters_to_url(&url);

  /* Convert r=TAG to t=TAG&rel. */

  /* 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);
    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);
    }

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
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







-
+

-
+

+
+
+
+
-
-
+
+
+
+
+
+
+
+










+
+
+
+
+
+
+








+







  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;
    tmFlags |= TIMELINE_BRIEF | TIMELINE_GRAPH | TIMELINE_CHPICK;
  }else{
    tmFlags |= TIMELINE_GRAPH;
    tmFlags |= TIMELINE_GRAPH | TIMELINE_CHPICK;
  }
  if( related ){
    tmFlags |= TIMELINE_FILLGAPS | TIMELINE_XMERGE;
    tmFlags &= ~TIMELINE_DISJOINT;
  }
  if( PB("ng") || zSearch!=0 ){
    tmFlags &= ~TIMELINE_GRAPH;
  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);"
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
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







+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+






-
+
-
-
-

+
-
-
+
+
+
+
+
+
+
+
+
+



-
+















+
+
+






+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







+
+


+
-
+
+
+
+
+
+

-
+







+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
-
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+




+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+




+
-
+
+
+
+
+

-
+


-
+






+
+



+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+


+
-
-
+
+
+



+
+
+
+
+
+
+
+
+
+
-
+

-
+

-
+


-
+

+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







      "           (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"
      "     AND pid IN rnfork;",
      "   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( bisectOnly
  if( bisectLocal && cgi_is_loopback(g.zIpAddr) && db_open_local(0) ){
   && fossil_strcmp(g.zIpAddr,"127.0.0.1")==0
   && db_open_local(0)
  ){
    int iCurrent = db_lget_int("checkout",0);
    char *zPerm = bisect_permalink();
    bisect_create_bilog_table(iCurrent);
    tmFlags |= TIMELINE_UNHIDE | TIMELINE_BISECT;
    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{
    bisectOnly = 0;
    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);
        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 = P("to");
      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, " AND event.objid IN (0", -1);
    while( p ){
      blob_append_sql(&sql, ",%d", p->rid);
      p = p->u.pTo;
    }
      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;
      }
    blob_append(&sql, ")", -1);
    }
    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");
    addFileGlobExclusion(zChng, &sql);
    tmFlags |= TIMELINE_DISJOINT;
    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 ",
                 db_int(0, "SELECT count(*) FROM timeline"));
    blob_appendf(&desc, "%z[%h]</a>", href("%R/info/%h", zFrom), zFrom);
    blob_append(&desc, " to ", -1);
    blob_appendf(&desc, "%z[%h]</a>", href("%R/info/%h",zTo), zTo);
      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, nd;
    int np = 0, nd;
    const char *zBackTo = 0;
    const char *zFwdTo = 0;
    int ridBackTo = 0;
    int ridFwdTo = 0;

    tmFlags |= TIMELINE_DISJOINT;
    tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS;
    if( p_rid && d_rid ){
      if( p_rid!=d_rid ) p_rid = d_rid;
      if( P("n")==0 ) nEntry = 10;
      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+1);
      /* 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 ){
      if( nd>0 ) blob_appendf(&desc, "%d descendant%s", nd,(1==nd)?"":"s");
      if( useDividers ) selectedRid = d_rid;
        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+1, 0);
      compute_ancestors(p_rid, nEntry==0 ? 0 : nEntry+1, 0, ridBackTo);
      np = db_int(0, "SELECT count(*)-1 FROM ok");
      if( np>0 ){
      if( np>0 || nd==0 ){
        if( nd>0 ) blob_appendf(&desc, " and ");
        blob_appendf(&desc, "%d ancestors", np);
        blob_appendf(&desc, "%d ancestor%s", np, (1==np)?"":"s");
        db_multi_exec("%s", blob_sql_text(&sql));
      }
      if( useDividers ) selectedRid = p_rid;
      if( useDividers && !selectedRid ) selectedRid = p_rid;
    }

    blob_appendf(&desc, " of %z[%S]</a>",
                   href("%R/info/%!S", zUuid), zUuid);
    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 ){
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
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







+
+
+
+
+
+
+
+
+


-
+



-
+











+


-
+










+
+
+
+
+
+
+
+
-
+



+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+
+


+
+
-
+





-
+

-
-
+
+










+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





+
+




+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+











+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


-
+



-
+
-
-
-
-
-
-
-
-
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-

+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-


+







    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 = f_rid;
    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_DISJOINT;
    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_DISJOINT;
      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( bisectOnly ){
    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);
                      zYearMonth);
      nEntry = -1;
    }
    else if( zYearWeek ){
      char *z, *zNext;
      zYearWeek = timeline_expand_datetime(zYearWeek);
      char *z = db_text(0, "SELECT strftime('%%Y-%%W',%Q)", 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 ){       
        if( strlen(zYearWeek)==7 ){
          zYearWeekStart = db_text(0,
             "SELECT date('%.4q-01-01','+%d days','weekday 1')",
             zYearWeek, atoi(zYearWeek+5)*7);
             "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 ){
      blob_append_sql(&cond,
        " AND (EXISTS(SELECT 1 FROM tagxref NATURAL JOIN tag"
        " WHERE %s AND tagtype>0 AND rid=blob.rid)\n", zTagSql/*safe-for-%s*/);

      if( related ){
      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.  This related check-ins are
        ** 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.
        */
        blob_append_sql(&cond,
        db_multi_exec(
          " OR EXISTS(SELECT 1 FROM plink CROSS JOIN tagxref ON rid=cid"
          " NATURAL JOIN tag WHERE %s AND tagtype>0 AND pid=blob.rid)\n",
           zTagSql/*safe-for-%s*/
        );
        if( (tmFlags & TIMELINE_UNHIDE)==0 ){
          blob_append_sql(&cond,
            " AND NOT EXISTS(SELECT 1 FROM plink JOIN tagxref ON rid=cid"
                       " WHERE tagid=%d AND tagtype>0 AND pid=blob.rid)\n",
          "INSERT OR IGNORE INTO related_nodes"
          " SELECT pid FROM selected_nodes CROSS JOIN plink"
          " WHERE selected_nodes.rid=plink.cid;"
            TAG_HIDDEN
          );
        }
        if( P("mionly")==0 ){
          blob_append_sql(&cond,
            " OR EXISTS(SELECT 1 FROM plink CROSS JOIN tagxref ON rid=pid"
            " NATURAL JOIN tag WHERE %s AND tagtype>0 AND cid=blob.rid)\n",
        );
        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;"
            zTagSql/*safe-for-%s*/
          );
        }
          if( (tmFlags & TIMELINE_UNHIDE)==0 ){
            blob_append_sql(&cond,
              " AND NOT EXISTS(SELECT 1 FROM plink JOIN tagxref ON rid=pid"
              " WHERE tagid=%d AND tagtype>0 AND cid=blob.rid)\n",
              TAG_HIDDEN
            );
          }
        }
      }
        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
          );
        }
      }
    }
      blob_append_sql(&cond, ")");
    }
    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";
    }
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
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







+
+
+
+
-
+
+






+
+












-







+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







        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);
        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 ){
        zCirca = zBefore = zAfter = 0;
        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%%')",
        zSearch, zSearch);
    }
    rBefore = symbolic_name_to_mtime(zBefore);
    rAfter = symbolic_name_to_mtime(zAfter);
    rCirca = symbolic_name_to_mtime(zCirca);
        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;
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
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







-
+


-





+
+
+
+


-
+












-
+
+

-
+















-
+



-
+





-
-
+
+


+
+
+
+


-
+


-
+












+
+
+
-
+




-
+


-
+


-
+

-
+






-
+
+







-
+






+








-
+






+







         rBefore+ONE_SECOND);
      zCirca = 0;
      url_add_parameter(&url, "c", 0);
    }else if( rCirca>0.0 ){
      Blob sql2;
      blob_init(&sql2, blob_sql_text(&sql), -1);
      blob_append_sql(&sql2,
          " AND event.mtime<=%f ORDER BY event.mtime DESC", rCirca);
          " AND event.mtime>=%f ORDER BY event.mtime ASC", rCirca);
      if( nEntry>0 ){
        blob_append_sql(&sql2," LIMIT %d", (nEntry+1)/2);
        nEntry -= (nEntry+1)/2;
      }
      if( PB("showsql") ){
         @ <pre>%h(blob_sql_text(&sql2))</pre>
      }
      db_multi_exec("%s", blob_sql_text(&sql2));
      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 ASC",
          " 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 %h", n, zEType, zPlural, 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", 
      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_DISJOINT;
      tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS;
    }
    if( renameOnly ){
      blob_appendf(&desc, " that contain filename changes");
      tmFlags |= TIMELINE_DISJOINT|TIMELINE_FRENAMES;
      tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS;
    }
    if( forkOnly ){
      blob_appendf(&desc, " associated with forks");
      tmFlags |= TIMELINE_DISJOINT;
    }
    if( bisectOnly ){
      blob_appendf(&desc, " in the most recent bisect");
    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_DISJOINT;
      tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS;
    }
    if( zTagSql ){
      if( matchStyle==MS_EXACT ){
      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_DISJOINT;
      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 />",
        blob_appendf(&desc, " occurring between %h and %h.<br>",
                     zAfter, zBefore);
      }else{
        blob_appendf(&desc, " occurring on or after %h.<br />", zAfter);
        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);
      blob_appendf(&desc, " occurring on or before %h.<br>", zBefore);
    }else if( rCirca>0.0 ){
      blob_appendf(&desc, " occurring around %h.<br />", zCirca);
      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"
        "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);
        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);
        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);
        }
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
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







-
+
+

-
+
+



-
-
+
+


+
+
+
-
+
+



+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+








+
-
+

+
-
+
+


+
-
+

+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



















-
+




+
+
+
+

-
+
+








+
+
+
+
+
+
+














+
+
+
+
+













-
+





+
-
+







  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", 0, 0));
    style_submenu_element("Basic", "%s",
        url_render(&url, "advm", "0", "udc", "1"));
  }else{
    style_submenu_element("Advanced", "%s", url_render(&url, "advm", "1", 0, 0));
    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);
    if( r>0.0 ) selectedRid = timeline_add_divider(r);
    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*/");
    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>
      @ <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)\
    @ %z(chref("button","%z",zNewerButton))More&nbsp;&uarr;</a>
    @ &nbsp;&uarr;</a>
  }
  cgi_check_for_malice();
  www_print_timeline(&q, tmFlags, zThisUser, zThisTag, selectedRid, 0);
  www_print_timeline(&q, tmFlags, zThisUser, zThisTag, zBrName,
                     selectedRid, secondaryRid, 0);
  db_finalize(&q);
  if( zOlderButton ){
    @ %z(chref("button","%s",zOlderButton))%h(zOlderButtonLabel)\
    @ %z(chref("button","%z",zOlderButton))More&nbsp;&darr;</a>
    @ &nbsp;&darr;</a>
  }
  document_emit_js(/*handles pikchrs rendered above*/);
  style_footer();
  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 and user
**    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, int verboseFlag){
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( fossil_strnicmp(zDate, zPrevDate, 10) ){
    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]);
      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;
2213
2214
2215
2216
2217
2218
2219



















2220
2221
2222




















2223
2224
2225
2226
2227
2228
2229
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







      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);
    /* record another X lines */
    nLine += comment_print(zFree, zCom, 9, width, g.comFmtFlags);
      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,"
2247
2248
2249
2250
2251
2252
2253



2254

2255
2256
2257
2258
2259
2260
2261
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363







+
+
+

+







          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{
2283
2284
2285
2286
2287
2288
2289
2290









2291
2292
2293
2294
2295
2296
2297
3385
3386
3387
3388
3389
3390
3391

3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407







-
+
+
+
+
+
+
+
+
+







    @                  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
    @   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'
  ;
2313
2314
2315
2316
2317
2318
2319

2320
2321
2322
2323
2324
2325
2326
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437







+







/*
** 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
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
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



+


+
-
+




+





-
-
-
+
+
+
-
-
+

















+
+












+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







** 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
**   --offset P           skip P changes
**                        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 <num>     Width of lines (default is to auto-detect). Must be
**                        >20 or 0 (= no limit, resulting in a single line per
**                        entry).
**   -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
**   -R REPO_FILE         Specifies the repository db to use. Default is
**                        the current checkout's repository.
**                        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);
2429
2430
2431
2432
2433
2434
2435
2436

2437
2438
2439
2440
2441
2442
2443
3583
3584
3585
3586
3587
3588
3589

3590
3591
3592
3593
3594
3595
3596
3597







-
+







      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");
            "?-W|--width WIDTH? ?-p|--path PATH?");
    }
    if( '-' != *g.argv[3] ){
      zOrigin = g.argv[3];
    }else{
      zOrigin = "now";
    }
  }else if( g.argc==3 ){
2451
2452
2453
2454
2455
2456
2457
2458

2459
2460
2461
2462
2463
2464
2465
3605
3606
3607
3608
3609
3610
3611

3612
3613
3614
3615
3616
3617
3618
3619







-
+







  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 checkout to use 'current'");
      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");
2500
2501
2502
2503
2504
2505
2506
2507

2508
2509
2510
2511
2512
2513
2514
3654
3655
3656
3657
3658
3659
3660

3661
3662
3663
3664
3665
3666
3667
3668







-
+







  /* 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);
      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 ){
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
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+











-
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







        "(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, verboseFlag);
  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?
2645
2646
2647
2648
2649
2650
2651
2652

2653
3894
3895
3896
3897
3898
3899
3900

3901
3902







-
+

  }
  db_finalize(&q);
  if( cnt==0 ){
    @ <p>No timewarps in this repository</p>
  }else{
    @ </tbody></table></div>
  }
  style_footer();
  style_finish_page();
}

Changes to src/tkt.c.

21
22
23
24
25
26
27
28

29
30
31
32
33
34

35
36
37
38
39



40
41
42
43





44
45
46
47
48
49
50
21
22
23
24
25
26
27

28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59







-
+






+





+
+
+




+
+
+
+
+







#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
** used.  The internal-use fields begin with "tkt_".
** 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);
67
68
69
70
71
72
73
74

75
76
77

78
79
80
81
82
83
84
85







86
87
88

89
90
91
92
93

















94
95
96
97
98
99





100
101

102
103
104
105
106
107
108

109
110
111
112
113
114

115
116
117











118
119
120
121
122
123
124
76
77
78
79
80
81
82

83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133

134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177







-
+



+








+
+
+
+
+
+
+



+





+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





-
+
+
+
+
+


+







+






+



+
+
+
+
+
+
+
+
+
+
+







** 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;
  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,"tkt_rid")==0 ) haveTicketChngRid = 1;
      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.
177
178
179
180
181
182
183










184
185
186
187
188
189
190







191
192
193
194
195





196
197
198

199
200
201
202
203
204
205
206
207
208
209
210
211
212

213
214
215
216


217
218
219
220
221
222

223
224
225


226
227




228
229
230
231
232
233
234
235

236
237
238
239






240
241
242



243
244
245






246
247
248

249
250
251

252
253




254
255
256
257
258
259
260
261
262
263
264
265
266
267

268
269


270
271
272
273



274
275
276











277
278
279
280
281






























282
283
284
285
286
287
288
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263


264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285

286

287


288
289
290
291
292
293


294

295

296
297
298

299
300
301
302
303
304
305





306
307
308


309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331

332
333
334

335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354

355
356

357
358
359
360


361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419







+
+
+
+
+
+
+
+
+
+







+
+
+
+
+
+
+



-
-
+
+
+
+
+



+













-
+
-

-
-
+
+




-
-
+
-

-
+
+

-
+
+
+
+



-
-
-
-
-
+


-
-
+
+
+
+
+
+



+
+
+



+
+
+
+
+
+


-
+


-
+


+
+
+
+













-
+

-
+
+


-
-
+
+
+



+
+
+
+
+
+
+
+
+
+
+





+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







  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, int rid, int tktid){
  Blob sql1, sql2, sql3;
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( nField );
  aUsed = fossil_malloc_zero( nField );
  memset(aUsed, 0, nField);
  for(i=0; i<p->nField; i++){
    const char *zName = p->aField[i].zName;
    const char *zBaseName = zName[0]=='+' ? zName+1 : zName;
    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 ){
      const char *zUsedByName = zName;
      if( zUsedByName[0]=='+' ){
      if( zName[0]=='+' ){
        zUsedByName++;
        blob_append_sql(&sql1,", \"%w\"=coalesce(\"%w\",'') || %Q",
                        zUsedByName, zUsedByName, p->aField[i].zValue);
                        zBaseName, zBaseName, p->aField[i].zValue);
        /* when appending keep "baseline for ..." unchanged */
      }else{
        blob_append_sql(&sql1,", \"%w\"=%Q", zUsedByName, p->aField[i].zValue);
        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 ){
      const char *zUsedByName = zName;
      if( zUsedByName[0]=='+' ){
        zUsedByName++;
      }
      blob_append_sql(&sql2, ",\"%w\"", zUsedByName);
      blob_append_sql(&sql2, ",\"%w\"", zBaseName);
      blob_append_sql(&sql3, ",%Q", p->aField[i].zValue);
    }
    if( rid>0 ){
      wiki_extract_links(p->aField[i].zValue, rid, 1, p->rDate, i==0, 0);
    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 ){
  if( blob_size(&sql2)>0 || haveTicketChngRid || haveTicketChngUser ){
    int fromTkt = 0;
    if( haveTicketChngRid ){
      blob_append(&sql2, ",tkt_rid", -1);
      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",
                     "SELECT %d,:mtime%s FROM ticket WHERE tkt_id=%d%s",
                     blob_sql_text(&sql2), tktid,
                     blob_sql_text(&sql3), 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)",
                     blob_sql_text(&sql2), tktid, blob_sql_text(&sql3));
                     "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.
*/
309
310
311
312
313
314
315
316

317

318
319
320
321
322
323

324
325
326
327
328



329
330
331
332
333
334

335
336
337
338
339
340

















341
342
343
344
345
346
347
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







-
+

+





-
+





+
+
+





-
+






+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







** Rebuild an entire entry in the TICKET table
*/
void ticket_rebuild_entry(const char *zTktUuid){
  char *zTag = mprintf("tkt-%s", zTktUuid);
  int tagid = tag_findid(zTag, 1);
  Stmt q;
  Manifest *pTicket;
  int tktid;
  int 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);
  search_doc_touch('t', tktid, 0);
  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);
      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){
357
358
359
360
361
362
363
364
365



















































































































366
367
368
369

370
371
372
373
374
375

376
377
378
379
380
381


382
383
384
385
386
387
388
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









+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



-
+






+






+
+







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){
  const char *zSql;
  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){
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
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







-
+


+
+
+





+




-
+


-
-
-
-
+
+
+
+
+

-
+


-
-
+
+






+

+
+
+
+
+
+
+
+
+
+
+
+
+
-
+






-
+
+

-
+





-
+


-
+







    @ mUsed = %d(aField[i].mUsed);
  }
  @ </ul></div>
}

/*
** WEBPAGE: tktview
** URL:  tktview?name=UUID
** 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", "%s/tktedit?name=%T", g.zTop, PD("name",""));
    style_submenu_element("Edit", "%R/tktedit/%T", PD("name",""));
  }
  if( g.perm.Hyperlink ){
    style_submenu_element("History", "%s/tkthistory/%T", g.zTop, zUuid);
    style_submenu_element("Timeline", "%s/tkttimeline/%T", g.zTop, zUuid);
    style_submenu_element("Check-ins", "%s/tkttimeline/%T?y=ci", g.zTop, zUuid);
  }
    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", "%s/tktnew", g.zTop);
    style_submenu_element("New Ticket", "%R/tktnew");
  }
  if( g.anon.ApndTkt && g.anon.Attach ){
    style_submenu_element("Attach", "%s/attachadd?tkt=%T&from=%s/tktview/%t",
        g.zTop, zUuid, g.zTop, zUuid);
    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);
  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);
  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);
  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>");
    attachment_list(zFullName, "<hr><h2>Attachments:</h2><ul>");
  }

  style_footer();
  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
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
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







-
+


















+
+




+









+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







-
+







){
  int idx;

  if( argc!=3 ){
    return Th_WrongNumArgs(interp, "append_field FIELD STRING");
  }
  if( g.thTrace ){
    Th_Trace("append_field %#h {%#h}<br />\n",
    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_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d);", rid);
    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{
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
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







-
+


-
+



-
+
+
+
+









+





+











-
+




+


+



















-
+










-
+
-



-
+


-
+
+

+
+
-
+


















+






+


-
+






-




+
+
+
+
+
+
+
+
+
+
+
+
+
+
+




-
+

-
+




-
-
+
+







static int submitTicketCmd(
  Th_Interp *interp,
  void *pUuid,
  int argc,
  const char **argv,
  int *argl
){
  char *zDate;
  char *zDate, *aUsed;
  const char *zUuid;
  int i;
  int nJ = 0;
  int nJ = 0, rc = TH_OK;
  Blob tktchng, cksum;
  int needMod;

  login_verify_csrf_secret();
  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
       || strlen(aField[i].zValue)!=nValue
       ||(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);
    return TH_OK;
    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 />
    @ <hr>
    return TH_OK;
  }else{
    if( g.thTrace ){
      Th_Trace("submit_ticket {\n<blockquote><pre>\n%h\n</pre></blockquote>\n"
               "}<br />\n",
               "}<br>\n",
         blob_str(&tktchng));
    }
    ticket_put(&tktchng, zUuid, needMod);
    ticket_put(&tktchng, zUuid, aUsed, needMod);
    rc = ticket_change(zUuid);
  }
  finish:
    fossil_free( aUsed );
  return ticket_change(zUuid);
    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);
  if( g.thTrace ) Th_Trace("BEGIN_TKTNEW<br>\n", -1);
  ticket_init();
  initializeVariablesFromCGI();
  getAllTicketFields();
  initializeVariablesFromDb();
  if( g.zPath[0]=='d' ) showAllFields();
  form_begin(0, "%R/%s", g.zPath);
  login_insert_csrf_secret();
  if( P("date_override") && g.perm.Setup ){
    @ <input type="hidden" name="date_override" value="%h(P("date_override"))">
  }
  zScript = ticket_newpage_code();
  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( g.thTrace ) Th_Trace("BEGIN_TKTNEW_SCRIPT<br>\n", -1);
  if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zNewUuid ){
    cgi_redirect(mprintf("%s/tktview/%s", g.zTop, zNewUuid));
    cgi_redirect(mprintf("%R/tktview/%s", zNewUuid));
    return;
  }
  captcha_generate(0);
  @ </form>
  if( g.thTrace ) Th_Trace("END_TKTVIEW<br />\n", -1);
  style_footer();
  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.
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
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







-
+

+




-
+






-
+





-
+


-
+






-
+
-





-
+

-
+




-
-
+
+







  login_check_credentials();
  if( !g.perm.ApndTkt && !g.perm.WrTkt ){
    login_needed(g.anon.ApndTkt || g.anon.WrTkt);
    return;
  }
  zName = P("name");
  if( P("cancel") ){
    cgi_redirectf("tktview?name=%T", zName);
    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_footer();
    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_footer();
    style_finish_page();
    return;
  }
  if( nRec>1 ){
    @ <span class="tktError">%d(nRec) tickets begin with:
    @ "%h(zName)"</span>
    style_footer();
    style_finish_page();
    return;
  }
  if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1);
  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)" />
  @ <input type="hidden" name="name" value="%s(zName)">
  login_insert_csrf_secret();
  zScript = ticket_editpage_code();
  Th_Store("login", login_name());
  Th_Store("date", db_text(0, "SELECT datetime('now')"));
  Th_CreateCommand(g.interp, "append_field", appendRemarkCmd, 0, 0);
  Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, (void*)&zName,0);
  if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT_SCRIPT<br />\n", -1);
  if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT_SCRIPT<br>\n", -1);
  if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zName ){
    cgi_redirect(mprintf("%s/tktview/%s", g.zTop, zName));
    cgi_redirect(mprintf("%R/tktview/%s", zName));
    return;
  }
  captcha_generate(0);
  @ </form>
  if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1);
  style_footer();
  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.
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
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









+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+


+
+
+
+


-

-

-












+
-
+
-
+

-
+

-
-
+
+





+







-
+


-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+




-
+

-
+
+
+
+
+
+
+
+
+
+
+







+








-
-
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+

+





-
+

+
+
+
+
+
+
+
+
+














-
+






-
+

-



-
-
-
+
+
+
+

-
-
+


-
+







-
-
+
+




+
+
+
+
+
+
+
+
-
+
+



+





-
+
+

















+
+
+

-
+
+
+
+
+
+

-
-
-
-
-
-
-
-

+


-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+











-
+




-
+







                       "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?name=TICKETUUID&y=TYPE
** 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){
  Stmt q;
  char *zTitle;
  char *zSQL;
  const char *zUuid;
  char *zFullUuid;
  int tagid;
  char zGlobPattern[50];
  const char *zType;

  login_check_credentials();
  if( !g.perm.Hyperlink || !g.perm.RdTkt ){
    login_needed(g.anon.Hyperlink && g.anon.RdTkt);
    return;
  }
  zUuid = PD("name","");
  zType = PD("y","a");
  if( zType[0]!='c' ){
    if( g.perm.Read ){
    style_submenu_element("Check-ins", "%s/tkttimeline?name=%T&y=ci",
      style_submenu_element("Check-ins", "%R/tkttimeline/%T?y=ci", zUuid);
       g.zTop, zUuid);
    }
  }else{
    style_submenu_element("Timeline", "%s/tkttimeline?name=%T", g.zTop, zUuid);
    style_submenu_element("Timeline", "%R/tkttimeline/%T", zUuid);
  }
  style_submenu_element("History", "%s/tkthistory/%s", g.zTop, zUuid);
  style_submenu_element("Status", "%s/info/%s", g.zTop, 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_footer();
    style_finish_page();
    return;
  }
  zFullUuid = db_text(0, "SELECT substr(tagname, 5) FROM tag WHERE tagid=%d",
                         tagid);
  tkt_draw_timeline(tagid, zType);
  if( zType[0]=='c' ){
    zSQL = mprintf(
         "%s AND event.objid IN "
         "   (SELECT srcid FROM backlink WHERE target GLOB '%.4s*' "
                                         "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 srcid 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,
                     0, 0, 0, 0);
  db_finalize(&q);
  style_footer();
  style_finish_page();
}

/*
** WEBPAGE: tkthistory
** URL: /tkthistory?name=TICKETUUID
** URL: /tkthistory/TICKETUUID
**
** Show the complete change history for a single ticket
** 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", "%s/info/%s", g.zTop, zUuid);
  style_submenu_element("Check-ins", "%s/tkttimeline?name=%s&y=ci",
  style_submenu_element("Status", "%R/info/%s", zUuid);
  if( g.perm.Read ){
    style_submenu_element("Check-ins", "%R/tkttimeline/%s?y=ci", zUuid);
    g.zTop, zUuid);
  style_submenu_element("Timeline", "%s/tkttimeline?name=%s", g.zTop, zUuid);
  if( P("plaintext")!=0 ){
    style_submenu_element("Formatted", "%R/tkthistory/%s", zUuid);
  }else{
    style_submenu_element("Plaintext", "%R/tkthistory/%s?plaintext", 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_footer();
    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
  );
  while( db_step(&q)==SQLITE_ROW ){
  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>
      @ <ol class="tkt-changes">
    }
    nChng++;
    if( zFile!=0 ){
      const char *zSrc = db_column_text(&q, 3);
      const char *zUser = db_column_text(&q, 5);
      if( zSrc==0 || zSrc[0]==0 ){
        @
        @ <li><p>Delete attachment "%h(zFile)"
      @
      @ <li id="%S(zChngUuid)"><p><span>
      if( zSrc==0 || zSrc[0]==0 ){
        @ Delete attachment "%h(zFile)"
      }else{
        @
        @ <li><p>Add attachment
        @ Add attachment
        @ "%z(href("%R/artifact/%!S",zSrc))%s(zFile)</a>"
      }
      @ [%z(href("%R/artifact/%!S",zChngUuid))%S(zChngUuid)</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><p>Ticket change
        @ [%z(href("%R/artifact/%!S",zChngUuid))%S(zChngUuid)</a>]
        @ <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");
          ticket_output_change_artifact(pTicket, "a", nChng, aLastVal);
        }
      }
      manifest_destroy(pTicket);
    }
    @ </li>
  }
  db_finalize(&q);
  if( nChng ){
    @ </ol>
  }
  style_footer();
  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, const char *zListType){
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;
  int wikiFlags = WIKI_NOBADLINKS;
  const char *zBlock = "<blockquote>";
  const char *zEnd = "</blockquote>";
  if( P("plaintext")!=0 ){
    wikiFlags |= WIKI_LINKSONLY;
    zBlock = "<blockquote><pre class='verbatim'>";
    zEnd = "</pre></blockquote>";
  }
  if( zListType==0 ) zListType = "1";
  getAllTicketFields();
  @ <ol type="%s(zListType)">
  for(i=0; i<pTkt->nField; i++){
    Blob val;
    const char *z;
    z = pTkt->aField[i].zName;
    blob_set(&val, pTkt->aField[i].zValue);
    if( z[0]=='+' ){
      @ <li>Appended to %h(&z[1]):%s(zBlock)
      wiki_convert(&val, 0, wikiFlags);
      @ %s(zEnd)</li>
    }else if( blob_size(&val)>50 || contains_newline(&val) ){
      @ <li>Change %h(z) to:%s(zBlock)
    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)
      wiki_convert(&val, 0, wikiFlags);
      @ %s(zEnd)</li>
    }else{
      @ <li>Change %h(z) to "%h(blob_str(&val))"</li>
    }
    blob_reset(&val);
          @ </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?
** > fossil ticket show (REPORTTITLE|REPORTNR) ?TICKETFILTER? ?OPTIONS?
**
**     Options:
**       -l|--limit LIMITCHAR
**       -q|--quote
**       -R|--repository FILE
**       -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
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
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







-
-
+
+



-
-
+
+



-
-
+
+















-
+



-
+












-
+







**     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
** > 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
** > 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?
** > 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?
** > fossil ticket add FIELD VALUE ?FIELD VALUE .. ? ?-q|--quote?
**
**     Like set, but create a new ticket with the given values.
**
**   %fossil ticket history TICKETUUID
** > 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 checkout */
  /* 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";
1202
1203
1204
1205
1206
1207
1208

1209
1210
1211
1212
1213
1214
1215
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663







+







        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{
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
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







-
+

















-
+







            z++;
          }else{
            fossil_print("  Change ");
          }
          fossil_print("%h: ",z);
          if( blob_size(&val)>50 || contains_newline(&val)) {
                  fossil_print("\n    ");
                  comment_print(blob_str(&val),0,4,-1,g.comFmtFlags);
                  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 commandline and assign fields in the aField[].zValue array */
      /* 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++];
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
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







+













+



+






+









-
+
+





+







          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, ticket_need_moderation(1))==0 ){
      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 */
1433
1434
1435
1436
1437
1438
1439

1440

1441
1442
1443
1444







1445
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898


1899
1900
1901
1902
1903
1904
1905
1906







+

+


-
-
+
+
+
+
+
+
+

/*
** 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));
  search_screen(SRCH_TKT, 0);
  style_footer();
  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.

52
53
54
55
56
57
58
59

60
61
62
63
64
65
66
52
53
54
55
56
57
58

59
60
61
62
63
64
65
66







-
+







  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_footer();
  style_finish_page();
}

/*
** NOTE:  When changing the table definition below, also change the
** equivalent definition found in schema.c.
*/
/* @-comment: ** */
84
85
86
87
88
89
90

91
92
93
94
95
96
97
98
99
100
101


102
103

104
105
106
107
108
109
110
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101

102
103
104

105
106
107
108
109
110
111
112







+










-
+
+

-
+







@   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
** Return the ticket table definition in heap-allocated
** memory owned by the caller.
*/
const char *ticket_table_schema(void){
char *ticket_table_schema(void){
  return db_get("ticket-table", zDefaultTicketTable);
}

/*
** Common implementation for the ticket setup editor pages.
*/
static void tktsetup_generic(
120
121
122
123
124
125
126

127
128
129
130
131
132
133
134

135
136

137
138

139
140
141

142
143
144
145
146
147

148
149
150
151
152

153
154
155
156
157
158
159



160
161
162

163
164
165
166
167

168
169
170
171
172
173
174
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139

140


141
142
143

144
145

146
147
148

149
150
151
152
153

154
155
156
157
158



159
160
161
162
163

164
165
166
167
168

169
170
171
172
173
174
175
176







+








+

-
+
-
-
+


-
+

-



-
+




-
+




-
-
-
+
+
+


-
+




-
+







  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 ){
  if( P("clear")!=0 && cgi_csrf_safe(2) ){
    login_verify_csrf_secret();
    db_unset(zDbField, 0);
    db_unset(zDbField/*works-like:"x"*/, 0);
    if( xRebuild ) xRebuild();
    cgi_redirect("tktsetup");
  }else if( isSubmit ){
  }else if( isSubmit && cgi_csrf_safe(2) ){
    char *zErr = 0;
    login_verify_csrf_secret();
    if( xText && (zErr = xText(z))!=0 ){
      @ <p class="tktsetupError">ERROR: %h(zErr)</p>
    }else{
      db_set(zDbField, z, 0);
      db_set(zDbField/*works-like:"x"*/, z, 0);
      if( xRebuild ) xRebuild();
      cgi_redirect("tktsetup");
    }
  }
  @ <form action="%s(g.zTop)/%s(g.zPath)" method="post"><div>
  @ <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" />
  @ <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 />
  @ <hr>
  @ <h2>Default %s(zTitle)</h2>
  @ <blockquote><pre>
  @ %h(zDfltValue)
  @ </pre></blockquote>
  style_footer();
  style_finish_page();
}

/*
** WEBPAGE: tktsetup_tab
** Administrative page for defining the "ticket" table used
** to hold ticket information.
*/
296
297
298
299
300
301
302
303

304
305
306
307
308
309


310
311
312
313
314
315
316
317
318
319
320
321
322
323
324


325
326
327
328
329
330
331
332
333
334
335
336
337

338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353

354
355
356
357
358
359
360
361
362
363
364
365
366
367


368
369
370

371
372
373
374
375

376
377
378
379
380
381


382
383
384
385
386
387
388
389

390
391
392
393
394

395
396
397
398
399
400
401
402

403
404
405
406
407
408
409
410
411

412
413
414
415
416
417
418
298
299
300
301
302
303
304

305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326


327
328
329
330
331
332
333
334
335
336
337
338
339
340

341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356

357

358
359
360
361
362
363
364
365
366
367
368


369
370
371
372

373
374
375
376
377

378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393

394
395
396
397
398

399
400
401
402
403
404
405
406

407
408
409
410
411
412
413
414
415

416
417
418
419
420
421
422
423







-
+






+
+













-
-
+
+












-
+















-
+
-











-
-
+
+


-
+




-
+






+
+







-
+




-
+







-
+








-
+







    0,
    30
  );
}

static const char zDefaultNew[] =
@ <th1>
@   if {![info exists mutype]} {set mutype {[links only]}}
@   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>" />
@ 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>" />
@ <input type="text" name="foundin" size="20" value="$<foundin>">
@ </td>
@ <td align="left">In what version or build number do you observe
@ the problem?</td>
@ </tr>
@
@ <tr>
@ <td align="right">Severity:</td>
@ <td align="left"><th1>combobox severity $severity_choices 1</th1></td>
@ <td align="left">How debilitating is the problem?  How badly does the problem
@ affect the operation of the product?</td>
@ </tr>
@
@ <tr>
@ <td align="right">EMail:</td>
@ <td align="left">
@ <input type="text" name="private_contact" value="$<private_contact>"
@ <input name="private_contact" value="$<private_contact>" size="30">
@  size="30" />
@ </td>
@ <td align="left"><u>Not publicly visible</u>
@ Used by developers to contact you with questions.</td>
@ </tr>
@
@ <tr>
@ <td colspan="3">
@ Enter a detailed description of the problem.
@ For code defects, be sure to provide details on exactly how
@ the problem can be reproduced.  Provide as much detail as
@ possible.  Format:
@ <th1>combobox mutype {Wiki HTML {Plain Text} {[links only]}} 1</th1>
@ <br />
@ <th1>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 />
@  wrap="virtual" class="wikiedit">$<icomment></textarea><br>
@ </tr>
@
@ <th1>enable_output [info exists preview]</th1>
@ <tr><td colspan="3">
@ Description Preview:<br /><hr />
@ 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>
@ <hr></td></tr>
@ <th1>enable_output 1</th1>
@
@ <tr>
@ <td><td align="left">
@ <input type="submit" name="preview" value="Preview" />
@ <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" />
@ <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" />
@ <input type="submit" name="cancel" value="Cancel">
@ </td>
@ <td>Abandon and forget this ticket</td>
@ </tr>
@ </table>
;

/*
441
442
443
444
445
446
447
448

449
450


451
452
453

454
455
456

457
458
459
460
461
462
463
446
447
448
449
450
451
452

453
454
455
456
457
458


459


460
461
462
463
464
465
466
467
468







-
+


+
+

-
-
+
-
-

+







    0,
    40
  );
}

static const char zDefaultView[] =
@ <table cellpadding="5">
@ <tr><td class="tktDspLabel">Ticket&nbsp;UUID:</td>
@ <tr><td class="tktDspLabel">Ticket&nbsp;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 "<td class='tktDspValue' colspan='3'>$tkt_uuid "
@     html "($tkt_id)</td></tr>\n"
@     html " ($tkt_id)"
@   } else {
@     html "<td class='tktDspValue' colspan='3'>$tkt_uuid</td></tr>\n"
@   }
@   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"
@   }
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
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







-
+





+




-
+
+






+
+







@ 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"
@     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:\n"
@   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"
@   }
@ }
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
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







-
+





+
+













-
+







    0,
    40
  );
}

static const char zDefaultEdit[] =
@ <th1>
@   if {![info exists mutype]} {set mutype {[links only]}}
@   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" />
@ <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>
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
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







-
+




-
+




-
+

-
+






-
+






+
+







-
+





-
+







-
+







-
+







@ <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>" />
@    value="$<private_contact>">
@   </td></tr>
@ <th1>enable_output 1</th1>
@
@ <tr><td class="tktDspLabel">Version&nbsp;Found&nbsp;In:</td><td>
@ <input type="text" name="foundin" size="50" value="$<foundin>" />
@ <input type="text" name="foundin" size="50" value="$<foundin>">
@ </td></tr>
@
@ <tr><td colspan="2">
@   Append Remark with format
@   <th1>combobox mutype {Wiki HTML {Plain Text} {[links only]}} 1</th1>
@  <th1>combobox mutype {HTML {[links only]} Markdown {Plain Text} Wiki} 1</th1>
@   from
@   <input type="text" name="username" value="$<username>" size="30" />:<br />
@   <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 />
@ 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 />
@ <hr>
@ </td></tr>
@ <th1>enable_output 1</th1>
@
@ <tr>
@ <td align="right">
@ <input type="submit" name="preview" value="Preview" />
@ <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" />
@ <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" />
@ <input type="submit" name="cancel" value="Cancel">
@ </td>
@ <td>Abandon this edit</td>
@ </tr>
@
@ </table>
;

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
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







+


-
+


-
+



-
+
+

-
+



-
+
+

-
+



-
+
+

-
+

-
-
+
+



-
+


    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="%s(g.zTop)/tktsetup_timeline" method="post"><div>
  @ <form action="%R/tktsetup_timeline" method="post"><div>
  login_insert_csrf_secret();

  @ <hr />
  @ <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.</p>
  @ return the title of the ticket for display purposes.
  @ (Property: ticket-title-expr)</p>

  @ <hr />
  @ <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.</p>
  @ status in human-readable form.  Case sensitive.
  @ (Property: ticket-status-column)</p>

  @ <hr />
  @ <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.</p>
  @ the ticket is closed.
  @ (Property: ticket-closed-expr)</p>

  @ <hr />
  @ <hr>
  @ <p>
  @ <input type="submit"  name="submit" value="Apply Changes" />
  @ <input type="submit" name="setup" value="Cancel" />
  @ <input type="submit"  name="submit" value="Apply Changes">
  @ <input type="submit" name="setup" value="Cancel">
  @ </p>
  @ </div></form>
  db_end_transaction(0);
  style_footer();
  style_finish_page();

}

Deleted src/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
























































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
/*
** 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.
      */
      const char *zNewline = "\\n";
      int indent;
      int nC;
      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;
        for(nC=1; zLine[i+nC] && zLine[i+nC]!='('; nC++){}
        if( zLine[i+nC]!='(' || !isalpha(zLine[i+nC-1]) ) continue;
        while( --nC ) zOut[j++] = zLine[++i];
        zArg[nArg++] = ',';
        k = 0; i++;
        while( (c = zLine[i])!=0 ){
          zArg[nArg++] = c;
          if( c==')' ){
            k--;
            if( k==0 ) break;
          }else if( c=='(' ){
            k++;
          }
          i++;
        }
      }
      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;
}

Changes to src/undo.c.

50
51
52
53
54
55
56
57

58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73


74

75
76
77
78
79
80
81
50
51
52
53
54
55
56

57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75

76
77
78
79
80
81
82
83







-
+
















+
+
-
+







    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);
    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(&current, zFullname, RepoFILE);
      new_exe = file_isexe(0,0);
    }else{
      blob_zero(&current);
      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 */
    if( old_exists ){
    }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);
162
163
164
165
166
167
168
169

170
171
172
173
174
175
176
177
178
179
180
181
182
183

184
185
186
187
188
189
190
164
165
166
167
168
169
170

171
172
173
174
175
176
177
178
179
180
181
182
183
184

185
186
187
188
189
190
191
192







-
+













-
+







        "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_lset_int("checkout", ncid);
  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_multi_exec(zSql /*works-like:""*/);
  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.
233
234
235
236
237
238
239
240

241
242
243
244
245
246
247
235
236
237
238
239
240
241

242
243
244
245
246
247
248
249







-
+







    @   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_multi_exec(zSql/*works-like:""*/);
  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;
}

265
266
267
268
269
270
271
272

273
274
275
276
277
278
279
267
268
269
270
271
272
273

274
275
276
277
278
279
280
281







-
+







** 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_panic("failed to save undo information for path: %s",
    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
421
422
423
424
425
426
427
428
429
430
431
432
433
434










435
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
423
424
425
426
427
428
429







430
431
432
433
434
435
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







-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+

-
-
+
+
+









+
+
+
+
+
+

-
+
+

+
+
+

-
+

-
+







/*
** COMMAND: undo
** COMMAND: redo*
**
** Usage: %fossil undo ?OPTIONS? ?FILENAME...?
**    or: %fossil redo ?OPTIONS? ?FILENAME...?
**
** Undo the changes to the working checkout caused by the most recent
** of the following operations:
**
**    (1) fossil update             (5) fossil stash apply
**    (2) fossil merge              (6) fossil stash drop
**    (3) fossil revert             (7) fossil stash goto
**    (4) fossil stash pop
** 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*)
**
** The "fossil clean" operation can also be undone; however, this is
** currently limited to files that are less than 10MiB in size.
** 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 checkout commands.
** 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
**   -n|--dry-run   Do not make changes, but show what would be done
**
** See also: commit, status
** 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";

Changes to src/unicode.c.

54
55
56
57
58
59
60
61

62
63
64
65
66
67
68
69
70







71
72
73
74
75
76
77
78
79
80

81
82
83

84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116



























117
118
119

120
121
122

123
124


125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143






















144
145
146
147
148
149
150
54
55
56
57
58
59
60

61
62
63







64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79

80
81
82

83
84
85
86
87
88
89



























90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118

119
120
121

122
123
124
125
126



















127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155







-
+


-
-
-
-
-
-
-
+
+
+
+
+
+
+









-
+


-
+






-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


-
+


-
+


+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







    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, 0x002D5802, 0x002D8802,
    0x002CF807, 0x002D1C02, 0x002D2C03, 0x002D5403, 0x002D8802,
    0x002DC001, 0x002E0801, 0x002EF805, 0x002F1803, 0x002F2804,
    0x002F5C01, 0x002FCC08, 0x00300005, 0x0030F807, 0x00311803,
    0x00312804, 0x00315402, 0x00318802, 0x0031FC01, 0x00320404,
    0x0032F001, 0x0032F807, 0x00331803, 0x00332804, 0x00335402,
    0x00338802, 0x00340004, 0x0034EC02, 0x0034F807, 0x00351803,
    0x00352804, 0x00353C01, 0x00355C01, 0x00358802, 0x0035E401,
    0x00360802, 0x00372801, 0x00373C06, 0x00375801, 0x00376008,
    0x0037C803, 0x0038C401, 0x0038D007, 0x0038FC01, 0x00391C09,
    0x00396802, 0x003AC401, 0x003AD006, 0x003AEC02, 0x003B2006,
    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, 0x006AC00F, 0x006C0005, 0x006CD011,
    0x006A8007, 0x006AA006, 0x006AC011, 0x006C0005, 0x006CD011,
    0x006D6823, 0x006E0003, 0x006E840D, 0x006F980E, 0x006FF004,
    0x00709014, 0x0070EC05, 0x0071F802, 0x00730008, 0x00734019,
    0x0073B401, 0x0073C803, 0x0073DC03, 0x0077003A, 0x0077EC05,
    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, 0x00AE6031, 0x00AF2835,
    0x00B39406, 0x00B3BC03, 0x00B3E404, 0x00B3F802, 0x00B5C001,
    0x00B5FC01, 0x00B7804F, 0x00B8C01F, 0x00BA001A, 0x00BA6C59,
    0x00BC00D6, 0x00BFC00C, 0x00C00005, 0x00C02019, 0x00C0A807,
    0x00C0D802, 0x00C0F403, 0x00C26404, 0x00C28001, 0x00C3EC01,
    0x00C64002, 0x00C6580A, 0x00C70024, 0x00C8001F, 0x00C8A81E,
    0x00C94001, 0x00C98020, 0x00CA2827, 0x00CB003F, 0x00CC0100,
    0x01370040, 0x02924037, 0x0293F802, 0x02983403, 0x0299BC10,
    0x029A7802, 0x029BC008, 0x029C0017, 0x029C8002, 0x029E2402,
    0x02A00801, 0x02A01801, 0x02A02C01, 0x02A08C09, 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,
    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, 0x0406400C, 0x04068001, 0x0407402E, 0x040B8001,
    0x040DD805, 0x040E7C01, 0x040F4001, 0x0415BC01, 0x04215C01,
    0x0421DC02, 0x04247C01, 0x0424FC01, 0x04280403, 0x04281402,
    0x04283004, 0x0428E003, 0x0428FC01, 0x04294009, 0x0429FC01,
    0x042B2001, 0x042B9402, 0x042BC007, 0x042CE407, 0x042E6404,
    0x04349004, 0x043D180B, 0x043D5405, 0x04400003, 0x0440E016,
    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,
    0x04471409, 0x04476C01, 0x04477403, 0x0448B013, 0x044AA401,
    0x0447140B, 0x04476C01, 0x04477403, 0x0448B013, 0x044AA401,
    0x044B7C0C, 0x044C0004, 0x044CEC02, 0x044CF807, 0x044D1C02,
    0x044D2C03, 0x044D5C01, 0x044D8802, 0x044D9807, 0x044DC005,
    0x0450D412, 0x04512C05, 0x04516C01, 0x04517402, 0x0452C014,
    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,
    0x0468040A, 0x0468CC07, 0x0468EC0D, 0x0469440B, 0x046A2813,
    0x046A7805, 0x0470BC08, 0x0470E008, 0x04710405, 0x0471C002,
    0x04724816, 0x0472A40E, 0x0474C406, 0x0474E801, 0x0474F002,
    0x0474FC07, 0x04751C01, 0x04762805, 0x04764002, 0x04764C05,
    0x047BCC06, 0x0491C005, 0x05A9B802, 0x05ABC006, 0x05ACC010,
    0x05AD1002, 0x05BA5C04, 0x05BD442E, 0x05BE3C04, 0x06F27008,
    0x074000F6, 0x07440027, 0x0744A4C0, 0x07480046, 0x074C0057,
    0x075B0401, 0x075B6C01, 0x075BEC01, 0x075C5401, 0x075CD401,
    0x075D3C01, 0x075DBC01, 0x075E2401, 0x075EA401, 0x075F0C01,
    0x0760028C, 0x076A6C05, 0x076A840F, 0x07800007, 0x07802011,
    0x07806C07, 0x07808C02, 0x07809805, 0x07A34007, 0x07A51007,
    0x07A57802, 0x07B2B001, 0x07B2C001, 0x07BBC002, 0x07C0002C,
    0x07C0C064, 0x07C2800F, 0x07C2C40F, 0x07C3040F, 0x07C34425,
    0x07C4405C, 0x07C5C03D, 0x07C7981D, 0x07C8402C, 0x07C90009,
    0x07C94002, 0x07C98006, 0x07CC03D5, 0x07DB800D, 0x07DBC00A,
    0x07DC0074, 0x07DE0059, 0x07E0000C, 0x07E04038, 0x07E1400A,
    0x07E18028, 0x07E2401E, 0x07E4000C, 0x07E4402F, 0x07E50031,
    0x07E5CC04, 0x07E5E801, 0x07E5F027, 0x07E6C00A, 0x07E70003,
    0x07E74030, 0x07E9800E, 0x38000401, 0x38008060, 0x380400F0,
    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 );
174
175
176
177
178
179
180
181

182
183
184
185
186
187
188
189
190
191
192
193
194
195












196

197

198
199
200











201
202
203
204
205
206










207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222

223


224
225
226
227
228
229
230
231
232
233
234
235
236
237


238
239
240
241
242
243
244
245
246
247
248
249
250

251
252
253
254
255
256
257
179
180
181
182
183
184
185

186
187
188
189
190
191









192
193
194
195
196
197
198
199
200
201
202
203
204
205

206



207
208
209
210
211
212
213
214
215
216
217






218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244

245
246
247
248
249
250
251
252
253
254
255
256
257
258


259
260
261
262
263
264
265
266
267
268
269
270
271
272

273
274
275
276
277
278
279
280







-
+





-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+

+
-
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
















+
-
+
+












-
-
+
+












-
+







** 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){
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,  3896,  3912,  3928,
     3968,  4008,  4040,  4106,  4138,  4170,  4202,  4234,
     4266,  4296,  4312,  4344,  4408,  4424,  4472,  4504,
     6148,  6198,  6264,  6280,  6360,  6429,  6505,  6529,
    61448, 61468, 61534, 61592, 61642, 61688, 61704, 61726,
    61784, 61800, 61836, 61880, 61914, 61948, 61998, 62122,
    62154, 62200, 62218, 62302, 62364, 62442, 62478, 62536,
    62554, 62584, 62604, 62640, 62648, 62656, 62664, 62730,
    62924, 63050, 63082, 63274, 63390,
     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 char aChar[] = {
  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',
    '\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',
    'u',  'g',  'k',  'o',  'j',  'g',  'n',  'a',  'e',  'i',  'o',  'r',
    'u',  's',  't',  'h',  'a',  'e',  'o',  'y',  '\0', '\0', '\0', '\0',
    '\0', '\0', '\0', '\0', 'a',  'b',  'd',  'd',  'e',  'f',  'g',  'h',
    'h',  'i',  'k',  'l',  'l',  'm',  'n',  'p',  'r',  'r',  's',  't',
    'u',  'v',  'w',  'w',  'x',  'y',  'z',  'h',  't',  'w',  'y',  'a',
    'e',  'i',  'o',  'u',  'y',
    '\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]);
  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 & (1 << (c-768))) :
      (mask1 & (1 << (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 bRemoveDiacritic){
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
268
269
270
271
272
273
274
275
276


277
278
279
280
281
282
283
284
285
286
287
288
289
290


291
292
293


294
295
296
297
298
299
300
301
302





303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320















321
322
323
324
325




326
327
328
329


330
331
332
333
334






335
336
337
338
339
340
341
342
343
344
345
346
347
348








349
350
351
352
353
354
355
291
292
293
294
295
296
297


298
299
300
301
302
303
304
305
306
307
308
309
310
311


312
313
314


315
316
317
318
319
320





321
322
323
324
325
326
327
328















329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344




345
346
347
348
349
350


351
352
353




354
355
356
357
358
359
360
361
362
363
364
365
366







367
368
369
370
371
372
373
374
375
376
377
378
379
380
381







-
-
+
+












-
-
+
+

-
-
+
+




-
-
-
-
-
+
+
+
+
+



-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
-
+
+
+
+


-
-
+
+

-
-
-
-
+
+
+
+
+
+







-
-
-
-
-
-
-
+
+
+
+
+
+
+
+







  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, 152, 1},
    {377, 1, 6},           {383, 140, 1},         {385, 52, 1},
    {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, 158, 1},
    {503, 170, 1},         {504, 1, 40},          {544, 146, 1},
    {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, 144, 1},         {574, 72, 1},          {577, 0, 1},
    {579, 142, 1},         {580, 30, 1},          {581, 32, 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, 176, 1},         {977, 178, 1},
    {981, 182, 1},         {982, 180, 1},         {984, 1, 24},
    {1008, 172, 1},        {1009, 174, 1},        {1012, 166, 1},
    {1013, 164, 1},        {1015, 0, 1},          {1017, 188, 1},
    {1018, 0, 1},          {1021, 146, 3},        {1024, 36, 16},
    {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, 186, 6},        {7296, 122, 1},
    {7297, 124, 1},        {7298, 126, 1},        {7299, 130, 2},
    {7301, 128, 1},        {7302, 132, 1},        {7303, 134, 1},
    {7304, 96, 1},         {7312, 138, 43},       {7357, 138, 3},
    {7680, 1, 150},        {7835, 168, 1},        {7838, 116, 1},
    {7840, 1, 96},         {7944, 186, 8},        {7960, 186, 6},
    {7976, 186, 8},        {7992, 186, 8},        {8008, 186, 6},
    {8025, 187, 8},        {8040, 186, 8},        {8072, 186, 8},
    {8088, 186, 8},        {8104, 186, 8},        {8120, 186, 2},
    {8122, 162, 2},        {8124, 184, 1},        {8126, 120, 1},
    {8136, 160, 4},        {8140, 184, 1},        {8152, 186, 2},
    {8154, 156, 2},        {8168, 186, 2},        {8170, 154, 2},
    {8172, 188, 1},        {8184, 148, 2},        {8186, 150, 2},
    {8188, 184, 1},        {8486, 118, 1},        {8490, 112, 1},
    {8491, 114, 1},        {8498, 12, 1},         {8544, 8, 16},
    {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, 108, 1},       {11363, 136, 1},
    {11364, 110, 1},       {11367, 1, 6},         {11373, 104, 1},
    {11374, 106, 1},       {11375, 100, 1},       {11376, 102, 1},
    {11378, 0, 1},         {11381, 0, 1},         {11390, 98, 2},
    {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, 94, 1},
    {42878, 1, 10},        {42891, 0, 1},         {42893, 86, 1},
    {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, 82, 1},
    {42926, 80, 1},        {42928, 90, 1},        {42929, 84, 1},
    {42930, 88, 1},        {42931, 68, 1},        {42932, 1, 6},
    {43888, 92, 80},       {65313, 14, 26},
    {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, 23231, 23254, 23256, 23275, 23278, 26672, 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,
   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 ){
375
376
377
378
379
380
381

382


383
384
385
386
387
388
389
401
402
403
404
405
406
407
408

409
410
411
412
413
414
415
416
417







+
-
+
+







    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 ){
    if( bRemoveDiacritic ) ret = unicode_remove_diacritic(ret);
      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;

Changes to src/unversioned.c.

15
16
17
18
19
20
21
22
23
24
25
26

27
28
29
30
31
32
33
34
15
16
17
18
19
20
21





22

23
24
25
26
27
28
29







-
-
-
-
-
+
-







**
*******************************************************************************
**
** This file contains code used to implement unversioned file interfaces.
*/
#include "config.h"
#include <assert.h>
#if defined(FOSSIL_ENABLE_MINIZ)
#  define MINIZ_HEADER_FILE_ONLY
#  include "miniz.c"
#else
#  include <zlib.h>
#include <zlib.h>
#endif
#include "unversioned.h"
#include <time.h>

/*
** SQL code to implement the tables needed by the unversioned.
*/
static const char zUnversionedInit[] =
60
61
62
63
64
65
66
67




68
69
70
71
72
73
74
55
56
57
58
59
60
61

62
63
64
65
66
67
68
69
70
71
72







-
+
+
+
+







** 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==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"
    );
83
84
85
86
87
88
89
90



91
92
93
94

95
96


97
98
99
100
101
102

103
104












105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124

125
126
127
128
129
130
131
81
82
83
84
85
86
87

88
89
90
91
92
93

94
95

96
97
98
99
100
101
102

103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136

137
138
139
140
141
142
143
144







-
+
+
+



-
+

-
+
+





-
+


+
+
+
+
+
+
+
+
+
+
+
+



















-
+







  }
  return zHash;
}

/*
** Initialize pContent to be the content of an unversioned file zName.
**
** Return 0 on success.  Return 1 if zName is not found.
** 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 = 1;
  int rc = 0;
  blob_init(pContent, 0, 0);
  db_prepare(&q, "SELECT encoding, content FROM unversioned WHERE name=%Q", zName);
  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 = 0;
    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)"
  );
  sha1sum_blob(pContent, &hash);
  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) ){
141
142
143
144
145
146
147
148

149
150
151
152
153

154
155
156
157
158
159
160
154
155
156
157
158
159
160

161
162
163
164
165

166
167
168
169
170
171
172
173







-
+




-
+







  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 SHA1 hash of a copy of this file on a remote
** 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 a older mtime
**    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,
185
186
187
188
189
190
191
192

193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210

211
212
213
214
215
216
217
198
199
200
201
202
203
204

205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222

223
224
225
226
227
228
229
230







-
+

















-
+







/*
** 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("dryrun","n",0)!=0 ){
  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: 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
237
238
239
240
241
242
243







244
245
246
247
248
249
250

251
252
253
254
255
256




257
258
259
260

261
262
263
264

265
266
267
268
269
270
271

272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288

289
290
291
292
293
294
295
296
297

298
299
300
301
302
303
304
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269

270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288

289
290
291
292
293

294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312

313
314
315
316
317
318
319
320

321
322
323
324
325
326
327
328
329







+
+
+
+
+
+
+






-
+






+
+
+
+




+



-
+




-


+
















-
+







-

+







**    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|--dryrun      Show what would have happened
**                              -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|--dryrun      Show what would have happened
**                              -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( memcmp(zCmd, "add", nCmd)==0 ){
  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);
    if( zAs && g.argc!=4 ) usage("add DISKFILE --as UVFILE");
    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]=='/' ){
313
314
315
316
317
318
319
320

321
322
323
324
325
326

327
328
329
330
331
332

333
334
335
336
337
338
339
340
341
342

343


344
345
346
347
348

349
350
351
352
353
354
355
356
357
358

359
360
361
362
363
364
365
366
367
368
369
370
371
372
373

374
375
376
377

378
379
380
381
382

383
384
385
386

387
388
389












390
391
392
393


394
395
396



397
398
399
400
401
402
403
404
405


406
407
408
409
410
411
412
338
339
340
341
342
343
344

345
346
347
348
349
350

351
352
353
354
355
356

357
358
359
360
361
362
363
364
365
366
367
368

369
370
371
372
373
374

375
376
377
378
379
380
381
382
383
384

385
386
387
388
389
390
391
392
393
394
395
396
397
398
399

400
401
402
403

404
405
406
407
408

409
410
411
412

413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431

432
433
434


435
436
437
438
439
440
441
442
443
444


445
446
447
448
449
450
451
452
453







-
+





-
+





-
+










+
-
+
+




-
+









-
+














-
+



-
+




-
+



-
+



+
+
+
+
+
+
+
+
+
+
+
+



-
+
+

-
-
+
+
+







-
-
+
+







      }
      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( memcmp(zCmd, "cat", nCmd)==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 ){
      if( unversioned_content(g.argv[i], &content)!=0 ){
        blob_write_to_file(&content, "-");
      }
      blob_reset(&content);
    }
    db_end_transaction(0);
  }else if( memcmp(zCmd, "edit", nCmd)==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 ){
    if( zEditor==0 ) fossil_fatal("no text editor - set the VISUAL env variable");
      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) ){
    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 \"%s\"", zEditor, 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( memcmp(zCmd, "export", nCmd)==0 ){
  }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) ){
    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( memcmp(zCmd, "hash", nCmd)==0 ){  /* undocumented */
  }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( memcmp(zCmd, "list", nCmd)==0 || memcmp(zCmd, "ls", nCmd)==0 ){
  }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 ORDER BY name");
        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 hash IS NOT NULL"
                       " ORDER BY name");
        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"
        "  ORDER BY 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)";
420
421
422
423
424
425
426

427
428



429
430
431
432
433


434
435

436















437
438
439
440
441
442
443
444
445
446

447
448
449
450
451

452
453
454
455
456
457
458
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







+
-
-
+
+
+



-
-
+
+

-
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+









-
+




-
+







           db_column_int(&q,3),
           db_column_text(&q,4),
           zNoContent
        );
      }
    }
    db_finalize(&q);
    sqlite3_free(zPattern);
  }else if( memcmp(zCmd, "revert", nCmd)==0 ){
    unsigned syncFlags = unversioned_sync_flags(SYNC_UNVERSIONED|SYNC_UV_REVERT);
  }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( memcmp(zCmd, "remove", nCmd)==0 || memcmp(zCmd, "rm", nCmd)==0
         || memcmp(zCmd, "delete", nCmd)==0 ){
  }else if( strncmp(zCmd, "remove", nCmd)==0 || strncmp(zCmd, "rm", nCmd)==0
         || strncmp(zCmd, "delete", nCmd)==0 ){
    int i;
    verify_all_options();
    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( memcmp(zCmd,"sync",nCmd)==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( memcmp(zCmd, "touch", nCmd)==0 ){
  }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]
482
483
484
485
486
487
488


489
490
491
492

493
494
495
496
497
498
499
540
541
542
543
544
545
546
547
548
549
550
551

552
553
554
555
556
557
558
559







+
+



-
+







  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_footer();
    style_finish_page();
    return;
  }
  if( PB("byage") ) zOrderBy = "mtime DESC";
  if( PB("showdel") ) showDel = 1;
  db_prepare(&q,
    "SELECT"
    "   name,"
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
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







+




+
+











-
+
+







  );
  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> SHA1
      @   <th> Hash
      @   <th> Algo
      if( g.perm.Admin ){
        @ <th> rcvid
      }
      @ </tr></thead>
      @ <tbody>
    }
    @ <tr>
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
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







-
+
+














-
+




+





-
+







      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> %h(zHash) </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 over %d(cnt) files</b><td><td>%s(zSzName)
    @ <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_footer();
  style_finish_page();
}

/*
** WEBPAGE: juvlist
**
** Return a complete list of unversioned files as JSON.  The JSON
** looks like this:
593
594
595
596
597
598
599

600


601
602
603
604
605
606
607
659
660
661
662
663
664
665
666

667
668
669
670
671
672
673
674
675







+
-
+
+







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("text/json");
  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,
617
618
619
620
621
622
623
624

625
626
627


628
629

630
631

632
633
634
635
636
685
686
687
688
689
690
691

692
693


694
695


696


697
698
699
700
701
702







-
+

-
-
+
+
-
-
+
-
-
+





   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\":\"", zSep);
     blob_appendf(&json, "%s{\"name\":\"%j\",\n", zSep, zName);
     zSep = ",\n ";
     blob_append_json_string(&json, zName);
     blob_appendf(&json, "\",\n  \"mtime\":%lld,\n  \"hash\":\"", mtime);
     blob_appendf(&json, "  \"mtime\":%lld,\n", mtime);
     blob_appendf(&json, "  \"hash\":\"%j\",\n", zHash);
     blob_append_json_string(&json, zHash);
     blob_appendf(&json, "\",\n  \"size\":%d,\n  \"user\":\"", fullSize);
     blob_appendf(&json, "  \"size\":%d,\n", fullSize);
     blob_append_json_string(&json, zLogin);
     blob_appendf(&json, "\"}");
     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
19

20
21
22
23
24
25
26
12
13
14
15
16
17
18

19
20
21
22
23
24
25
26







-
+







** Author contact information:
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*******************************************************************************
**
** This file contains code used to merge the changes in the current
** checkout into a different version and switch to that version.
** 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
60
61
62
63
64
65
66
67
68


69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84


85
86

87
88
89
90

91
92
93
94
95
96
97
98
99
100
101
102
103

















104
105

106
107
108
109
110
111
112
113
114
115
116

117
118
119
120
121
122

123
124
125
126
127
128
129
60
61
62
63
64
65
66


67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82


83
84
85

86
87
88
89

90
91
92
93
94
95








96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113

114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140







-
-
+
+














-
-
+
+

-
+



-
+





-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+











+






+







}

/*
** COMMAND: update
**
** Usage: %fossil update ?OPTIONS? ?VERSION? ?FILES...?
**
** Change the version of the current checkout to VERSION.  Any
** uncommitted changes are retained and applied to the new checkout.
** 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 checkout are subject
** to being updated and the version of the current checkout is changed
** 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 checkout.
** 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 checkout or the repository.
** 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
**   --latest         acceptable in place of VERSION, update to latest version
**   --force-missing  force update if missing content after sync
**   -n|--dry-run     If given, display instead of run actions
**   -v|--verbose     print status information about all files
**   -W|--width <num> Width of lines (default is to auto-detect). Must be >20
**                    or 0 (= no limit, resulting in a single line per entry).
**   --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
** 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();
142
143
144
145
146
147
148


149
150
151
152
153
154
155
156
157


158
159
160
161
162
163
164
165

166
167
168
169
170
171
172
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168


169
170

171
172
173
174
175
176

177
178
179
180
181
182
183
184







+
+







-
-
+
+
-






-
+







  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 ){
    if( autosync_loop(SYNC_PULL + SYNC_VERBOSE*verboseFlag,
  if( !dryRunFlag && !internalUpdate && !bNosync ){
    if( autosync_loop(SYNC_PULL + SYNC_VERBOSE*verboseFlag, 1, "update") ){
                      db_get_int("autosync-tries", 1), 1) ){
      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();
  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. */
209
210
211
212
213
214
215
216

217
218
219
220
221
222
223
224
225
226
227
228
229
230
231




232
233
234
235
236
237
238
239
240
241

242
243
244
245
246
247
248
221
222
223
224
225
226
227

228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256

257
258
259
260
261
262
263
264







-
+















+
+
+
+









-
+







        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);
        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 checkout, the pivot, and the version being merged.
  ** 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 */
371
372
373
374
375
376
377
378

379
380
381
382
383
384
385
387
388
389
390
391
392
393

394
395
396
397
398
399
400
401







-
+







      blob_reset(&treename);
    }
    db_multi_exec("%s", blob_sql_text(&sql));
    blob_reset(&sql);
  }

  /*
  ** Alter the content of the checkout so that it conforms with the
  ** 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,
409
410
411
412
413
414
415
416
417


418
419
420
421
422
423





424



425
426
427
428
429
430
431
425
426
427
428
429
430
431


432
433
434
435
436
437
438
439
440
441
442
443
444

445
446
447
448
449
450
451
452
453
454







-
-
+
+






+
+
+
+
+
-
+
+
+







    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 checkout
      ** but also exists in the target checkout.  Use the current version.
      /* 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\n", zName);
        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) ){
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
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







-
+











-
+
+
+
+
+
+
+
+
+
+
+
+
+















+















+
+
+
+
+
+
+
+



-
+
+
+
+
+







      /* 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 checkout.  Continue to hold the file as
        /* 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 ) file_delete(zFullPath);
        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\n", zNewName);
          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);
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
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







+
+
+










-
-
+







  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 uuid, id FROM vmerge JOIN blob ON merge=rid"
                   " WHERE id<=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.",
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
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







+
-
+
+
+
+
+
+
+
+
+
+

-
+



-
+


-
+











-
+



-
+
-

-
-
+
-
-
-
-
-
+












-
+
+
+
+
+
+








-

-
-
+




-
+












-
+



+


-
+

-
+









-
+










-
+








  /*
  ** 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();
    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 checkout to the target. */
      /* 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_lset_int("checkout", tid);
      db_set_checkout(tid);
    }else{
      /* A subset of files have been checked out.  Keep the current
      ** checkout unchanged. */
      ** 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(void){
void ensure_empty_dirs_created(int clearDirTable){
  char *zEmptyDirs = db_get("empty-dirs", 0);
  if( zEmptyDirs!=0 ){
    int i;
    Blob dirName;
    Glob *pGlob = glob_create(zEmptyDirs);
    Blob dirsList;

    zEmptyDirs = fossil_strdup(zEmptyDirs);
    for(i=0; zEmptyDirs[i]; i++){
    for(i=0; pGlob!=0 && i<pGlob->nPattern; i++){
      if( zEmptyDirs[i]==',' ) zEmptyDirs[i] = ' ';
    }
    blob_init(&dirsList, zEmptyDirs, -1);
    while( blob_token(&dirsList, &dirName) ){
      char *zDir = blob_str(&dirName);
      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 */
          /* do nothing - required directory exists already */
          /* 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);
      blob_reset(&dirName);
    }
    blob_reset(&dirsList);
    fossil_free(zEmptyDirs);
    glob_free(pGlob);
  }
}

/*
** Get the manifest record for a given revision, or the current checkout if
** 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", "trunk"), "ci");
    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("checkout artifact is not a check-in: %s", zRevision);
        fossil_fatal("check-out artifact is not a check-in: %s", zRevision);
      }else{
        fossil_fatal("invalid checkout artifact ID: %d", vid);
        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 checkout");
      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 checkout.
** 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 */
){
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
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







-
+


-
+
-



+
+
+







-
+
+

-
+


-
+

-
+

-
+




+
+









-
+
+



-
+













+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



+
+
+
+
+
+
+
+
+











+
















-
+
















+
+





-
+







  /* Return 1 on success and (assuming fatal is not set) 0 if not found. */
  return result;
}

/*
** COMMAND: revert
**
** Usage: %fossil revert ?-r REVISION? ?FILE ...?
** Usage: %fossil revert ?OPTIONS? ?FILE ...?
**
** Revert to the current repository version of FILE, or to
** the version associated with baseline REVISION if the -r flag
** the baseline VERSION specified with -r flag.
** appears.
**
** 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    revert given FILE(s) back to given REVISION
**   -r|--revision VERSION    Revert given FILE(s) back to given
**                            VERSION
**
** See also: redo, undo, update
** See also: [[redo]], [[undo]], [[checkout]], [[update]]
*/
void revert_cmd(void){
  Manifest *pCoManifest;          /* Manifest of current checkout */
  Manifest *pCoManifest;          /* Manifest of current check-out */
  Manifest *pRvManifest;          /* Manifest of selected revert version */
  ManifestFile *pCoFile;          /* File within current checkout manifest */
  ManifestFile *pCoFile;          /* File within current check-out manifest */
  ManifestFile *pRvFile;          /* File within revert version manifest */
  const char *zFile;              /* Filename relative to checkout root */
  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("the --revision option does not work for the entire tree");
    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 checkout. */
  /* 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(
        "REPLACE INTO torevert VALUES(%B);"
        "INSERT OR IGNORE INTO torevert"
        " SELECT pathname"
        "   FROM vfile"
        "  WHERE origname=%B;",
        &fname, &fname
      );
        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 = manifest_file_find(pRvManifest, 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. */
      /* 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);
863
864
865
866
867
868
869
870


871
872
873
874
875
876
877
973
974
975
976
977
978
979

980
981
982
983
984
985
986
987
988







-
+
+







        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"
         "   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);
  }

Changes to src/url.c.

31
32
33
34
35
36
37
38
39
40
41
42
43











44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65




















66

67
68
69
70
71
72
73
74
75
76
77
78
79
80

81
82

83
84
85
86
87
88

89
90
91
92
93
94
95
96
97













98
99
100
101
102
103
104
105
106






107
108
109
110
111
112
































113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135

136
137
138
139
140
141
142
31
32
33
34
35
36
37






38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

















54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73

74
75
76
77
78
79









80


81

82
83
84
85

86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123






124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166


167
168
169
170
171
172
173
174
175

176
177
178
179
180
181
182
183







-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+





-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+





-
-
-
-
-
-
-
-
-
+
-
-
+
-




-
+









+
+
+
+
+
+
+
+
+
+
+
+
+









+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+











-
-









-
+







#endif
#endif

#if INTERFACE
/*
** Flags for url_parse()
*/
#define URL_PROMPT_PW        0x001  /* Prompt for password if needed */
#define URL_REMEMBER         0x002  /* Remember the url for later reuse */
#define URL_ASK_REMEMBER_PW  0x004  /* Ask whether to remember prompted pw */
#define URL_REMEMBER_PW      0x008  /* Should remember pw */
#define URL_PROMPTED         0x010  /* Prompted for PW already */
#define URL_OMIT_USER        0x020  /* Omit the user name from URL */
#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 */
  char *name;      /* Hostname for http: or filename for file: */
  char *hostname;  /* The HOST: parameter on http headers */
  char *protocol;  /* "http" or "https" */
  int port;        /* TCP port number for http: or https: */
  int dfltPort;    /* The default port for the given protocol */
  char *path;      /* Pathname for http: */
  char *user;      /* User id for http: */
  char *passwd;    /* Password for http: */
  char *canonical; /* Canonical representation of the URL */
  char *proxyAuth; /* Proxy-Authorizer: string */
  char *fossil;    /* The fossil query parameter on ssh: */
  unsigned flags;  /* Boolean flags controlling URL processing */
  int useProxy;    /* Used to remember that a proxy is in use */
  char *proxyUrlPath;
  int 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 */
  int proxyOrigPort; /* Tunneled port number for https through proxy */
  char *proxyUrlCanonical; /* Remember canonical path when proxy is use */
};
#endif /* INTERFACE */


/*
** Convert a string to lower-case.
*/
static void url_tolower(char *z){
  while( *z ){
     *z = fossil_tolower(*z);
     z++;
  }
}

** Parse the URL in the zUrl argument. Store results in the pUrlData object.
/*
** Parse the given URL.  Populate members of the provided UrlData structure
** Populate members of pUrlData as follows:
** as follows:
**
**      isFile      True if FILE:
**      isHttps     True if HTTPS:
**      isSsh       True if SSH:
**      protocol    "http" or "https" or "file"
**      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);
    if( zUrl==0 ) return;
    if( pUrlData->passwd==0 ){
      pUrlData->passwd = unobscure(db_get("last-sync-pw", 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 = '?';

    pUrlData->isFile = 0;
    pUrlData->useProxy = 0;
    if( zUrl[4]=='s' ){
      pUrlData->isHttps = 1;
      pUrlData->protocol = "https";
      pUrlData->dfltPort = 443;
      iStart = 8;
    }else if( zUrl[0]=='s' ){
      pUrlData->isSsh = 1;
      pUrlData->protocol = "ssh";
      pUrlData->dfltPort = 22;
      pUrlData->fossil = "fossil";
      pUrlData->fossil = fossil_strdup("fossil");
      iStart = 6;
    }else{
      pUrlData->isHttps = 0;
      pUrlData->protocol = "http";
      pUrlData->dfltPort = 80;
      iStart = 7;
    }
175
176
177
178
179
180
181
182

183
184
185
186
187
188
189

190
191
192
193
194
195
196
216
217
218
219
220
221
222

223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238







-
+







+







      n = strlen(pUrlData->name);
      if( pUrlData->name[0]=='[' && n>2 && pUrlData->name[n-1]==']' ){
        pUrlData->name++;
        pUrlData->name[n-2] = 0;
      }
      zLogin = mprintf("");
    }
    url_tolower(pUrlData->name);
    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]);
212
213
214
215
216
217
218

219

220

221
222

223
224
225
226
227
228
229

230
231
232
233
234

235
236
237
238
239





240
241
242
243
244
245
246
254
255
256
257
258
259
260
261

262
263
264
265
266
267
268
269
270
271
272
273

274
275
276
277
278

279
280
281
282
283

284
285
286
287
288
289
290
291
292
293
294
295







+
-
+

+


+






-
+




-
+




-
+
+
+
+
+







        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 = zValue;
        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%s",
        "%s://%s%T%T%z",
        pUrlData->protocol, zLogin, pUrlData->name, pUrlData->path, zExe
      );
    }else{
      pUrlData->canonical = mprintf(
        "%s://%s%T:%d%T%s",
        "%s://%s%T:%d%T%z",
        pUrlData->protocol, zLogin, pUrlData->name, pUrlData->port,
        pUrlData->path, zExe
      );
    }
    if( pUrlData->isSsh && pUrlData->path[1] ) pUrlData->path++;
    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;
265
266
267
268
269
270
271
272

273
274
275
276


277
278
279

280
281
282
283
284
285
286
287
288
289


























































































290
291



292
293
294
295
296

297
298
299
300
301
302
303
304

305
306
307
308
309
310
311
312
313









314
315
316
317


























318
319
320
321
322
323

324


325

326
327
328
329


330
331

332
333
334
335





336
337
338
339
340
341

342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359

360
361
362
363
364
365
366
367
368
369
370
371
372
373
374

375
376
377











378
379
380
381
382





383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399


400
401
402
403
404
405
406
314
315
316
317
318
319
320

321
322
323
324

325
326
327
328

329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430

431
432
433
434
435
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







-
+



-
+
+


-
+










+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+
+
+




-
+








+









+
+
+
+
+
+
+
+
+




+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+






+

+
+
-
+




+
+

-
+
-
-
-
-
+
+
+
+
+





-
+
-
-
-
-
-
-
-
-
-
-
-
-
-





+















+
-
-
-
+
+
+
+
+
+
+
+
+
+
+





+
+
+
+
+















-
-
+
+







  if( pUrlData->isFile ){
    Blob cfile;
    dehttpize(zFile);
    file_canonical_name(zFile, &cfile, 0);
    free(zFile);
    zFile = 0;
    pUrlData->protocol = "file";
    pUrlData->path = "";
    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) ){
  }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)) ){
    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" structure as follows:
** 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"
**      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
**    --prompt-pw     Prompt for password if missing
**    --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) ){
  if( find_option("remember",0,0) )    fg |= URL_REMEMBER;
    db_must_be_within_tree();
    fg |= URL_REMEMBER;
  }
  if( find_option("prompt-pw",0,0) ) fg |= URL_PROMPT_PW;
  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++){
    fossil_print("g.url.isFile    = %d\n", g.url.isFile);
    urlparse_print(showPw);
    fossil_print("g.url.isHttps   = %d\n", g.url.isHttps);
    fossil_print("g.url.isSsh     = %d\n", g.url.isSsh);
    fossil_print("g.url.protocol  = %s\n", g.url.protocol);
    fossil_print("g.url.name      = %s\n", g.url.name);
    fossil_print("g.url.port      = %d\n", g.url.port);
    fossil_print("g.url.dfltPort  = %d\n", g.url.dfltPort);
    fossil_print("g.url.hostname  = %s\n", g.url.hostname);
    fossil_print("g.url.path      = %s\n", g.url.path);
    fossil_print("g.url.user      = %s\n", g.url.user);
    fossil_print("g.url.passwd    = %s\n", g.url.passwd);
    fossil_print("g.url.canonical = %s\n", g.url.canonical);
    fossil_print("g.url.fossil    = %s\n", g.url.fossil);
    fossil_print("g.url.flags     = 0x%02x\n", g.url.flags);
    if( g.url.isFile || g.url.isSsh ) break;
    if( i==0 ){
      fossil_print("********\n");
      url_enable_proxy("Using proxy: ");
    }
    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
** This also happens to be a convenient function to use to look for
** the --nosync option that will temporarily disable the "autosync"
** feature.
** 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", 0);
    if( zProxy==0 || zProxy[0]==0 || is_false(zProxy) ){
    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;
422
423
424
425
426
427
428

429
430
431
432
433
434
435
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622







+







      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
510
511
512
513
514
515
516
517

518
519
520
521
522
523
524
697
698
699
700
701
702
703

704
705
706
707
708
709
710
711







-
+







  const char *zName2,     /* Second override */
  const char *zValue2     /* Second override value */
){
  const char *zSep = "?";
  int i;

  blob_reset(&p->url);
  blob_appendf(&p->url, "%s/%s", g.zTop, p->zBase);
  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;
    }
551
552
553
554
555
556
557
558

559
560
561
562
563
564
565
738
739
740
741
742
743
744

745
746
747
748
749
750
751
752







-
+







void url_prompt_for_password_local(UrlData *pUrlData){
  if( pUrlData->isSsh || pUrlData->isFile ) return;
  if( isatty(fileno(stdin))
   && (pUrlData->flags & URL_PROMPT_PW)!=0
   && (pUrlData->flags & URL_PROMPTED)==0
  ){
    pUrlData->flags |= URL_PROMPTED;
    pUrlData->passwd = prompt_for_user_password(pUrlData->user);
    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;
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























































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







+
+
+
+
+
+
+
+
+
-
+
+

+
+
+
-
+
+















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
}

/*
** 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", g.url.canonical, 0);
      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);
        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.

36
37
38
39
40
41
42
43

44
45
46
47
48
49
50
36
37
38
39
40
41
42

43
44
45
46
47
48
49
50







-
+







       break;
    }
    if( z[i]>0 && z[i]<' ' ) z[i] = ' ';
  }
  blob_append(pBlob, z, -1);
}

#if defined(_WIN32) || defined(__BIONIC__)
#if defined(_WIN32) || (defined(__BIONIC__) && !defined(FOSSIL_HAVE_GETPASS))
#ifdef _WIN32
#include <conio.h>
#endif

/*
** getpass() for Windows and Android.
*/
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
110
111
112
113
114
115
116









117
118
119
120
121
122
123







-
-
-
-
-
-
-
-
-







void freepass(){
  if( !zPwdBuffer ) return;
  assert( nPwdBuffer>0 );
  fossil_secure_free_page(zPwdBuffer, nPwdBuffer);
}
#endif

#if defined(_WIN32) || defined(WIN32)
# include <io.h>
# include <fcntl.h>
# undef popen
# define popen _popen
# undef pclose
# define pclose _pclose
#endif

/*
** Scramble substitution matrix:
*/
static char aSubst[256];

/*
** Descramble the password
164
165
166
167
168
169
170
171

172
173
174
175
176
177
178
155
156
157
158
159
160
161

162
163
164
165
166
167
168
169







-
+







  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<sizeof(aSubst); i++) aSubst[i] = i;
  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 );
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
268
269
270
271
272
273
274




275
276
277
278
279
280
281







-
-
-
-








/*
** Prompt to save Fossil user password
*/
int save_password_prompt(const char *passwd){
  Blob x;
  char c;
  const char *old = db_get("last-sync-pw", 0);
  if( (old!=0) && fossil_strcmp(unobscure(old), passwd)==0 ){
     return 0;
  }
  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' );
}

324
325
326
327
328
329
330
331

332
333
334
335
336

337
338
339




340

341
342
343
344
345
346


347
348
349
350

351
352
353
354
355
356

357
358
359
360
361
362
363
364

365
366
367
368
369
370

371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390

391
392
393
394
395

396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411


412
413
414
415
416
417
418
311
312
313
314
315
316
317

318
319
320
321
322

323
324
325
326
327
328
329
330

331
332
333
334
335


336
337
338
339
340

341
342
343
344
345
346

347
348
349
350
351
352
353
354

355
356
357
358
359
360

361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403

404
405
406
407
408
409
410
411
412







-
+




-
+



+
+
+
+
-
+




-
-
+
+



-
+





-
+







-
+





-
+




















+





+















-
+
+







    strip_string(pIn, z);
  }
}

/*
** COMMAND: user*
**
** Usage: %fossil user SUBCOMMAND ...  ?-R|--repository FILE?
** 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?
** > 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?
** > 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
** > fossil user list
** > fossil user ls
**
**        List all users known to the repository
**
**    %fossil user new ?USERNAME? ?CONTACT-INFO? ?PASSWORD?
** > 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?
** > 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|default|list|new|password ...");
    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", "u"), -1);
    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 )){
  }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 ){
430
431
432
433
434
435
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
424
425
426
427
428
429
430
431
432
433
434
435
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







+


+












+




+


+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


-
+







      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 default list new password");
                 "capabilities contact default list new password");
  }
}

/*
** Attempt to set the user to zLogin
*/
static int attempt_user(const char *zLogin){
496
497
498
499
500
501
502

503
504
505
506
507
508
509
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526







+







**   (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;
    }
517
518
519
520
521
522
523

524
525


526
527
528
529
530
531
532
534
535
536
537
538
539
540
541


542
543
544
545
546
547
548
549
550







+
-
-
+
+








  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(0, 0);
  if( g.url.user && attempt_user(g.url.user) ) return;
  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");
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
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







-
-
+
+





-
+






+
+
+
+
+
+
+
+
+
+
+
+
+
+















+





+
+
+
+
+
+
+
+
+
+
+
+
+
+







** 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("checkout default-user: %s\n", g.localOpen ?
               db_lget("default-user","") : "<<no open checkout>>");
  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, 0);
  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
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
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







+










-
+

















-
+




-
+




-
+






-
+


-
+
+
+
















-
-
+
+



-
+












-
-
+
+










-
+



-
-
+
+




-
+




-
+




-
+





-
+

  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 access_log_page(void){
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("%s/access_log?y=%d&n=%d&o=%o", g.zTop, y, n, skip);
    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("%s/access_log?y=%d&n=%d&o=%o", g.zTop, y, n, skip);
    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("%s/access_log?y=%d&n=%d&o=%o", g.zTop, y, n, skip);
    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("%s/access_log?y=%d&n=%d", g.zTop, y, n);
    cgi_redirectf("%R/user_log?y=%d&n=%d", y, n);
    return;
  }
  style_header("Access Log");
  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", "%s/access_log?o=%d&n=%d&y=%d",
              g.zTop, skip>=n ? skip-n : 0, n, y);
    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">Access logging is %s(fLogEnabled?"on":"off").
  @ <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", "%s/access_log?o=%d&n=%d&y=%d",
                  g.zTop, skip+n, n, y);
      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", "%s/access_log?n=10000000", g.zTop);
    style_submenu_element("All", "%R/user_log?n=10000000");
  }
  @ </tbody></table>
  db_finalize(&q);
  @ <hr />
  @ <form method="post" action="%s(g.zTop)/access_log">
  @ <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="%s(g.zTop)/access_log">
  @ <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="%s(g.zTop)/access_log">
  @ <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="%s(g.zTop)/access_log">
  @ <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_footer();
  style_finish_page();
}

Changes to src/utf8.c.

86
87
88
89
90
91
92
93

94
95
96
97
98
99
100
86
87
88
89
90
91
92

93
94
95
96
97
98
99
100







-
+







  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().
** 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>
296
297
298
299
300
301
302





















































303
304
305
306
307
308
309
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







  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.
*/

Changes to src/util.c.

19
20
21
22
23
24
25

26
27
28
29
30
31
32
33
34


35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60







61
62
63
64
65
66
67
68

69
70
71
72
73
74
75
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61

62
63
64
65
66
67
68
69
70
71
72
73
74
75

76
77
78
79
80
81
82
83







+









+
+




-




















-
+
+
+
+
+
+
+







-
+







*/
#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_panic("out of memory");
  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_panic("out of memory");
  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;
96
97
98
99
100
101
102
103

104
105
106

107
108
109
110
111

112
113
114

115
116
117
118
119
120
121
104
105
106
107
108
109
110

111
112
113

114
115
116
117
118

119
120
121

122
123
124
125
126
127
128
129







-
+


-
+




-
+


-
+








  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_panic("VirtualAlloc failed: %lu\n", GetLastError());
    fossil_fatal("VirtualAlloc failed: %lu\n", GetLastError());
  }
  if( !VirtualLock(p, pageSize) ){
    fossil_panic("VirtualLock failed: %lu\n", GetLastError());
    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_panic("mmap failed: %d\n", errno);
    fossil_fatal("mmap failed: %d\n", errno);
  }
  if( mlock(p, pageSize) ){
    fossil_panic("mlock failed: %d\n", errno);
    fossil_fatal("mlock failed: %d\n", errno);
  }
#else
  p = fossil_malloc(pageSize);
#endif
  fossil_secure_zero(p, pageSize);
  if( pN ) *pN = pageSize;
  return p;
138
139
140
141
142
143
144




































































































































145
146
147
148
149
150
151
152
153
154
155
156
157
158
159

160
161
162
163
164
165
166

167
168
169
170
171
172
173
174
175
176
177
178
179
180
181






































182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200

201
202
203
204
205
206
207
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374






375
376
377
378
379
380
381
382







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+















+







+















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+













-
-
-
-
-
-
+







  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{
    int a, b;
    do{
      a = *zA++;
      b = *zB++;
    }while( a==b && a!=0 );
    return ((unsigned char)a) - (unsigned char)b;
    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 ){
272
273
274
275
276
277
278














279
280
281
282
283
284
285
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+







  }
  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. */
};
404
405
406
407
408
409
410
411

412
413
414
415
416
417
418
593
594
595
596
597
598
599

600
601
602
603
604
605
606
607







-
+







#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_uuid(const char *zSym){
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.
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
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







+
+



-
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
-
+
+
+
+
+

-
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+







}

/*
** 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;
  sqlite3 *db;
  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
    db = g.db;
  cDirSep = '\\';
  dwTempLenW = GetTempPathW(dwTempSizeW, zTempDirW);
  if( dwTempLenW>0 && dwTempLenW<dwTempSizeW
      && ( zTempDirA = fossil_path_to_utf8(zTempDirW) )){
    zDir = zTempDirA;
  }else{
    sqlite3_open("",&db);
    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;
  sqlite3_file_control(db, 0, SQLITE_FCNTL_TEMPFILENAME, (void*)&zTFile);
  if( g.db==0 ) sqlite3_close(db);
  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.
**
523
524
525
526
527
528
529


































































































































































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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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.

66
67
68
69
70
71
72
73

74
75
76
77
78
79
80
66
67
68
69
70
71
72

73
74
75
76
77
78
79
80







-
+







** 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();
  content_clear_cache(0);
  inFinalVerify = 1;
  rid = bag_first(&toVerify);
  while( rid>0 ){
    verify_rid(rid);
    rid = bag_next(&toVerify, rid);
  }
  bag_clear(&toVerify);

Changes to src/vfile.c.

86
87
88
89
90
91
92
93
94


95
96
97
98
99
100
101
86
87
88
89
90
91
92


93
94
95
96
97
98
99
100
101







-
-
+
+







  db_begin_transaction();
  p = manifest_get(vid, CFTYPE_MANIFEST, 0);
  if( p==0 ) {
    db_end_transaction(1);
    return 0;
  }
  db_prepare(&ins,
    "INSERT INTO vfile(vid,isexe,islink,rid,mrid,pathname) "
    " VALUES(:vid,:isexe,:islink,:id,:id,:name)");
    "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);
157
158
159
160
161
162
163
164
165
166
167




168
169
170
171
172
173
174
157
158
159
160
161
162
163




164
165
166
167
168
169
170
171
172
173
174







-
-
-
-
+
+
+
+







** 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 false and the CKSIG_HASH flag is false.  If the mtime-changes
** setting is true (or undefined - it defaults to true) 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.
** 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;
240
241
242
243
244
245
246
247

248
249
250
251
252
253
254
255
256
257
258
259



260
261
262
263
264
265
266
240
241
242
243
244
245
246

247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269







-
+












+
+
+







      ** 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) ){
    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 ){
311
312
313
314
315
316
317



318
319
320
321
322
323
324
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330







+
+
+







    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);
      }
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383

384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401

402
403
404
405
406
407
408
363
364
365
366
367
368
369

370


















371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388

389
390
391
392
393
394
395
396







-

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+

















-
+







    blob_reset(&content);
    db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d",
                  file_mtime(zName, RepoFILE), id);
  }
  db_finalize(&q);
}


/*
** Delete from the disk every file in VFILE vid.
*/
void vfile_unlink(int vid){
  Stmt q;
  db_prepare(&q, "SELECT %Q || pathname FROM vfile"
                 " WHERE vid=%d AND mrid>0", g.zLocalRoot, vid);
  while( db_step(&q)==SQLITE_ROW ){
    const char *zName;

    zName = db_column_text(&q, 0);
    file_delete(zName);
  }
  db_finalize(&q);
  db_multi_exec("UPDATE vfile SET mtime=NULL WHERE vid=%d AND mrid>0", vid);
}

/*
** Check to see if the directory named in zPath is the top of a checkout.
** 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-checkout database name is deprecated.  At some point, all support
  ** 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);
420
421
422
423
424
425
426

427

428
429
430
431
432
433
434
408
409
410
411
412
413
414
415

416
417
418
419
420
421
422
423







+
-
+







     "original",
     "output",
  };
  int i, j, n;

  if( sqlite3_strglob("ci-comment-????????????.txt", zName)==0 ) return 1;
  for(; zName[0]!=0; zName++){
    if( zName[0]=='/'
    if( zName[0]=='/' && sqlite3_strglob("/ci-comment-????????????.txt", 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;
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
435
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







+






-

+
-
+
+













-
+
+



















+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







** 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.
** 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 */
  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) SELECT :file%s%s"
      "  WHERE NOT EXISTS(SELECT 1 FROM vfile WHERE"
      " pathname=:file %s)",
      scanFlags & SCAN_MTIME ? ", mtime"  : "",
      scanFlags & SCAN_SIZE  ? ", size"   : "",
      scanFlags & SCAN_MTIME ? ", :mtime" : "",
      scanFlags & SCAN_SIZE  ? ", :size"  : "",
      filename_collation()
    );
      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 ){
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
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







-
+

-
+


-
+










-
+


-
+
+
+
+







      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, RepoFILE)==1) : (pEntry->d_type==DT_DIR) ){
          ? (file_isdir(zPath, eFType)==1) : (pEntry->d_type==DT_DIR) ){
#else
      }else if( file_isdir(zPath, RepoFILE)==1 ){
      }else if( file_isdir(zPath, eFType)==1 ){
#endif
        if( !vfile_top_of_checkout(zPath) ){
          vfile_scan(pPath, nPrefix, scanFlags, pIgnore1, pIgnore2);
          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, RepoFILE));
            db_bind_int(&ins, ":mtime", file_mtime(zPath, eFType));
          }
          if( scanFlags & SCAN_SIZE ){
            db_bind_int(&ins, ":size", file_size(zPath, RepoFILE));
            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);
580
581
582
583
584
585
586
587


588
589
590
591
592
593
594
589
590
591
592
593
594
595

596
597
598
599
600
601
602
603
604







-
+
+







** 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 */
  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;
640
641
642
643
644
645
646
647

648
649

650
651
652
653
654

655
656
657
658
659
660
661
650
651
652
653
654
655
656

657
658

659
660
661
662
663

664
665
666
667
668
669
670
671







-
+

-
+




-
+







      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, RepoFILE)==1) : (pEntry->d_type==DT_DIR) ){
          ? (file_isdir(zPath, eFType)==1) : (pEntry->d_type==DT_DIR) ){
#else
      }else if( file_isdir(zPath, RepoFILE)==1 ){
      }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);
                                     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? */
        }
743
744
745
746
747
748
749
750

751
752
753
754
755
756
757
753
754
755
756
757
758
759

760
761
762
763
764
765
766
767







-
+







          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);*/
        /*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);
964
965
966
967
968
969
970






































































































































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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
  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;"
  );
}

Deleted src/webmail.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





















































































































































































































































































































































































































































































































































































































































































































































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
/*
** 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 web pages for managing the email storage tables
** (if they exist):
**
**     emailbox
**     emailblob
**     emailroute
*/
#include "config.h"
#include "webmail.h"
#include <assert.h>


#if INTERFACE

/* Recognized content encodings */
#define EMAILENC_NONE   0         /* No encoding */
#define EMAILENC_B64    1         /* Base64 encoded */
#define EMAILENC_QUOTED 2         /* Quoted printable */

/* An instance of the following object records the location of important
** attributes on a single element in a multipart email message body.
*/
struct EmailBody {
  char zMimetype[32];     /* Mimetype */
  u8 encoding;            /* Type of encoding */
  char *zFilename;        /* From content-disposition: */
  char *zContent;         /* Content.  \0 terminator inserted */
};

/*
** An instance of the following object describes the struture of
** an rfc-2822 email message.
*/
struct EmailToc {
  int nHdr;              /* Number of header lines */
  int nHdrAlloc;         /* Number of header lines allocated */
  char **azHdr;          /* Pointer to header line.  \0 terminator inserted */
  int nBody;             /* Number of body segments */
  int nBodyAlloc;        /* Number of body segments allocated */
  EmailBody *aBody;      /* Location of body information */
};
#endif

/*
** Free An EmailToc object
*/
void emailtoc_free(EmailToc *p){
  int i;
  fossil_free(p->azHdr);
  for(i=0; i<p->nBody; i++){
    fossil_free(p->aBody[i].zFilename);
  }
  fossil_free(p->aBody);
  fossil_free(p);
}

/*
** Allocate a new EmailToc object
*/
EmailToc *emailtoc_alloc(void){
  EmailToc *p = fossil_malloc( sizeof(*p) );
  memset(p, 0, sizeof(*p));
  return p;
}

/*
** Add a new body element to an EmailToc.
*/
EmailBody *emailtoc_new_body(EmailToc *p){
  EmailBody *pNew;
  p->nBody++;
  if( p->nBody>p->nBodyAlloc ){
    p->nBodyAlloc = (p->nBodyAlloc+1)*2;
    p->aBody = fossil_realloc(p->aBody, sizeof(p->aBody[0])*p->nBodyAlloc);
  }
  pNew = &p->aBody[p->nBody-1];
  memset(pNew, 0, sizeof(*pNew));
  return pNew;
}

/*
** Add a new header line to the EmailToc.
*/
void emailtoc_new_header_line(EmailToc *p, char *z){
  p->nHdr++;
  if( p->nHdr>p->nHdrAlloc ){
    p->nHdrAlloc = (p->nHdrAlloc+1)*2;
    p->azHdr = fossil_realloc(p->azHdr, sizeof(p->azHdr[0])*p->nHdrAlloc);
  }
  p->azHdr[p->nHdr-1] = z;
}

/*
** Return the length of a line in an email header.  Continuation lines
** are included.  Hence, this routine returns the number of bytes up to
** and including the first \n character that is followed by something
** other than whitespace.
*/
static int email_line_length(const char *z){
  int i;
  for(i=0; z[i] && (z[i]!='\n' || z[i+1]==' ' || z[i+1]=='\t'); i++){}
  if( z[i]=='\n' ) i++;
  return i;
}

/*
** Look for a parameter of the form NAME=VALUE in the given email
** header line.  Return a copy of VALUE in space obtained from 
** fossil_malloc().  Or return NULL if there is no such parameter.
*/
static char *email_hdr_value(const char *z, const char *zName){
  int nName = (int)strlen(zName);
  int i;
  const char *z2 = strstr(z, zName);
  if( z2==0 ) return 0;
  z2 += nName;
  if( z2[0]!='=' ) return 0;
  z2++;
  if( z2[0]=='"' ){
    z2++;
    for(i=0; z2[i] && z2[i]!='"'; i++){}
    if( z2[i]!='"' ) return 0;
  }else{
    for(i=0; z2[i] && !fossil_isspace(z2[i]); i++){}
  }
  return mprintf("%.*s", i, z2);
}

/*
** Return a pointer to the first non-whitespace character in z
*/
static const char *firstToken(const char *z){
  while( fossil_isspace(*z) ){
    z++;
  }
  return z;
}

/*
** The n-bytes of content in z is a single multipart mime segment
** with its own header and body.  Decode this one segment and add it to p;
**
** Rows of the header of the segment are added to p if bAddHeader is
** true.
*/
LOCAL void emailtoc_add_multipart_segment(
  EmailToc *p,          /* Append the segments here */
  char *z,              /* The body component */
  int bAddHeader        /* True to add header lines to p */
){
  int i, j;
  int n;
  int multipartBody = 0;
  EmailBody *pBody = emailtoc_new_body(p);
  i = 0;
  while( z[i] ){
    n = email_line_length(&z[i]);
    if( (n==2 && z[i]=='\r' && z[i+1]=='\n') || z[i]=='\n' || n==0 ){
      /* This is the blank line at the end of the header */
      i += n;
      break;
    }
    for(j=i+n; j>i && fossil_isspace(z[j-1]); j--){}
    z[j] = 0;
    if( sqlite3_strnicmp(z+i, "Content-Type:", 13)==0 ){
      const char *z2 = firstToken(z+i+13);
      if( z2 && strncmp(z2, "multipart/", 10)==0 ){
        multipartBody = 1;
      }else{
        int j;
        for(j=0; z2[j]=='/' || fossil_isalnum(z2[j]); j++){}
        if( j>=sizeof(pBody->zMimetype) ) j = sizeof(pBody->zMimetype);
        memcpy(pBody->zMimetype, z2, j);
        pBody->zMimetype[j] = 0;
      }
    }
                           /*  123456789 123456789 123456 */
    if( sqlite3_strnicmp(z+i, "Content-Transfer-Encoding:", 26)==0 ){
      const char *z2 = firstToken(z+(i+26));
      if( z2 && sqlite3_strnicmp(z2, "base64", 6)==0 ){
        pBody->encoding = EMAILENC_B64;
                                 /*  123456789 123456 */
      }else if( sqlite3_strnicmp(z2, "quoted-printable", 16)==0 ){
        pBody->encoding = EMAILENC_QUOTED;
      }else{
        pBody->encoding = EMAILENC_NONE;
      }
    }
    if( bAddHeader ){
      emailtoc_new_header_line(p, z+i);
    }else if( sqlite3_strnicmp(z+i, "Content-Disposition:", 20)==0 ){
                                /*   123456789 123456789  */
       fossil_free(pBody->zFilename);
       pBody->zFilename = email_hdr_value(z+i, "filename");
    }
    i += n;
  }
  if( multipartBody ){
    p->nBody--;
    emailtoc_add_multipart(p, z+i);
  }else{
    pBody->zContent = z+i;
  }
}

/*
** The n-bytes of content in z are a multipart/ body component for
** an email message.  Decode this into its individual segments.
**
** The component should start and end with a boundary line.  There
** may be additional boundary lines in the middle.
*/
LOCAL void emailtoc_add_multipart(
  EmailToc *p,          /* Append the segments here */
  char *z               /* The body component.  zero-terminated */
){
  int nB;               /* Size of the boundary string */
  int iStart;           /* Start of the coding region past boundary mark */
  int i;                /* Loop index */
  char *zBoundary = 0;  /* Boundary marker */

  /* Skip forward to the beginning of the boundary mark.  The boundary
  ** mark always begins with "--" */
  while( z[0]!='-' || z[1]!='-' ){
    while( z[0] && z[0]!='\n' ) z++;
    if( z[0]==0 ) return;
    z++;
  }

  /* Find the length of the boundary mark. */
  zBoundary = z;
  for(nB=0; z[nB] && !fossil_isspace(z[nB]); nB++){}
  if( nB==0 ) return;

  z += nB;
  while( fossil_isspace(z[0]) ) z++;
  zBoundary[nB] = 0;
  for(i=iStart=0; z[i]; i++){
    if( z[i]=='\n' && strncmp(z+i+1, zBoundary, nB)==0 ){
      z[i+1] = 0;
      emailtoc_add_multipart_segment(p, z+iStart, 0);
      iStart = i+nB;
      if( z[iStart]=='-' && z[iStart+1]=='-' ) return;
      while( fossil_isspace(z[iStart]) ) iStart++;
      i = iStart;
    }
  }
}

/*
** Compute a table-of-contents (EmailToc) for the email message
** provided on the input.
**
** This routine will cause pEmail to become zero-terminated if it is
** not already.  It will also insert zero characters into parts of
** the message, to delimit the various components.
*/
EmailToc *emailtoc_from_email(Blob *pEmail){
  char *z;
  EmailToc *p = emailtoc_alloc();
  blob_terminate(pEmail);
  z = blob_buffer(pEmail);
  emailtoc_add_multipart_segment(p, z, 1);
  return p;
}

/*
** Inplace-unfolding of an email header line.
**
** Actually - this routine works by converting all contiguous sequences
** of whitespace into a single space character.
*/
static void email_hdr_unfold(char *z){
  int i, j;
  char c;
  for(i=j=0; (c = z[i])!=0; i++){
    if( fossil_isspace(c) ){
      c = ' ';
      if( j && z[j-1]==' ' ) continue;
    }
    z[j++] = c;
  }
  z[j] = 0;
}

/*
** COMMAND: test-decode-email
**
** Usage: %fossil test-decode-email FILE
**
** Read an rfc-2822 formatted email out of FILE, then write a decoding
** to stdout.  Use for testing and validating the email decoder.
*/
void test_email_decode_cmd(void){
  Blob email;
  EmailToc *p;
  int i;
  verify_all_options();
  if( g.argc!=3 ) usage("FILE");
  blob_read_from_file(&email, g.argv[2], ExtFILE);
  p = emailtoc_from_email(&email);
  fossil_print("%d header line and %d content segments\n",
               p->nHdr, p->nBody);
  for(i=0; i<p->nHdr; i++){
    email_hdr_unfold(p->azHdr[i]);
    fossil_print("%3d: %s\n", i, p->azHdr[i]);
  }
  for(i=0; i<p->nBody; i++){
    fossil_print("\nBODY %d mime \"%s\" encoding %d",
                 i, p->aBody[i].zMimetype, p->aBody[i].encoding);
    if( p->aBody[i].zFilename ){
      fossil_print(" filename \"%s\"", p->aBody[i].zFilename);
    }
    fossil_print("\n");
    if( strncmp(p->aBody[i].zMimetype,"text/",5)!=0 ) continue;
    switch( p->aBody[i].encoding ){
      case EMAILENC_B64: {
        int n = 0;
        decodeBase64(p->aBody[i].zContent, &n, p->aBody[i].zContent);
        fossil_print("%s", p->aBody[i].zContent);
        if( n && p->aBody[i].zContent[n-1]!='\n' ) fossil_print("\n");
        break;
      }
      case EMAILENC_QUOTED: {
        int n = 0;
        decodeQuotedPrintable(p->aBody[i].zContent, &n);
        fossil_print("%s", p->aBody[i].zContent);
        if( n && p->aBody[i].zContent[n-1]!='\n' ) fossil_print("\n");
        break;
      }
      default: {
        fossil_print("%s\n", p->aBody[i].zContent);
        break;
      }
    }
  }
  emailtoc_free(p);
  blob_reset(&email);
}

/*
** Add the select/option box to the timeline submenu that shows
** the various email message formats.
*/
static void webmail_f_submenu(void){
  static const char *az[] = {
     "0", "Normal",
     "1", "Decoded",
     "2", "Raw",
  };
  style_submenu_multichoice("f", sizeof(az)/(2*sizeof(az[0])), az, 0);
}

/*
** If the first N characters of z[] are the name of a header field
** that should be shown in "Normal" mode, then return 1.
*/
static int webmail_normal_header(const char *z, int N){
  static const char *az[] = {
    "To",  "Cc",  "Bcc",  "Date", "From",  "Subject",
  };
  int i;
  for(i=0; i<sizeof(az)/sizeof(az[0]); i++){
    if( sqlite3_strnicmp(z, az[i], N)==0 ) return 1;
  }
  return 0;
}

/*
** Paint a page showing a single email message
*/
static void webmail_show_one_message(
  HQuery *pUrl,          /* Calling context */
  int emailid,           /* emailbox.ebid to display */
  const char *zUser      /* User who owns it, or NULL if does not matter */
){
  Blob sql;
  Stmt q;
  int eState = -1;
  int eTranscript = 0;
  char zENum[30];
  style_submenu_element("Index", "%s", url_render(pUrl,"id",0,0,0));
  webmail_f_submenu();
  blob_init(&sql, 0, 0);
  db_begin_transaction();
  blob_append_sql(&sql,
    "SELECT decompress(etxt), estate, emailblob.ets"
    " FROM emailblob, emailbox"
    " WHERE emailid=emsgid AND ebid=%d",
     emailid
  );
  if( zUser ) blob_append_sql(&sql, " AND euser=%Q", zUser);
  db_prepare_blob(&q, &sql);
  blob_reset(&sql);
  style_header("Message %d",emailid);
  if( db_step(&q)==SQLITE_ROW ){
    Blob msg = db_column_text_as_blob(&q, 0);
    int eFormat = atoi(PD("f","0"));
    eState = db_column_int(&q, 1);
    eTranscript = db_column_int(&q, 2);
    if( eFormat==2 ){
      @ <pre>%h(db_column_text(&q, 0))</pre>
    }else{      
      EmailToc *p = emailtoc_from_email(&msg);
      int i, j;
      @ <p>
      for(i=0; i<p->nHdr; i++){
        char *z = p->azHdr[i];
        email_hdr_unfold(z);
        for(j=0; z[j] && z[j]!=':'; j++){}
        if( eFormat==0 && !webmail_normal_header(z, j) ) continue;
        if( z[j]!=':' ){
          @ %h(z)<br>
        }else{
          z[j] = 0;
          @ <b>%h(z):</b> %h(z+j+1)<br>
        }
      }
      for(i=0; i<p->nBody; i++){
        @ <hr><b>Messsage Body #%d(i): %h(p->aBody[i].zMimetype) \
        if( p->aBody[i].zFilename ){
          @ "%h(p->aBody[i].zFilename)"
        }
        @ </b>
        if( eFormat==0 ){
          if( strncmp(p->aBody[i].zMimetype, "text/plain", 10)!=0 ) continue;
          if( p->aBody[i].zFilename ) continue;
        }else{
          if( strncmp(p->aBody[i].zMimetype, "text/", 5)!=0 ) continue;
        }
        switch( p->aBody[i].encoding ){
          case EMAILENC_B64: {
            int n = 0;
            decodeBase64(p->aBody[i].zContent, &n, p->aBody[i].zContent);
            break;
          }
          case EMAILENC_QUOTED: {
            int n = 0;
            decodeQuotedPrintable(p->aBody[i].zContent, &n);
            break;
          }
        }
        @ <pre>%h(p->aBody[i].zContent)</pre>
      }
    }
  }
  db_finalize(&q);

  /* Optionally show the SMTP transcript */
  if( eTranscript>0
   && db_exists("SELECT 1 FROM emailblob WHERE emailid=%d", eTranscript)
  ){
    if( P("ts")==0 ){
      sqlite3_snprintf(sizeof(zENum), zENum, "%d", emailid);
      style_submenu_element("SMTP Transcript","%s",
            url_render(pUrl, "ts", "1", "id", zENum));
    }else{
      db_prepare(&q,
        "SELECT decompress(etxt) FROM emailblob WHERE emailid=%d", eTranscript
      );
      if( db_step(&q)==SQLITE_ROW ){
        const char *zTranscript = db_column_text(&q, 0);
        @ <hr>
        @ <pre>%h(zTranscript)</pre>
      }
      db_finalize(&q);
    }
  }

  if( eState==0 ){
    /* If is message is currently Unread, change it to Read */
    blob_append_sql(&sql,
      "UPDATE emailbox SET estate=1 "
      " WHERE estate=0 AND ebid=%d",
       emailid
    );
    if( zUser ) blob_append_sql(&sql, " AND euser=%Q", zUser);
    db_multi_exec("%s", blob_sql_text(&sql));
    blob_reset(&sql);
    eState = 1;
  }

  url_add_parameter(pUrl, "id", 0);
  sqlite3_snprintf(sizeof(zENum), zENum, "e%d", emailid);
  if( eState==2 ){
    style_submenu_element("Undelete","%s",
      url_render(pUrl,"read","1",zENum,"1"));
  }
  if( eState==1 ){
    style_submenu_element("Delete", "%s",
      url_render(pUrl,"trash","1",zENum,"1"));
    style_submenu_element("Mark As Unread", "%s",
      url_render(pUrl,"unread","1",zENum,"1"));
  }
  if( eState==3 ){
    style_submenu_element("Delete", "%s",
      url_render(pUrl,"trash","1",zENum,"1"));
  }

  db_end_transaction(0);
  style_footer();
  return;
}

/*
** Scan the query parameters looking for parameters with name of the
** form "eN" where N is an integer.  For all such integers, change
** the state of every emailbox entry with ebid==N to eStateNew provided
** that either zUser is NULL or matches.
**
** Or if eNewState==99, then delete the entries.
*/
static void webmail_change_state(int eNewState, const char *zUser){
  Blob sql;
  int sep = '(';
  int i;
  const char *zName;
  int n;
  if( !cgi_csrf_safe(0) ) return;
  blob_init(&sql, 0, 0);
  if( eNewState==99 ){
    blob_append_sql(&sql, "DELETE FROM emailbox WHERE estate==2 AND ebid IN ");
  }else{
    blob_append_sql(&sql, "UPDATE emailbox SET estate=%d WHERE ebid IN ",
                    eNewState);
  }
  for(i=0; (zName = cgi_parameter_name(i))!=0; i++){
    if( zName[0]!='e' ) continue;
    if( !fossil_isdigit(zName[1]) ) continue;
    n = atoi(zName+1);
    blob_append_sql(&sql, "%c%d", sep, n);
    sep = ',';
  }
  if( zUser ){
    blob_append_sql(&sql, ") AND euser=%Q", zUser);
  }else{
    blob_append_sql(&sql, ")");
  }
  if( sep==',' ){
    db_multi_exec("%s", blob_sql_text(&sql));
  }
  blob_reset(&sql);
}


/*
** Add the select/option box to the timeline submenu that shows
** which messages to include in the index.
*/
static void webmail_d_submenu(void){
  static const char *az[] = {
     "0", "InBox",
     "1", "Unread",
     "2", "Trash",
     "3", "Sent",
     "4", "Everything",
  };
  style_submenu_multichoice("d", sizeof(az)/(2*sizeof(az[0])), az, 0);
}

/*
** WEBPAGE:  webmail
**
** This page can be used to read content from the EMAILBOX table
** that contains email received by the "fossil smtpd" command.
**
** Query parameters:
**
**     id=N                 Show a single email entry emailbox.ebid==N
**     f=N                  Display format.  0: decoded 1: raw
**     user=USER            Show mailbox for USER (admin only).
**     user=*               Show mailbox for all users (admin only).
**     d=N                  0: inbox+unread 1: unread-only 2: trash 3: all
**     eN                   Select email entry emailbox.ebid==N
**     trash                Move selected entries to trash (estate=2)
**     read                 Mark selected entries as read (estate=1)
**     unread               Mark selected entries as unread (estate=0)
**  
*/
void webmail_page(void){
  int emailid;
  Stmt q;
  Blob sql;
  int showAll = 0;
  const char *zUser = 0;
  int d = 0;               /* Display mode.  0..3.  d= query parameter */
  int pg = 0;              /* Page number */
  int N = 50;              /* Results per page */
  int got;                 /* Number of results on this page */
  char zPPg[30];           /* Previous page */
  char zNPg[30];           /* Next page */
  HQuery url;
  login_check_credentials();
  if( !login_is_individual() ){
    login_needed(0);
    return;
  }
  if( !db_table_exists("repository","emailbox") ){
    style_header("Webmail Not Available");
    @ <p>This repository is not configured to provide webmail</p>
    style_footer();
    return;
  }
  add_content_sql_commands(g.db);
  emailid = atoi(PD("id","0"));
  url_initialize(&url, "webmail");
  if( g.perm.Admin ){
    zUser = PD("user",g.zLogin);
    if( zUser ){
      url_add_parameter(&url, "user", zUser);
      if( fossil_strcmp(zUser,"*")==0 ){
        showAll = 1;
        zUser = 0;
      }
    }
  }else{
    zUser = g.zLogin;
  }
  if( P("d") ) url_add_parameter(&url, "d", P("d"));
  if( emailid>0 ){
    webmail_show_one_message(&url, emailid, zUser);
    return;
  }
  style_header("Webmail");
  webmail_d_submenu();
  db_begin_transaction();
  if( P("trash")!=0 ) webmail_change_state(2,zUser);
  if( P("unread")!=0 ) webmail_change_state(0,zUser);
  if( P("read")!=0 ) webmail_change_state(1,zUser);
  if( P("purge")!=0 ) webmail_change_state(99,zUser);
  blob_init(&sql, 0, 0);
  blob_append_sql(&sql,
    "CREATE TEMP TABLE tmbox AS "
    "SELECT ebid,"                   /* 0 */
    " efrom,"                        /* 1 */
    " datetime(edate,'unixepoch'),"  /* 2 */
    " estate,"                       /* 3 */
    " esubject,"                     /* 4 */
    " euser"                         /* 5 */
    " FROM emailbox"
  );
  d = atoi(PD("d","0"));
  switch( d ){
    case 0: {   /* Show unread and read */
      blob_append_sql(&sql, " WHERE estate<=1");
      break;
    }
    case 1: {   /* Unread messages only */
      blob_append_sql(&sql, " WHERE estate=0");
      break;
    }
    case 2: {   /* Trashcan only */
      blob_append_sql(&sql, " WHERE estate=2");
      break;
    }
    case 3: {   /* Outgoing email only */
      blob_append_sql(&sql, " WHERE estate=3");
      break;
    }
    case 4: {   /* Everything */
      blob_append_sql(&sql, " WHERE 1");
      break;
    }
  }
  if( showAll ){
    style_submenu_element("My Emails", "%s", url_render(&url,"user",0,0,0));
  }else if( zUser!=0 ){
    style_submenu_element("All Users", "%s", url_render(&url,"user","*",0,0));
    if( fossil_strcmp(zUser, g.zLogin)!=0 ){
      style_submenu_element("My Emails", "%s", url_render(&url,"user",0,0,0));
    }
    if( zUser ){
      blob_append_sql(&sql, " AND euser=%Q", zUser);
    }else{
      blob_append_sql(&sql, " AND euser=%Q", g.zLogin);
    }
  }else{
    if( g.perm.Admin ){
      style_submenu_element("All Users", "%s", url_render(&url,"user","*",0,0));
    }
    blob_append_sql(&sql, " AND euser=%Q", g.zLogin);
  }
  pg = atoi(PD("pg","0"));
  blob_append_sql(&sql, " ORDER BY edate DESC limit %d offset %d", N+1, pg*N);
  db_multi_exec("%s", blob_sql_text(&sql));
  got = db_int(0, "SELECT count(*) FROM tmbox");
  db_prepare(&q, "SELECT * FROM tmbox LIMIT %d", N);
  blob_reset(&sql);
  @ <form action="%R/webmail" method="POST">
  @ <input type="hidden" name="d" value="%d(d)">
  @ <input type="hidden" name="user" value="%h(zUser?zUser:"*")">
  @ <table border="0" width="100%%">
  @ <tr><td align="left">
  if( d==2 ){
    @ <input type="submit" name="read" value="Undelete">
    @ <input type="submit" name="purge" value="Delete Permanently">
  }else{
    @ <input type="submit" name="trash" value="Delete">
    if( d!=1 ){
      @ <input type="submit" name="unread" value="Mark as unread">
    }
    @ <input type="submit" name="read" value="Mark as read">
  }
  @ <button onclick="webmailSelectAll(); return false;">Select All</button>
  @ <a href="%h(url_render(&url,0,0,0,0))">refresh</a>
  @ </td><td align="right">
  if( pg>0 ){
    sqlite3_snprintf(sizeof(zPPg), zPPg, "%d", pg-1);
    @ <a href="%s(url_render(&url,"pg",zPPg,0,0))">&lt; Newer</a>&nbsp;&nbsp;
  }
  if( got>50 ){
    sqlite3_snprintf(sizeof(zNPg),zNPg,"%d",pg+1);
    @ <a href="%s(url_render(&url,"pg",zNPg,0,0))">Older &gt;</a></td>
  }
  @ </table>
  @ <table>
  while( db_step(&q)==SQLITE_ROW ){
    const char *zId = db_column_text(&q,0);
    const char *zFrom = db_column_text(&q, 1);
    const char *zDate = db_column_text(&q, 2);
    const char *zSubject = db_column_text(&q, 4);
    if( zSubject==0 || zSubject[0]==0 ) zSubject = "(no subject)";
    @ <tr>
    @ <td><input type="checkbox" class="webmailckbox" name="e%s(zId)"></td>
    @ <td>%h(zFrom)</td>
    @ <td><a href="%h(url_render(&url,"id",zId,0,0))">%h(zSubject)</a> \
    @ %s(zDate)</td>
    if( showAll ){
      const char *zTo = db_column_text(&q,5);
      @ <td><a href="%h(url_render(&url,"user",zTo,0,0))">%h(zTo)</a></td>
    }
    @ </tr>
  }
  db_finalize(&q);
  @ </table>
  @ </form>
  @ <script>
  @ function webmailSelectAll(){
  @   var x = document.getElementsByClassName("webmailckbox");
  @   for(i=0; i<x.length; i++){
  @     x[i].checked = true;
  @   }
  @ }
  @ </script>
  style_footer();
  db_end_transaction(0);
}

/*
** WEBPAGE:  emailblob
**
** This page, accessible only to administrators, allows easy viewing of
** the emailblob table - the table that contains the text of email messages
** both inbound and outbound, and transcripts of SMTP sessions.
**
**    id=N          Show the text of emailblob with emailid==N
**    
*/
void webmail_emailblob_page(void){
  int id = atoi(PD("id","0"));
  Stmt q;
  login_check_credentials();
  if( !g.perm.Setup ){
    login_needed(0);
    return;
  }
  add_content_sql_commands(g.db);
  style_header("emailblob table");
  if( id>0 ){
    style_submenu_element("Index", "%R/emailblob");
    @ <ul>
    db_prepare(&q, "SELECT emailid FROM emailblob WHERE ets=%d", id);
    while( db_step(&q)==SQLITE_ROW ){
      int id = db_column_int(&q, 0);
      @ <li> <a href="%R/emailblob?id=%d(id)">emailblob entry %d(id)</a>
    }
    db_finalize(&q);
    db_prepare(&q, "SELECT euser, estate FROM emailbox WHERE emsgid=%d", id);
    while( db_step(&q)==SQLITE_ROW ){
      const char *zUser = db_column_text(&q, 0);
      int e = db_column_int(&q, 1);
      @ <li> emailbox for %h(zUser) state %d(e)
    }
    db_finalize(&q);
    db_prepare(&q, "SELECT efrom, eto FROM emailoutq WHERE emsgid=%d", id);
    while( db_step(&q)==SQLITE_ROW ){
      const char *zFrom = db_column_text(&q, 0);
      const char *zTo = db_column_text(&q, 1);
      @ <li> emailoutq message body from %h(zFrom) to %h(zTo)
    }
    db_finalize(&q);
    db_prepare(&q, "SELECT efrom, eto FROM emailoutq WHERE ets=%d", id);
    while( db_step(&q)==SQLITE_ROW ){
      const char *zFrom = db_column_text(&q, 0);
      const char *zTo = db_column_text(&q, 1);
      @ <li> emailoutq transcript from %h(zFrom) to %h(zTo)
    }
    db_finalize(&q);
    @ </ul>
    @ <hr>
    db_prepare(&q, "SELECT decompress(etxt) FROM emailblob WHERE emailid=%d",
               id);
    while( db_step(&q)==SQLITE_ROW ){
      const char *zContent = db_column_text(&q, 0);
      @ <pre>%h(zContent)</pre>
    }
    db_finalize(&q);
  }else{
    style_submenu_element("emailoutq table","%R/emailoutq");
    db_prepare(&q,
       "SELECT emailid, enref, ets, datetime(etime,'unixepoch'), esz,"
       " length(etxt)"
       " FROM emailblob ORDER BY etime DESC, emailid DESC");
    @ <table border="1" cellpadding="5" cellspacing="0" class="sortable" \
    @ data-column-types='nnntkk'>
    @ <thead><tr><th> emailid <th> enref <th> ets <th> etime \
    @ <th> uncompressed <th> compressed </tr></thead><tbody>
    while( db_step(&q)==SQLITE_ROW ){
      int id = db_column_int(&q, 0);
      int nref = db_column_int(&q, 1);
      int ets = db_column_int(&q, 2);
      const char *zDate = db_column_text(&q, 3);
      int sz = db_column_int(&q,4);
      int csz = db_column_int(&q,5);
      @ <tr>
      @  <td align="right"><a href="%R/emailblob?id=%d(id)">%d(id)</a>
      @  <td align="right">%d(nref)</td>
      if( ets>0 ){
        @  <td align="right">%d(ets)</td>
      }else{
        @  <td>&nbsp;</td>
      }
      @  <td>%h(zDate)</td>
      @  <td align="right" data-sortkey='%08x(sz)'>%,d(sz)</td>
      @  <td align="right" data-sortkey='%08x(csz)'>%,d(csz)</td>
      @ </tr>
    }
    @ </tbody></table>
    db_finalize(&q);
    style_table_sorter();
  }
  style_footer();
}

/*
** WEBPAGE:  emailoutq
**
** This page, accessible only to administrators, allows easy viewing of
** the emailoutq table - the table that contains the email messages
** that are queued for transmission via SMTP.
*/
void webmail_emailoutq_page(void){
  Stmt q;
  login_check_credentials();
  if( !g.perm.Setup ){
    login_needed(0);
    return;
  }
  add_content_sql_commands(g.db);
  style_header("emailoutq table");
  style_submenu_element("emailblob table","%R/emailblob");
  db_prepare(&q,
     "SELECT edomain, efrom, eto, emsgid, "
     "       datetime(ectime,'unixepoch'),"
     "       datetime(nullif(emtime,0),'unixepoch'),"
     "       ensend, ets"
     " FROM emailoutq"
  );
  @ <table border="1" cellpadding="5" cellspacing="0" class="sortable" \
  @ data-column-types='tttnttnn'>
  @ <thead><tr><th> edomain <th> efrom <th> eto <th> emsgid \
  @ <th> ectime <th> emtime <th> ensend <th> ets </tr></thead><tbody>
  while( db_step(&q)==SQLITE_ROW ){
    const char *zDomain = db_column_text(&q, 0);
    const char *zFrom = db_column_text(&q, 1);
    const char *zTo = db_column_text(&q, 2);
    int emsgid = db_column_int(&q, 3);
    const char *zCTime = db_column_text(&q, 4);
    const char *zMTime = db_column_text(&q, 5);
    int ensend = db_column_int(&q, 6);
    int ets = db_column_int(&q, 7);
    @ <tr>
    @  <td>%h(zDomain)
    @  <td>%h(zFrom)
    @  <td>%h(zTo)
    @  <td align="right"><a href="%R/emailblob?id=%d(emsgid)">%d(emsgid)</a>
    @  <td>%h(zCTime)
    @  <td>%h(zMTime)
    @  <td align="right">%d(ensend)
    if( ets>0 ){
      @  <td align="right"><a href="%R/emailblob?id=%d(ets)">%d(ets)</a></td>
    }else{
      @  <td>&nbsp;</td>
    }
  }
  @ </tbody></table>
  db_finalize(&q);
  style_table_sorter();
  style_footer();
}

Changes to src/wiki.c.

60
61
62
63
64
65
66

67
68
69
70
71

72
73
74
75






























76
77
78
79
80
81
82
83
84
85
86
87
88

89
90
91
92
93
94
95
96

97
98
99

100
101
102
103
104
105
106
107
108

109
110
111
112
113
114
115
116

117
118
119
120
121
122
123
60
61
62
63
64
65
66
67
68
69
70
71

72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127

128
129
130

131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148

149
150
151
152
153
154
155
156







+




-
+




+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+













+







-
+


-
+









+







-
+








/*
** 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_footer();
    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("%s/%s", g.zTop, zIndexPage);
    cgi_redirectf("%R/%s", zIndexPage);
  }
  if( !g.perm.RdWiki ){
    cgi_redirectf("%s/login?g=%s/home", g.zTop, g.zTop);
    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_footer();
  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 ||
154
155
156
157
158
159
160

161


162
163
164
165
166
167
168

169
170




















171
172
173
174
175
176
177
178
179
180
181
182
183
184
185

186
187
188
189
190
191

192



193
194
195

196
197
198
199
200
201
202
203
204
205

206
207
208
209
210
211

212



213
214
215
















216
217
218
219
220
221
222
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256

257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281

282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304







+

+
+







+


+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+















+






+

+
+
+


-
+










+






+

+
+
+


-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







}

/*
** 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_footer();
  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_footer();
  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(
260
261
262
263
264
265
266
267
268
269
270
271

272
273
274
275
276
277
278
279
280
281

282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301

302
303

304
305

306
307
308
309
310


311

312
313
314
315
316
317
318
319

320
321
322
323
324
325
326
327
328
329
330

331
332
333
334



























































































































335
336
337
338

339













340
341
342
343
344
345
346

347
348
349
350

351

352
353
354
355


356
357
358
359
360
361
362
342
343
344
345
346
347
348



349

350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365












366
367
368

369
370

371
372

373
374
375
376
377
378
379
380

381
382
383
384
385
386
387
388

389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404

405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
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







-
-
-

-
+










+




-
-
-
-
-
-
-
-
-
-
-
-



-
+

-
+

-
+





+
+
-
+







-
+











+



-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+




+
-
+
+
+
+
+
+
+
+
+
+
+
+
+





-
-
+




+

+




+
+







  }
  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 0
  if( (ok & W_BLOG)!=0
#endif
  if( (ok & W_SANDBOX)!=0 ){
    style_submenu_element("Sandbox", "%R/wiki?name=Sandbox");
    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>
  { char *zWikiHomePageName = db_get("index-page",0);
    if( zWikiHomePageName ){
      @ <li> %z(href("%R%s",zWikiHomePageName))
      @      %h(zWikiHomePageName)</a> wiki home page.</li>
    }
  }
  { char *zHomePageName = db_get("project-name",0);
    if( zHomePageName ){
      @ <li> %z(href("%R/wiki?name=%t",zHomePageName))
      @      %h(zHomePageName)</a> project home page.</li>
    }
  }
  @ <li> %z(href("%R/timeline?y=w"))Recent changes</a> to wiki pages.</li>
  @ <li> Formatting rules for %z(href("%R/wiki_rules"))Fossil Wiki</a> and for
  @ %z(href("%R/md_rules"))Markdown Wiki</a>.</li>
  @ <li> Use the %z(href("%R/wiki?name=Sandbox"))Sandbox</a>
  @ <li> Use the %z(href("%R/wikiedit?name=Sandbox"))Sandbox</a>
  @      to experiment.</li>
  if( g.anon.NewWiki ){
  if( g.perm.NewWiki ){
    @ <li>  Create a %z(href("%R/wikinew"))new wiki page</a>.</li>
    if( g.anon.Write ){
    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.anon.ModWiki ){
  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_footer();
  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_footer();
  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
**
** URL: /wiki?name=PAGENAME
** 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;
  char *zUuid;
  unsigned submenuFlags = W_ALL;
  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;
382
383
384
385
386
387
388
389

390
391
392
393
394
395
396
397




398
399
400
401
402
403
404


405
406
407
408
409
410
411
412
413

414
415
416

417
418
419
420
421
422













423

424







425


426
427
428
429
430
431
432
433
434
435

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
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







-
+
-
-
-
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
+
+
-
-

-
-
-
-

-
+
-


+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+

+
+
+
+
+
+
+
-
+
+









-
+





-
+


+
+
+





-
+
+
+
+

-
+

-
+















-
+








+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
-
+
+
+
+

-
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+

+
+
+
+
-
-
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
-
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+




















-
-
-
-
-
-
-
+
+
-
+






-
-
-
-
+
+
+
+





-
+

















-
+




-
+







    pWiki = manifest_get(rid, CFTYPE_WIKI, 0);
    if( pWiki ){
      zBody = pWiki->zWiki;
      zMimetype = pWiki->zMimetype;
    }
  }
  zMimetype = wiki_filter_mimetypes(zMimetype);
  if( !g.isHome ){
  if( !noSubmenu ){
    if( rid ){
      style_submenu_element("Diff", "%R/wdiff?name=%T&a=%d", zPageName, rid);
      zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
      style_submenu_element("Details", "%R/info/%s", zUuid);
    }
    if( (rid && g.anon.WrWiki) || (!rid && g.anon.NewWiki) ){
      if( db_get_boolean("wysiwyg-wiki", 0) ){
        style_submenu_element("Edit", "%s/wikiedit?name=%T&wysiwyg=1",
    if( ((rid && g.perm.WrWiki) || (!rid && g.perm.NewWiki))
     && wiki_special_permission(zPageName)
    ){
      style_submenu_element("Edit", "%R/wikiedit?name=%T", zPageName);
             g.zTop, zPageName);
      }else{
        style_submenu_element("Edit", "%s/wikiedit?name=%T", g.zTop, zPageName);
      }
    }
    if( rid && g.anon.ApndWiki && g.anon.Attach ){
      style_submenu_element("Attach",
    }else if( rid && g.perm.ApndWiki ){
      style_submenu_element("Edit", "%R/wikiappend?name=%T", zPageName);
           "%s/attachadd?page=%T&from=%s/wiki%%3fname=%T",
           g.zTop, zPageName, g.zTop, zPageName);
    }
    if( rid && g.anon.ApndWiki ){
      style_submenu_element("Append", "%s/wikiappend?name=%T&mimetype=%s",
           g.zTop, zPageName, zMimetype);
    }
    if( g.perm.Hyperlink ){
      style_submenu_element("History", "%s/whistory?name=%T",
      style_submenu_element("History", "%R/whistory?name=%T", zPageName);
           g.zTop, zPageName);
    }
  }
  if( !isPopup ){
  style_set_current_page("%T?name=%T", g.zPath, zPageName);
  style_header("%s", zPageName);
  wiki_standard_submenu(submenuFlags);
  blob_init(&wiki, zBody, -1);
  wiki_render_by_mimetype(&wiki, zMimetype);
  blob_reset(&wiki);
    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);
  attachment_list(zPageName, "<hr /><h2>Attachments:</h2><ul>");
  }
  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_footer();
    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);
    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_multi_exec("INSERT OR IGNORE INTO unsent 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.
** 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){
void mimetype_option_menu(const char *zMimetype, const char *zParam){
  unsigned i;
  @ <select name="mimetype" size="1">
  @ <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=4; i>=2; i-=2){
  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;
** WEBPAGE: wikiedit
** URL: /wikiedit?name=PAGENAME

  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.
** Edit a wiki page.
**
** 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.
*/
void wikiedit_page(void){
static void wiki_ajax_emit_page_attachments(Manifest * pWiki,
  char *zTag;
  int rid = 0;
  int isSandbox;
  Blob wiki;
  Manifest *pWiki = 0;
  const char *zPageName;
                                            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);
  int n;
  const char *z;
  char *zBody = (char*)P("w");
  const char *zMimetype = wiki_filter_mimetypes(P("mimetype"));
  int isWysiwyg = P("wysiwyg")!=0;
  int goodCaptcha = 1;

    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;
  }
  if( P("edit-wysiwyg")!=0 ){ isWysiwyg = 1; zBody = 0; }
  if( P("edit-markup")!=0 ){ isWysiwyg = 0; zBody = 0; }
  if( zBody ){
    if( isWysiwyg ){
      Blob body;
      blob_zero(&body);
      htmlTidy(zBody, &body);
      zBody = blob_str(&body);
    }else{
  wiki_ajax_emit_page_attachments(pWiki, latestOnly, nullIfEmpty);
  manifest_destroy(pWiki);
  return 1;
      zBody = mprintf("%s", zBody);
    }
  }
  login_check_credentials();
  zPageName = PD("name","");
  if( check_name(zPageName) ) return;
  isSandbox = is_sandbox(zPageName);
  if( isSandbox ){
    if( !g.perm.WrWiki ){
}


/*
** 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;
      login_needed(g.anon.WrWiki);
      return;
    }
    if( zBody==0 ){
      zBody = db_get("sandbox","");
      zMimetype = db_get("sandbox-mimetype","text/x-fossil-wiki");
  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, ",
    zTag = mprintf("wiki-%s", zPageName);
    rid = db_int(0,
       pWiki->zWikiTitle,
       wiki_page_type_name(pWiki->zWikiTitle),
       zUuid,
      "SELECT rid FROM tagxref"
      " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
      " ORDER BY mtime DESC", zTag
    );
    free(zTag);
       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);
    if( (rid && !g.perm.WrWiki) || (!rid && !g.perm.NewWiki) ){
      login_needed(rid ? g.anon.WrWiki : g.anon.NewWiki);
      return;
    }
    if( zBody==0 && (pWiki = manifest_get(rid, CFTYPE_WIKI, 0))!=0 ){
      zBody = pWiki->zWiki;
      zMimetype = pWiki->zMimetype;
    }
    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( P("submit")!=0 && zBody!=0
  if(isNew){
   && (goodCaptcha = captcha_is_correct(0))
  ){
    char *zDate;
    Blob cksum;
    blob_zero(&wiki);
    db_begin_transaction();
    if( isSandbox ){
      db_set("sandbox",zBody,0);
    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");

      db_set("sandbox-mimetype",zMimetype,0);
    }else{
      login_verify_csrf_secret();
      zDate = date_in_standard_format("now");
      blob_appendf(&wiki, "D %s\n", zDate);
      free(zDate);
      blob_appendf(&wiki, "L %F\n", zPageName);
  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);
      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());
      }
      blob_appendf(&wiki, "W %d\n%s\n", strlen(zBody), zBody);
      md5sum_blob(&wiki, &cksum);
      blob_appendf(&wiki, "Z %b\n", &cksum);
      blob_reset(&cksum);
      wiki_put(&wiki, 0, wiki_need_moderation(0));
    }
    db_end_transaction(0);
    cgi_redirectf("wiki?name=%T", zPageName);
}

/*
** 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();
}
  if( P("cancel")!=0 ){
    cgi_redirectf("wiki?name=%T", zPageName);
    return;
  }
  if( zBody==0 ){
    zBody = mprintf("<i>Empty Page</i>");
  }
  style_set_current_page("%T?name=%T", g.zPath, zPageName);
  style_header("Edit: %s", zPageName);
  if( !goodCaptcha ){
    @ <p class="generalError">Error:  Incorrect security code.</p>
  }
  blob_zero(&wiki);
  blob_append(&wiki, zBody, -1);
  if( P("preview")!=0 ){
    @ Preview:<hr />
    wiki_render_by_mimetype(&wiki, zMimetype);
    @ <hr />
    blob_reset(&wiki);

/*
** 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*/);
  }
  for(n=2, z=zBody; z[0]; z++){
    if( z[0]=='\n' ) n++;
  }
  if( n<20 ) n = 20;

  /******* 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);
  if( n>30 ) n = 30;
  if( !isWysiwyg ){
    /* Traditional markup-only editing */
    form_begin(0, "%R/wikiedit");
    @ <div>Markup style:
    mimetype_option_menu(zMimetype);
    @ <br /><textarea name="w" class="wikiedit" cols="80"
    @  rows="%d(n)" wrap="virtual">%h(zBody)</textarea>
    CX("<div class='input-with-label'>"
       /*will get moved around dynamically*/
       "<button class='wikiedit-save'>"
       "Save</button>"
       "<button class='wikiedit-save-close'>"
       "Save &amp; 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 &amp; 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>");
    @ <br />
    if( db_get_boolean("wysiwyg-wiki", 0) ){
      @ <input type="submit" name="edit-wysiwyg" value="Wysiwyg Editor"
      @  onclick='return confirm("Switching to WYSIWYG-mode\nwill erase your markup\nedits. Continue?")' />
    }
    @ <input type="submit" name="preview" value="Preview Your Changes" />
  }else{
    /* Wysiwyg editing */
    Blob html, temp;
    form_begin("", "%R/wikiedit");
    @ <div>
    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>");
    @ <input type="hidden" name="wysiwyg" value="1" />
    blob_zero(&temp);
    wiki_convert(&wiki, &temp, 0);
    blob_zero(&html);
    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*/);
  }

    htmlTidy(blob_str(&temp), &html);
    blob_reset(&temp);
  /****** The obligatory "Misc" tab ******/
  {
    CX("<div id='wikiedit-tab-misc' "
       "data-tab-parent='wikiedit-tabs' "
       "data-tab-label='Misc.' "
       "class='hidden'"
       ">");
    wysiwygEditor("w", blob_str(&html), 60, n);
    blob_reset(&html);
    @ <br />
    @ <input type="submit" name="edit-markup" value="Markup Editor"
    @  onclick='return confirm("Switching to markup-mode\nwill erase your WYSIWYG\nedits. Continue?")' />
    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");
  login_insert_csrf_secret();
  @ <input type="submit" name="submit" value="Apply These Changes" />
  @ <input type="hidden" name="name" value="%h(zPageName)" />
  @ <input type="submit" name="cancel" value="Cancel"
  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));
  @  onclick='confirm("Abandon your changes?")' />
  @ </div>
  captcha_generate(0);
    /* 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){"
  @ </form>
  manifest_destroy(pWiki);
  blob_reset(&wiki);
  style_footer();
     "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) ){
    if( fossil_strcmp(zMimetype,"text/x-fossil-wiki")==0
     && db_get_boolean("wysiwyg-wiki", 0)
    ){
      cgi_redirectf("wikiedit?name=%T&wysiwyg=1", zName);
    }else{
      cgi_redirectf("wikiedit?name=%T&mimetype=%s", zName, zMimetype);
    }
    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 />
  @ Markup style:
  mimetype_option_menu("text/x-fossil-wiki");
  @ <br /><input type="submit" value="Create" />
  @ <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_footer();
  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",
    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);
    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{
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
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







-





+
+


+
+
+
+




-
+







+
-
+



-
-
+
-
-
+

-
+

+





-


-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+

+

-
+
+




+




+
+
+
+
-
+



-
+
+

-
+




-
-
-
+
+

-
+

-
+


-
-
-
-
+
+
+
+


-
-
-
+
-
-
-
-
-
+
-
-
-
-
-
-
-
-






+
+
+
+





+
+
+

-
+

+

-
-
-
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
-
+




-

-
+
+
+
+
+
+
+
+
+
+


-
-
+
+

+

-
+


-
-
-
-
+
+
+
+
-
-
-
+
-
-
-
-
-
-
-
+
-

+



-
-
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+

-
-
+
+
+
-

-


-
+



-
+

-
-
+
+
+
+
+

-
+

-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+




+





+

+
+



+


-
+

-
+

+
+
+
+
+
+
+
+
+

+
+
+
-
+
-
+
+
+
+
+
+
+
+

-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
-
+
+
+
+
+

-
-
+
+














+
+












-
+







** URL: /wikiappend?name=PAGENAME&mimetype=MIMETYPE
**
** Append text to the end of a wiki page.
*/
void wikiappend_page(void){
  char *zTag;
  int rid = 0;
  int isSandbox;
  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 ){
  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( !rid ){
    if( !pWiki ){
      fossil_redirect_home();
      return;
    }
  }
  if( !g.perm.ApndWiki ){
    zMimetype = wiki_filter_mimetypes(pWiki->zMimetype)
    login_needed(g.anon.ApndWiki);
    return;
      /* see https://fossil-scm.org/forum/forumpost/0acfdaac80 */;
  }
  if( P("submit")!=0 && P("r")!=0 && P("u")!=0
  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;
    Manifest *pWiki = 0;

    blob_zero(&body);
    if( isSandbox ){
      blob_append(&body, db_get("sandbox",""), -1);
      appendRemark(&body, zMimetype);
      db_set("sandbox", blob_str(&body), 0);
    }else{
      login_verify_csrf_secret();
      pWiki = manifest_get(rid, CFTYPE_WIKI, 0);
      if( pWiki ){
        blob_append(&body, pWiki->zWiki, -1);
    blob_append(&body, pWiki->zWiki, -1);
        manifest_destroy(pWiki);
      }
      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);
    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( P("cancel")!=0 ){
  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( P("preview")!=0 ){
  if( !isSandbox && P("preview")!=0 ){
    Blob preview;
    blob_zero(&preview);
    appendRemark(&preview, zMimetype);
    @ Preview:<hr />
    @ Preview:<hr>
    safe_html_context(DOCSRC_WIKI);
    wiki_render_by_mimetype(&preview, zMimetype);
    @ <hr />
    @ <hr>
    blob_reset(&preview);
  }
  zUser = PD("u", g.zLogin);
  form_begin(0, "%R/wikiappend");
  login_insert_csrf_secret();
  @ <input type="hidden" name="name" value="%h(zPageName)" />
  @ <input type="hidden" name="mimetype" value="%h(zMimetype)" />
  @ <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 />
  @ <input type="text" name="u" size="20" value="%h(zUser)"><br>
  zFormat = mimetype_common_name(zMimetype);
  @ Comment to append (formatted as %s(zFormat)):<br />
  @ 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" />
  @ <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>
  style_footer();
}

  manifest_destroy(pWiki);
/*
** Name of the wiki history page being generated
*/
static const char *zWikiPageName;

  style_finish_page();
/*
** Function called to output extra text at the end of each line in
** a wiki history listing.
*/
static void wiki_history_extra(int rid){
  if( db_exists("SELECT 1 FROM tagxref WHERE rid=%d", rid) ){
    @ %z(href("%R/wdiff?name=%t&a=%d",zWikiPageName,rid))[diff]</a>
  }
}

/*
** 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.Hyperlink ){ login_needed(g.anon.Hyperlink); return; }
  if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; }
  zPageName = PD("name","");
  style_set_current_feature("wiki");
  style_header("History Of %s", zPageName);

  db_prepare(&q, "%s AND event.objid IN "
                 "  (SELECT rid FROM tagxref WHERE tagid="
  showRid = P("showid")!=0;
  db_prepare(&q,
    "SELECT"
                       "(SELECT tagid FROM tag WHERE tagname='wiki-%q')"
                 "   UNION SELECT attachid FROM attachment"
                          " WHERE target=%Q)"
                 "ORDER BY mtime DESC",
                 timeline_query_for_www(), zPageName, zPageName);
  zWikiPageName = zPageName;
  www_print_timeline(&q, TIMELINE_ARTID, 0, 0, 0, wiki_history_extra);
    "  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">&#9875;</span></th>
  @ <th>User<span hidden class="wh-clickable"
  @                   id="wh-collapser">&emsp;&#9842;</span></th>
  if( showRid ){
    @ <th>RID</th>
  }
  @ <th>&nbsp;</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_footer();
  style_finish_page();
}

/*
** WEBPAGE: wdiff
** URL: /whistory?name=PAGENAME&a=RID1&b=RID2
**
** Show the difference between two wiki pages.
** 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){
  int rid1, rid2;
  const char *zPageName;
  const char *zId;
  const char *zPid;
  Manifest *pW1, *pW2 = 0;
  int rid1, rid2, nextRid;
  Blob w1, w2, d;
  u64 diffFlags;
  DiffConfig DCfg;

  login_check_credentials();
  rid1 = atoi(PD("a","0"));
  if( !g.perm.Hyperlink ){ login_needed(g.anon.Hyperlink); return; }
  if( rid1==0 ) fossil_redirect_home();
  rid2 = atoi(PD("b","0"));
  if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; }
  zId = P("id");
  if( zId==0 ){
    rid1 = atoi(PD("rid","0"));
  zPageName = PD("name","");
  style_header("Changes To %s", zPageName);

  }else{
  if( rid2==0 ){
    rid2 = db_int(0,
      "SELECT objid FROM event JOIN tagxref ON objid=rid AND tagxref.tagid="
                        "(SELECT tagid FROM tag WHERE tagname='wiki-%q')"
      " WHERE event.mtime<(SELECT mtime FROM event WHERE objid=%d)"
      " ORDER BY event.mtime DESC LIMIT 1",
      zPageName, rid1
    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);
  blob_zero(&w2);
  if( rid2 && (pW2 = manifest_get(rid2, CFTYPE_WIKI, 0))!=0 ){
  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);
  diffFlags = construct_diff_flags(1);
  text_diff(&w2, &w1, &d, 0, diffFlags | DIFF_HTML | DIFF_LINENO);
  construct_diff_flags(1, &DCfg);
  DCfg.diffFlags |= DIFF_HTML | DIFF_LINENO;
  text_diff(&w2, &w1, &d, &DCfg);
  @ <pre class="udiff">
  @ %s(blob_str(&d))
  @ <pre>
  manifest_destroy(pW1);
  manifest_destroy(pW2);
  style_footer();
  style_finish_page();
}

/*
** prepare()s pStmt with a query requesting:
** A query that returns information about all wiki pages.
**
** - wiki page name
** - tagxref (whatever that really is!)
**    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
**
** Used by wcontent_page() and the JSON wiki code.
** The wrid value is zero for deleted wiki pages.
*/
void wiki_prepare_page_list( Stmt * pStmt ){
static const char listAllWikiPages[] =
  db_prepare(pStmt,
    "SELECT"
    "  substr(tagname, 6) as name,"
    "  (SELECT value FROM tagxref WHERE tagid=tag.tagid"
    "    ORDER BY mtime DESC) as tagXref"
    "  FROM tag WHERE tagname GLOB 'wiki-*'"
    " ORDER BY lower(tagname) /*sort*/"
@ 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", "%s/wcontent", g.zTop);
    style_submenu_element("Active", "%R/wcontent");
  }else{
    style_submenu_element("All", "%s/wcontent?all=1", g.zTop);
    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'>
  @ <ul>
  @ <thead><tr>
  wiki_prepare_page_list(&q);
  @ <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 *zName = db_column_text(&q, 0);
    int size = db_column_int(&q, 1);
    if( size>0 ){
      @ <li>%z(href("%R/wiki?name=%T",zName))%h(zName)</a></li>
    }else if( showAll ){
      @ <li>%z(href("%R/wiki?name=%T",zName))<s>%h(zName)</s></a></li>
    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);
  @ </ul>
  style_footer();
  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_footer();
  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.
**
1060
1061
1062
1063
1064
1065
1066
1067
1068


1069
1070
1071
1072
1073
1074
1075
2096
2097
2098
2099
2100
2101
2102


2103
2104
2105
2106
2107
2108
2109
2110
2111







-
-
+
+







  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 or its
** timestamp. Returns 0 if there is no such item and -1 if the details
** 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) ){
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
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+










-
-
+
+

-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+



-
-
+
+







-
+




-
-
-
-
+
+
+
+
+
+
+

-
+
+




-
-
+
+


-
+
+


+
+




+
+












+
+
+



+
+
+










-
+


-
+


-
+

-
+
+
+
+
+
+
+
+
+
+

+


-
+


-
-
-
-
-
-
-
+
+
+
+
+
+
+
+







+
-
+
















+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+









+




+











-
+
-
-
+
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-




-
+







-
+








+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+







                      " 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 PAGENAME ?FILE?
**    %fossil wiki export ?FILE? -t|--technote DATETIME|TECHNOTE-ID
** > 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.
**       If PAGENAME is provided, the wiki page will be output. For
**       a tech note either DATETIME or TECHNOTE-ID must be specified. If
**       DATETIME is used, the most recently modified tech note with that
**       DATETIME will be sent.
**       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 ?FILE? ?OPTIONS?
** > 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 or the timeline comment of the
**       technote.
**       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/markdown and text/plain. fossil,
**                                     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. When updating a tech note
**                                     the most recently modified tech note
**                                     with the specified timestamp will be
**                                     updated.
**                                     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.
**                                     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?
** > fossil wiki list ?OPTIONS?
** > fossil wiki ls ?OPTIONS?
**
**       Lists all wiki entries, one per line, ordered
**       case-insensitively by name.
**       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;        /* Name of the wiki page to export */
    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;                      /* Artifact ID of the wiki page */
    int rid = 0;                  /* Artifact ID of the wiki page */
    int i;                        /* Loop counter */
    char *zBody = 0;              /* Wiki page content */
    Blob body;                    /* 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 PAGENAME ?FILE?");
        usage("export ?-html? PAGENAME ?FILE?");
      }
      zPageName = g.argv[3];
      rid = db_int(0, "SELECT x.rid FROM tag t, tagxref x"
        " WHERE x.tagid=t.tagid AND t.tagname='wiki-%q'"
        " ORDER BY x.mtime DESC LIMIT 1",
        zPageName
      );
      if( (pWiki = manifest_get(rid, CFTYPE_WIKI, 0))!=0 ){
        zBody = pWiki->zWiki;
      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 "
        usage("export ?FILE? --technote DATETIME|TECHNOTE-ID");
              "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);
    blob_write_to_file(&body, zFile);
      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);
    }
    if( !zMimeType || !*zMimeType ){
    isSandbox = is_sandbox(zPageName);
      /* Try to deduce the mime type based on the prior version. */
      if ( !zETime ){
    if ( !zETime ){
        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( !isSandbox ){
        wiki_fetch_by_name(zPageName, 0, &rid, &pWiki);
                     );
        if( rid>0 && (pWiki = manifest_get(rid, CFTYPE_WIKI, 0))!=0
           && (pWiki->zMimetype && *pWiki->zMimetype) ){
          zMimeType = pWiki->zMimetype;
        }
      }else{
        rid = wiki_technote_to_rid(zETime);
        if( rid>0 && (pWiki = manifest_get(rid, CFTYPE_EVENT, 0))!=0
           && (pWiki->zMimetype && *pWiki->zMimetype) ){
          zMimeType = pWiki->zMimetype;
      }
    }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( g.argv[2][1]=='r' && rid>0 ){
    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( g.argv[2][1]=='o' && 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);
        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,
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
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







-
+






+
-
-
-
-
+
+
+
+
+
-
-
-

-

-
+







-


+
+
+
+
+
+
+
+
+

















+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+

-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

      }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!=5 ){
    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;
    int showIds = 0;

    if ( !find_option("technote","t",0) ){
      db_prepare(&q,
    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:""*/);
        "SELECT substr(tagname, 6) FROM tag WHERE tagname GLOB 'wiki-*'"
        " ORDER BY lower(tagname) /*sort*/"
      );
    }else{
      showIds = find_option("show-technote-ids","s",0)!=0;
      db_prepare(&q,
        "SELECT datetime(e.mtime), substr(t.tagname,7)"
        "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
** COMMAND: test-markdown-render
**
** Usage: %fossil test-markdown-render FILE
**
** Render markdown wiki from FILE to stdout.

/*
** 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.
*/
void test_markdown_render(void){
  Blob in, out;
  verify_all_options();
  if( g.argc!=3 ) usage("FILE");
  blob_zero(&out);
  blob_read_from_file(&in, g.argv[2], ExtFILE);
  markdown_to_html(&in, 0, &out);
  blob_write_to_file(&out, "-");
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;
}

Changes to src/wiki.wiki.

1
2
3
4
5
6
7
8
9


10
11
12
13
14
15
16
1
2
3
4
5
6
7
8

9
10
11
12
13
14
15
16
17








-
+
+







<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>"[target]" or "[target|label]"</nowiki>
     <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
  #  &lt;verbatim&gt; and &lt;nowiki&gt;

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>
29
30
31
32
33
34
35
36

37
38
39
40
41
42
43
44
45
46
47



48
49
50
51
52
53
54
55
56
57
58
59


60

61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78







30
31
32
33
34
35
36

37
38
39
40
41
42
43
44
45
46
47

48
49
50
51
52
53
54
55
56
57
58
59
60
61

62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90







-
+










-
+
+
+











-
+
+

+


















+
+
+
+
+
+
+
  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 numerials, use HTML.
      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, or a URL.
      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 
      "&lt;a name='anchor-name'&gt;&lt;/a&gt;" tag to your wiki page.

  6.  <b>HTML.</b>
      The following standard HTML elements may be used:
      &lt;a&gt; &lt;address&gt; &lt;article&gt; &lt;aside&gt; &lt;b&gt;
      &lt;big&gt; &lt;blockquote&gt; &lt;br&gt; &lt;center&gt; &lt;cite&gt;
      &lt;code&gt; &lt;col&gt; &lt;colgroup&gt; &lt;dd&gt; &lt;dfn&gt;
      &lt;code&gt; &lt;col&gt; &lt;colgroup&gt; &lt;dd&gt; 
      &lt;del&gt; &lt;dfn&gt;
      &lt;div&gt; &lt;dl&gt; &lt;dt&gt; &lt;em&gt; &lt;font&gt; &lt;footer&gt;
      &lt;ins&gt;
      &lt;h1&gt; &lt;h2&gt; &lt;h3&gt; &lt;h4&gt; &lt;h5&gt; &lt;h6&gt;
      &lt;header&gt; &lt;hr&gt; &lt;i&gt; &lt;img&gt; &lt;kbd&gt; &lt;li&gt;
      &lt;nav&gt; &lt;nobr&gt; &lt;nowiki&gt; &lt;ol&gt; &lt;p&gt; &lt;pre&gt;
      &lt;s&gt; &lt;samp&gt; &lt;section&gt; &lt;small&gt; &lt;span&gt;
      &lt;strike&gt; &lt;strong&gt; &lt;sub&gt; &lt;sup&gt; &lt;table&gt;
      &lt;tbody&gt; &lt;td&gt; &lt;tfoot&gt; &lt;th&gt; &lt;thead&gt;
      &lt;title&gt; &lt;tr&gt; &lt;tt&gt; &lt;u&gt; &lt;ul&gt; &lt;var&gt;
      &lt;verbatim&gt;. There are two non-standard elements available:
      &lt;verbatim&gt; and &lt;nowiki&gt;. 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 &lt;nowiki&gt; tag disables all wiki formatting rules through
      the matching &lt;/nowiki&gt; element. The &lt;verbatim&gt; tag works
      like &lt;pre&gt; with the addition that it also disables all wiki
      and HTML markup through the matching &lt;/verbatim&gt;.
      Text within 
      <tt>&lt;verbatim&nbsp;type="pikchr"&gt;...&lt;/verbatim&gt;</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.

27
28
29
30
31
32
33





34
35
36
37
38
39
40
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45







+
+
+
+
+







*/
#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 {
58
59
60
61
62
63
64

65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97














98
99
100
101
102
103
104
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89














90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110







+



















-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+







  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,
  /* re-use         = 0x00000800, */
  AMSK_FACE         = 0x00001000,
  AMSK_HEIGHT       = 0x00002000,
  AMSK_HREF         = 0x00004000,
  AMSK_HSPACE       = 0x00008000,
  AMSK_ID           = 0x00010000,
  AMSK_LINKS        = 0x00020000,
  AMSK_NAME         = 0x00040000,
  AMSK_ROWSPAN      = 0x00080000,
  AMSK_SIZE         = 0x00100000,
  AMSK_SRC          = 0x00200000,
  AMSK_START        = 0x00400000,
  AMSK_STYLE        = 0x00800000,
  AMSK_TARGET       = 0x01000000,
  AMSK_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
};

130
131
132
133
134
135
136

137
138
139
140
141
142
143
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150







+







  { "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          },
};

166
167
168
169
170
171
172
173

174

175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236




































































237
238
239
240
241
242
243
244
245
246
247
248
249
250



251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270




271
272
273

274
275

276
277
278
279
280
281
282
283
284
285
286
287
288



289
290
291
292
293
294
295
296
297
298
299

300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315

316
317
318
319
320
321
322
323

324
325
326
327
328

329
330
331
332
333
334
335
336
337
338
339

340
341
342
343
344
345


346
347
348
349
350
351
352
173
174
175
176
177
178
179

180
181
182






























































183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286

287
288
289
290
291
292

293
294

295

296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320

321

322
323
324
325
326
327
328
329
330
331
332
333

334

335

336
337
338
339
340
341
342
343
344
345
346
347

348
349
350
351
352
353
354
355
356
357
358

359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374







-
+

+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+














+
+
+



















-
+
+
+
+


-
+

-
+
-












+
+
+










-
+
-












-

-
+
-







+




-
+










-
+






+
+








/*
** 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 aAllowedMarkup[].
** in aMarkup[].
*/
enum markup_t {
#define MARKUP_INVALID            0
#define MARKUP_A                  1
#define MARKUP_ADDRESS            2
#define MARKUP_HTML5_ARTICLE      3
#define MARKUP_HTML5_ASIDE        4
#define MARKUP_B                  5
#define MARKUP_BIG                6
#define MARKUP_BLOCKQUOTE         7
#define MARKUP_BR                 8
#define MARKUP_CENTER             9
#define MARKUP_CITE               10
#define MARKUP_CODE               11
#define MARKUP_COL                12
#define MARKUP_COLGROUP           13
#define MARKUP_DD                 14
#define MARKUP_DFN                15
#define MARKUP_DIV                16
#define MARKUP_DL                 17
#define MARKUP_DT                 18
#define MARKUP_EM                 19
#define MARKUP_FONT               20
#define MARKUP_HTML5_FOOTER       21
#define MARKUP_H1                 22
#define MARKUP_H2                 23
#define MARKUP_H3                 24
#define MARKUP_H4                 25
#define MARKUP_H5                 26
#define MARKUP_H6                 27
#define MARKUP_HTML5_HEADER       28
#define MARKUP_HR                 29
#define MARKUP_I                  30
#define MARKUP_IMG                31
#define MARKUP_KBD                32
#define MARKUP_LI                 33
#define MARKUP_HTML5_NAV          34
#define MARKUP_NOBR               35
#define MARKUP_NOWIKI             36
#define MARKUP_OL                 37
#define MARKUP_P                  38
#define MARKUP_PRE                39
#define MARKUP_S                  40
#define MARKUP_SAMP               41
#define MARKUP_HTML5_SECTION      42
#define MARKUP_SMALL              43
#define MARKUP_SPAN               44
#define MARKUP_STRIKE             45
#define MARKUP_STRONG             46
#define MARKUP_SUB                47
#define MARKUP_SUP                48
#define MARKUP_TABLE              49
#define MARKUP_TBODY              50
#define MARKUP_TD                 51
#define MARKUP_TFOOT              52
#define MARKUP_TH                 53
#define MARKUP_THEAD              54
#define MARKUP_TITLE              55
#define MARKUP_TR                 56
#define MARKUP_TT                 57
#define MARKUP_U                  58
#define MARKUP_UL                 59
#define MARKUP_VAR                60
#define MARKUP_VERBATIM           61
  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_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 },
                    AMSK_ID|AMSK_CLASS|AMSK_STYLE },
 { "aside",         MARKUP_HTML5_ASIDE,  MUTYPE_BLOCK,
                                            AMSK_ID|AMSK_CLASS|AMSK_STYLE },
                    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 },
                    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 },
                    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 },
                    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 },
                    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  },
436
437
438
439
440
441
442

443
444
445
446
447
448
449
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472







+







  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 */
465
466
467
468
469
470
471
472

473
474
475
476
477
478
479
488
489
490
491
492
493
494

495
496
497
498
499
500
501
502







-
+








/*
** 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 htmlTagLength(const char *z){
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];
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
546
547
548
549
550
551
552



553
554

555

556


557
558

559
560





561
562
563
564
565
566
567
568







-
-
-
+

-
+
-

-
-
+

-
+

-
-
-
-
-
+







**      \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){
  int n = 0;
  int c, x1, x2;

  const char *zReject;
  if( flags & ALLOW_WIKI ){
    x1 = '[';
    zReject = "<&[\n";
    x2 = '\n';
  }else if( flags & ALLOW_LINKS ){
    x1 = '[';
    x2 = 0;
    zReject = "<&[";
  }else{
    x1 = x2 = 0;
    zReject = "<&";
  }
  while( (c = z[0])!=0 && c!='<' && c!='&' && c!=x1 && c!=x2 ){
    n++;
    z++;
  }
  return n;
  return strcspn(z, zReject);
}

/*
** Return true if z[] begins with an HTML character element.
*/
static int isElement(const char *z){
  int i;
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







-
+







    n++;
  }
  if( i<2 || fossil_isspace(z[n]) ) return 0;
  return n;
}

/*
** Check to see if the z[] string is the beginning of a enumeration value.
** 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.
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
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







-
+


















-
+







**
** 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 = htmlTagLength(z);
    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 if( fossil_isspace(z[1]) ){
      }else{
        *pTokenType = TOKEN_NEWLINE;
        return 1;
      }
    }
    if( (p->state & AT_NEWLINE)!=0 && fossil_isspace(z[0]) ){
      n = listItemLength(z, '*');
      if( n>0 ){
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







-
+







    i = 2;
  }else{
    p->endTag = 0;
    i = 1;
  }
  j = 0;
  while( fossil_isalnum(z[i]) ){
    if( j<sizeof(zTag)-1 ) zTag[j++] = fossil_tolower(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;
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







-
+







    if( c=='>' ) return 0;
  }
  while( fossil_isspace(z[i]) ){ i++; }
  while( c!='>' && p->nAttr<8 && fossil_isalpha(z[i]) ){
    int attrOk;    /* True to preserve attribute.  False to ignore it */
    j = 0;
    while( fossil_isalnum(z[i]) ){
      if( j<sizeof(zTag)-1 ) zTag[j++] = fossil_tolower(z[i]);
      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]!='=' ){
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
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







-
+
+
+
+




+
+
+
-
+
+








-
+


















-
+






-
+

-
+







        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]!='>' ){ z++; }
        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;
          z[i] = 0;
        }
      }
      i++;
    }
    if( attrOk ){
      seen |= aAttribute[iACode].iMask;
      p->nAttr++;
    }
    while( fossil_isspace(z[i]) ){ i++; }
    if( z[i]=='>' || (z[i]=='/' && z[i+1]=='>') ) break;
    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, "=\"%s%s\"", g.zTop, zVal);
          blob_appendf(pOut, "=\"%R%s\"", zVal);
        }else{
          blob_appendf(pOut, "=\"%s\"", zVal);
        }
      }
    }
    if (p->iType & MUTYPE_SINGLE){
      blob_append(pOut, " /", 2);
      blob_append_string(pOut, " /");
    }
    blob_append(pOut, ">", 1);
    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.
1045
1046
1047
1048
1049
1050
1051
1052

1053
1054
1055
1056
1057
1058
1059
1067
1068
1069
1070
1071
1072
1073

1074
1075
1076
1077
1078
1079
1080
1081







-
+







/*
** Begin a new paragraph if that something that is needed.
*/
static void startAutoParagraph(Renderer *p){
  if( p->wantAutoParagraph==0 ) return;
  if( p->state & WIKI_LINKSONLY ) return;
  if( p->wikiList==MARKUP_OL || p->wikiList==MARKUP_UL ) return;
  blob_append(p->pOut, "<p>", -1);
  blob_append_string(p->pOut, "<p>");
  p->wantAutoParagraph = 0;
  p->inAutoParagraph = 1;
}

/*
** End a paragraph if we are in one.
*/
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
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







-
+















-
+




-









-
-
+
+

-
+



-


















-
+






-
+








+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+














-
-
-
+
+
+


-
+
+


+
+
+
+
+




-
-
-
+

-
+

-
-
+
+
+



-
+
+



+
+
+

+
+
+
+
+
+
+






-
+

-
+


-
-
+
+

-
+


+






-
-
-
+
+
+



-
+




-
+


-
+




-
+


-
+



-
+




+
+
+
+
+
+
+
+
+
+
+
+


+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+

-
+
+


-
+


+
-
+

















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







  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>=sizeof(zU2) ) n = sizeof(zU2)-1;
  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.
*/
static int is_ticket(
int is_ticket(
  const char *zTarget,    /* Ticket UUID */
  int *pClosed            /* True if the ticket is closed */
){
  static Stmt q;
  static int once = 1;
  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( once ){
    const char *zClosedExpr = db_get("ticket-closed-expr", "status='Closed'");
  if( !db_static_stmt_is_init(&q) ){
    char *zClosedExpr = db_get("ticket-closed-expr", "status='Closed'");
    db_static_prepare(&q,
      "SELECT %s FROM ticket "
      "SELECT %z FROM ticket "
      " WHERE tkt_uuid>=:lwr AND tkt_uuid<:upr",
      zClosedExpr /*safe-for-%s*/
    );
    once = 0;
  }
  db_bind_text(&q, ":lwr", zLower);
  db_bind_text(&q, ":upr", zUpper);
  if( db_step(&q)==SQLITE_ROW ){
    rc = 1;
    *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(Renderer *p, const char *zTarget){
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)
   && ((p->state & WIKI_NOBADLINKS)==0 ||
   && ((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://www.fossil-scm.org/]
**    [https://www.fossil-scm.org/]
**    [ftp://www.fossil-scm.org/]
**    [http://fossil-scm.org/]
**    [https://fossil-scm.org/]
**    [ftp://fossil-scm.org/]
**    [mailto:fossil-users@lists.fossil-scm.org]
**
**    [/path]
**    [/path]        ->  Refers to the root of the Fossil hierarchy, not
**                       the root of the URI domain
**
**    [./relpath]
**    [../relpath]
**
**    [#fragment]
**
**    [0123456789abcdef]
**
**    [WikiPageName]
**    [wiki:WikiPageName]
**
**    [0123456789abcdef]
**
**    [#fragment]
**    [2010-02-27 07:13]
**
**    [2010-02-27 07:13]
**    [InterMap:Link]  ->  Interwiki link
*/
static void openHyperlink(
  Renderer *p,            /* Rendering context */
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 *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(p->pOut, "<a href=\"%s\">", zTarget);
    blob_appendf(pOut, "<a href=\"%s\"%s>", zTarget, zExtra);
  }else if( zTarget[0]=='/' ){
    blob_appendf(p->pOut, "<a href=\"%R%h\">", zTarget);
    blob_appendf(pOut, "<a href=\"%R%h\"%s>", zTarget, zExtra);
  }else if( zTarget[0]=='.'
         && (zTarget[1]=='/' || (zTarget[1]=='.' && zTarget[2]=='/'))
         && (p->state & WIKI_LINKSONLY)==0 ){
    blob_appendf(p->pOut, "<a href=\"%h\">", zTarget);
         && (mFlags & WIKI_LINKSONLY)==0 ){
    blob_appendf(pOut, "<a href=\"%h\"%s>", zTarget, zExtra);
  }else if( zTarget[0]=='#' ){
    blob_appendf(p->pOut, "<a href=\"%h\">", zTarget);
    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(p->pOut,
             "%z<span class=\"wikiTagCancelled\">[",
             href("%R/info/%s",zTarget)
          blob_appendf(pOut,
             "%z<span class=\"wikiTagCancelled\">%s",
             xhref(zExtraNS,"%R/info/%s",zTarget), zLB
          );
          zTerm = "]</span></a>";
        }else{
          blob_appendf(p->pOut,"<span class=\"wikiTagCancelled\">[");
          blob_appendf(pOut,"<span class=\"wikiTagCancelled\">%s", zLB);
          zTerm = "]</span>";
        }
      }else{
        if( g.perm.Hyperlink ){
          blob_appendf(p->pOut,"%z[", href("%R/info/%s", zTarget));
          blob_appendf(pOut,"%z%s", xhref(zExtraNS,"%R/info/%s", zTarget),zLB);
          zTerm = "]</a>";
        }else{
          blob_appendf(p->pOut, "[");
          blob_appendf(pOut, "%s", zLB);
          zTerm = "]";
        }
      }
    }else if( !in_this_repo(zTarget) ){
      if( (p->state & (WIKI_LINKSONLY|WIKI_NOBADLINKS))!=0 ){
      if( (mFlags & (WIKI_LINKSONLY|WIKI_NOBADLINKS))!=0 ){
        zTerm = "";
      }else{
        blob_appendf(p->pOut, "<span class=\"brokenlink\">[");
        blob_appendf(pOut, "<span class=\"brokenlink\">%s", zLB);
        zTerm = "]</span>";
      }
    }else if( g.perm.Hyperlink ){
      blob_appendf(p->pOut, "%z[",href("%R/info/%s", zTarget));
      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(p->pOut, "<a href=\"%R/timeline?c=%T\">", zTarget);
  }else if( (z = validWikiPageName(p, zTarget))!=0 ){
    blob_appendf(p->pOut, "<a href=\"%R/wiki?name=%T\">", z);
  }else if( zTarget>=&zOrig[2] && !fossil_isspace(zTarget[-2]) ){
    /* Probably an array subscript in code */
    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( (p->state & (WIKI_NOBADLINKS|WIKI_LINKSONLY))!=0 ){
  }else if( (mFlags & (WIKI_NOBADLINKS|WIKI_LINKSONLY))!=0 ){
    /* Also ignore the link if various flags are set */
    zTerm = "";
  }else{
    blob_appendf(p->pOut, "<span class=\"brokenlink\">[%h]", zTarget);
    blob_appendf(pOut, "<span class=\"brokenlink\">[%h]", zTarget);
    zTerm = "</span>";
  }
  if( zExtra ) fossil_free(zExtra);
  assert( strlen(zTerm)<nClose );
  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;
  return aMarkup[p->aStack[p->nStack-1].iCode].iType;
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
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







-
-
+
+






-
+






+
+
+
-
+
+





-
+







-
+





-
+





-
+







-
+





-
+













-
+













-
+








-
+

-
+







    }else{
      n = nextWikiToken(z, p, &tokenType);
    }
    p->state &= ~(AT_NEWLINE|AT_PARAGRAPH);
    switch( tokenType ){
      case TOKEN_PARAGRAPH: {
        if( inlineOnly ){
          /* blob_append(p->pOut, " &para; ", -1); */
          blob_append(p->pOut, " &nbsp;&nbsp; ", -1);
          /* blob_append_string(p->pOut, " &para; "); */
          blob_append_string(p->pOut, " &nbsp;&nbsp; ");
        }else{
          if( p->wikiList ){
            popStackToTag(p, p->wikiList);
            p->wikiList = 0;
          }
          endAutoParagraph(p);
          blob_append(p->pOut, "\n\n", 1);
          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(p->pOut, "\n", 1);
          blob_append_string(p->pOut, "\n");
        }
        p->state |= AT_NEWLINE;
        break;
      }
      case TOKEN_BUL_LI: {
        if( inlineOnly ){
          blob_append(p->pOut, " &bull; ", -1);
          blob_append_string(p->pOut, " &bull; ");
        }else{
          if( p->wikiList!=MARKUP_UL ){
            if( p->wikiList ){
              popStackToTag(p, p->wikiList);
            }
            endAutoParagraph(p);
            pushStack(p, MARKUP_UL);
            blob_append(p->pOut, "<ul>", 4);
            blob_append_string(p->pOut, "<ul>");
            p->wikiList = MARKUP_UL;
          }
          popStackToTag(p, MARKUP_LI);
          startAutoParagraph(p);
          pushStack(p, MARKUP_LI);
          blob_append(p->pOut, "<li>", 4);
          blob_append_string(p->pOut, "<li>");
        }
        break;
      }
      case TOKEN_NUM_LI: {
        if( inlineOnly ){
          blob_append(p->pOut, " # ", -1);
          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(p->pOut, "<ol>", 4);
            blob_append_string(p->pOut, "<ol>");
            p->wikiList = MARKUP_OL;
          }
          popStackToTag(p, MARKUP_LI);
          startAutoParagraph(p);
          pushStack(p, MARKUP_LI);
          blob_append(p->pOut, "<li>", 4);
          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(p->pOut, "<ol>", 4);
            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(p->pOut, "<blockquote>", -1);
          blob_append_string(p->pOut, "<blockquote>");
          p->wantAutoParagraph = 0;
          p->wikiList = MARKUP_BLOCKQUOTE;
        }
        break;
      }
      case TOKEN_CHARACTER: {
        startAutoParagraph(p);
        if( z[0]=='<' ){
          blob_append(p->pOut, "&lt;", 4);
          blob_append_string(p->pOut, "&lt;");
        }else if( z[0]=='&' ){
          blob_append(p->pOut, "&amp;", 5);
          blob_append_string(p->pOut, "&amp;");
        }
        break;
      }
      case TOKEN_LINK: {
        char *zTarget;
        char *zDisplay = 0;
        int i, j;
1443
1444
1445
1446
1447
1448
1449
1450

1451
1452
1453

1454

1455
1456
1457
1458
1459
1460
1461
1591
1592
1593
1594
1595
1596
1597

1598
1599
1600
1601
1602

1603
1604
1605
1606
1607
1608
1609
1610







-
+



+
-
+







            iS1 = j;
            cS1 = z[j];
            z[j] = 0;
          }
        }
        z[i] = 0;
        if( zDisplay==0 ){
          zDisplay = zTarget;
          zDisplay = zTarget + interwiki_removable_prefix(zTarget);
        }else{
          while( fossil_isspace(*zDisplay) ) zDisplay++;
        }
        wiki_resolve_hyperlink(p->pOut, p->state,
        openHyperlink(p, zTarget, zClose, sizeof(zClose), zOrig);
                               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);
          }
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
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







-
+


















-
+


-
+










-
+







        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(p->pOut, "</pre>", 6);
            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(p->pOut, "</pre>", 6);
            blob_append_string(p->pOut, "</pre>");
          }else{
            unparseMarkup(&markup);
            blob_append(p->pOut, "&lt;", 4);
            blob_append_string(p->pOut, "&lt;");
            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(p->pOut, "&lt;", 4);
          blob_append_string(p->pOut, "&lt;");
          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 ){
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
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







-
+
+








-
-
+
-





-
-
-
+
+
+
+
+
+
+
+
+
+
+







-
+














-
+

+







        }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;
          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 ){
              blob_appendf(p->pOut, "<pre name='code' class='%s'>",
                markup.aAttr[ii].zValue);
              zClass = markup.aAttr[ii].zValue;
              vAttrDidAppend=1;
            }else if( markup.aAttr[ii].iACode==ATTR_LINKS
                   && !is_false(markup.aAttr[ii].zValue) ){
              p->state |= ALLOW_LINKS;
            }
          }
          if( !vAttrDidAppend ) {
            endAutoParagraph(p);
            blob_append(p->pOut, "<pre class='verbatim'>",-1);
          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(p->pOut, "<ul>", 4);
            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(p->pOut, "<tr>", 4);
              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);
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
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







-
-
-




















-
+



-
-
-
-
-
-
-
-
-
-




+
+
+








+










+
+
+
+







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







*/
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_NOBLOCK ){
    renderer.state |= INLINE_MARKUP_ONLY;
  }
  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(renderer.pOut, "\n", 1);
  blob_append_char(renderer.pOut, '\n');
  free(renderer.aStack);
}

/*
** Send a string as wiki to CGI output.
*/
void wiki_write(const char *zIn, int flags){
  Blob in;
  blob_init(&in, zIn, -1);
  wiki_convert(&in, 0, flags);
  blob_reset(&in);
}

/*
** 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
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
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







-
+
-
-
-



















-
-
-
-










-
+
-




-
-
-
-
+
-
-
-
-
-
-
-







**
** 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 */
  int srcid,         /* srcid field for new BACKLINK table entries */
  Backlink *pBklnk,  /* Backlink extraction context */
  int srctype,       /* srctype field for new BACKLINK table entries */
  double mtime,      /* mtime field for new BACKLINK table entries */
  int replaceFlag,   /* True first delete prior BACKLINK entries */
  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;
  if( replaceFlag ){
    db_multi_exec("DELETE FROM backlink WHERE srctype=%d AND srcid=%d",
                  srctype, srcid);
  }

  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, c;
        int i;
        char zLink[HNAME_MAX+4];

        zTarget = &z[1];
        for(i=0; zTarget[i] && zTarget[i]!='|' && zTarget[i]!=']'; i++){}
        while(i>1 && zTarget[i-1]==' '){ i--; }
        c = zTarget[i];
        zTarget[i] = 0;
        if( is_valid_hname(zTarget) ){
          memcpy(zLink, zTarget, i+1);
        backlink_create(pBklnk, zTarget, i);
          canonical16(zLink, i);
          db_multi_exec(
             "REPLACE INTO backlink(target,srctype,srcid,mtime)"
             "VALUES(%Q,%d,%d,%g)", zLink, srctype, srcid, mtime
          );
        }
        zTarget[i] = c;
        break;
      }
      case TOKEN_MARKUP: {
        const char *zId;
        int iDiv;
        parseMarkup(&markup, z);

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
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







-
+
-
-
-

-
+



-
+
















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







    }
    z += n;
  }
  free(renderer.aStack);
}

/*
** Get the next HTML token.
** Return the length, in bytes, of the HTML token that z is pointing to.
**
** z points to the start of a token.  Return the number of
** characters in that token.
*/
static int nextHtmlToken(const char *z){
int html_token_length(const char *z){
  int n;
  char c;
  if( (c=z[0])=='<' ){
    n = htmlTagLength(z);
    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:
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
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







-
+


















-
+


-
+













-
+







-
+


-
+







-
+











-
+


-
+









-
+

















-
+




















-
+















-
+











-
+











-
+







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 = nextHtmlToken(zIn);
    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(pOut, "\n", 1); iCur = 0; }
          if( nPre==0 ){ blob_append_char(pOut, '\n'); iCur = 0; }
          continue;
        }else{
          if( iCur && nPre==0 ){ blob_append(pOut, "\n", 1); iCur = 0; }
          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(pOut, "\n", 1);
          blob_append_char(pOut, '\n');
          iCur = 0;
        }
        wantSpace = 0;
        omitSpace = 1;
      }
      if( wantSpace && nPre==0 ){
        if( iCur+n+1>=80 ){
          blob_append(pOut, "\n", 1);
          blob_append_char(pOut, '\n');
          iCur = 0;
        }else{
          blob_append(pOut, " ", 1);
          blob_append_char(pOut, ' ');
          iCur++;
        }
      }
      blob_append(pOut, zIn, n);
      iCur += n;
      wantSpace = 0;
      if( eTag==MARKUP_BR || eTag==MARKUP_HR ){
        blob_append(pOut, "\n", 1);
        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(pOut, "\n", 1);
          blob_append_char(pOut, '\n');
          iCur = 0;
        }else{
          blob_append(pOut, " ", 1);
          blob_append_char(pOut, ' ');
          iCur++;
        }
      }
      blob_append(pOut, zIn, n);
      iCur += n;
      wantSpace = omitSpace = 0;
    }
    zIn += n;
  }
  if( iCur ) blob_append(pOut, "\n", 1);
  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_str(&out), 0);
    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 = nextHtmlToken(zIn);
    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 = nextHtmlToken(zIn);
          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(pOut, "\n", 1);
          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(pOut, nNL ? "\n" : " ", 1);
          blob_append_char(pOut, nNL ? '\n' : ' ');
          nWS = 1;
        }
      }
    }else if( zIn[0]=='&' ){
      char c = '?';
      if( zIn[1]=='#' ){
        int x = atoi(&zIn[1]);
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
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







-
+



-
+


-
+


-
+






-
+







          if( aEntity[jj].n==n && strncmp(aEntity[jj].z,zIn,n)==0 ){
            c = aEntity[jj].c;
            break;
          }
        }
      }
      if( fossil_isspace(c) ){
        if( nWS==0 && seenText ) blob_append(pOut, &c, 1);
        if( nWS==0 && seenText ) blob_append_char(pOut, c);
        nWS = 1;
        nNL = c=='\n';
      }else{
        if( !seenText && !inTitle ) blob_append(pOut, "\n", 1);
        if( !seenText && !inTitle ) blob_append_char(pOut, '\n');
        seenText = 1;
        nNL = nWS = 0;
        blob_append(pOut, &c, 1);
        blob_append_char(pOut, c);
      }
    }else{
      if( !seenText && !inTitle ) blob_append(pOut, "\n", 1);
      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(pOut, "\n", 1);
  if( nNL==0 ) blob_append_char(pOut, '\n');
}

/*
** COMMAND: test-html-to-text
**
** Usage: %fossil test-html-to-text FILE ...
**
2233
2234
2235
2236
2237
2238
2239
2240

2241
2242
2243





























































































































































































































































































































































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







-
+



+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
  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_str(&out), 0);
    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'>&lt;/%s&gt;</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, "&lt;", 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'>&lt;%.*s&gt;</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);
  }
}

Changes to src/winfile.c.

280
281
282
283
284
285
286
287

288
289
290
291
292
293
294
280
281
282
283
284
285
286

287
288
289
290
291
292
293
294







-
+







** 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_panic("cannot find current working directory.");
    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);
}

Changes to src/winhttp.c.

201
202
203
204
205
206
207
208

209
210
211
212
213
214

215
216
217
218

219
220
221
222
223


224
225
226
227
228
229
230
201
202
203
204
205
206
207

208
209
210
211
212
213

214
215
216
217

218
219
220
221


222
223
224
225
226
227
228
229
230







-
+





-
+



-
+



-
-
+
+







};

/*
** Accepts connections on DualSocket.
*/
static void DualSocket_accept(DualSocket* pListen, DualSocket* pClient,
                              DualAddr* pClientAddr){
	fd_set rs;
  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 ){
  if( pListen->s4!=INVALID_SOCKET ){
    FD_SET(pListen->s4, &rs);
    ++rs_count;
  }
	if( pListen->s6!=INVALID_SOCKET ){
  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( 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,
286
287
288
289
290
291
292
293

294
295
296
297
298
299
300
286
287
288
289
290
291
292

293
294
295
296
297
298
299
300







-
+







** Issue a fatal error.
*/
static NORETURN void winhttp_fatal(
  const char *zOp,
  const char *zService,
  const char *zErr
){
  fossil_panic("unable to %s service '%s': %s", zOp, zService, 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){
338
339
340
341
342
343
344

345
346
347
348
349


350
351
352
353
354
355
356
357





358
359
360









361
362
363
364
365
366
367


368
369

370





371
372
373




374
375
376

377





378
379
380
381





382
383
384
385
386
387
388
389
390


391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411



412
413
414
415



416
417
418
419








420
421
422
423
424
425
426
427





428
429
430
431
432

433
434
435




436
437
438
439
440
441
442
338
339
340
341
342
343
344
345
346
347
348
349

350
351
352
353
354
355
356
357
358
359
360
361
362
363
364



365
366
367
368
369
370
371
372
373
374
375
376
377
378


379
380
381

382
383
384
385
386
387
388



389
390
391
392
393
394

395
396
397
398
399
400
401




402
403
404
405
406
407
408
409
410
411
412
413


414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434


435
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







+




-
+
+








+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+





-
-
+
+

-
+

+
+
+
+
+
-
-
-
+
+
+
+


-
+

+
+
+
+
+
-
-
-
-
+
+
+
+
+







-
-
+
+



















-
-
+
+
+




+
+
+


-
-
+
+
+
+
+
+
+
+








+
+
+
+
+





+
-
-
-
+
+
+
+







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 zHdr[4000];          /* The HTTP request header */
  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<sizeof(zHdr) ){
    got = recv(p->s, &zHdr[amt], sizeof(zHdr)-1-amt, 0);
    if( got==SOCKET_ERROR ) goto end_request;
  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;
    zHdr[amt] = 0;
    z = strstr(zHdr, "\r\n\r\n");
    zBuf[amt] = 0;
    z = strstr(zBuf, "\r\n\r\n");
    if( z ){
      wanted = find_content_length(zHdr) + (&z[4]-zHdr) - amt;
      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>=sizeof(zHdr) ) goto end_request;
      }
    }
  }
  if( amt>=szHdr ) goto end_request;
  out = fossil_fopen(zRequestFName, "wb");
  if( out==0 ) goto end_request;
  fwrite(zHdr, 1, amt, out);
  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, zHdr, sizeof(zHdr), 0);
    if( got==SOCKET_ERROR ) goto end_request;
    if( got ){
      fwrite(zHdr, 1, got, out);
      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 checkout.  This
  ** is designed to allow the open checkout for the interactive user to work
  ** 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\" --nossl%s",
    g.nameOfExe, zCmdFName, p->zOptions
    "\"%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(zHdr, 1, sizeof(zHdr), in))>0 ){
      send(p->s, zHdr, got, 0);
    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); }
    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){
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
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







+
+







+
+
+

-
+
+


-
+
+
+
+
+
+













+
+
+
+




+
+
+
+
+
+
+
+



-
+







  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 %s", zBaseUrl);
    blob_appendf(&options, " --baseurl ");
    blob_append_escaped_arg(&options, zBaseUrl, 0);
  }
  if( zNotFound ){
    blob_appendf(&options, " --notfound %s", 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( zSavedKey!=0 && savedKeySize>0 ){
  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");
  }
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
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







+

-
+

-
+






+
+
+
+
+
+
+
+
+
-
-
+
+
+


-
+
+







        iPort++;
        continue;
      }
    }
    break;
  }
  if( iPort>mxPort ){
    /* These exits are merely fatal because firewall settings can cause them. */
    if( mnPort==mxPort ){
      fossil_panic("unable to open listening socket on port %d", mnPort);
      fossil_fatal("unable to open listening socket on port %d", mnPort);
    }else{
      fossil_panic("unable to open listening socket on any"
      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);
    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":"HTTP", iPort);
               (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. */
860
861
862
863
864
865
866
867

868
869
870
871
872
873
874
937
938
939
940
941
942
943

944
945
946
947
948
949
950
951







-
+







    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
** a interactive console session, this routine fails and returns a non zero
** 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 */
889
890
891
892
893
894
895
896

897
898
899
900
901
902
903
966
967
968
969
970
971
972

973
974
975
976
977
978
979
980







-
+







  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_panic("error from StartServiceCtrlDispatcher()");
      fossil_fatal("error from StartServiceCtrlDispatcher()");
    }
  }
  return 0;
}

/* Duplicate #ifdef needed for mkindex */
#ifdef _WIN32
929
930
931
932
933
934
935
936

937
938
939
940
941
942
943
1006
1007
1008
1009
1010
1011
1012

1013
1014
1015
1016
1017
1018
1019
1020







-
+







**
**              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.
**
**         -U|--username USERNAME
**         --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.
**
953
954
955
956
957
958
959
960

961
962
963
964

965
966
967
968
969
970
971
1030
1031
1032
1033
1034
1035
1036

1037
1038
1039
1040

1041
1042
1043
1044
1045
1046
1047
1048







-
+



-
+







**              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 REPOSITORY
**         -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 checkout.
**              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
**
1033
1034
1035
1036
1037
1038
1039
1040

1041
1042
1043
1044
1045
1046
1047
1110
1111
1112
1113
1114
1115
1116

1117
1118
1119
1120
1121
1122
1123
1124







-
+







    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", "U", 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;
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305






1306
1307
1308
1309
1310
1311
1312
1370
1371
1372
1373
1374
1375
1376






1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389







-
-
-
-
-
-
+
+
+
+
+
+







    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); 
      } 
      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 ){

Deleted src/wysiwyg.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








































































































































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
/*
** 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 that generates WYSIWYG text editors on
** web pages.
*/
#include "config.h"
#include <assert.h>
#include <ctype.h>
#include "wysiwyg.h"


/*
** Output code for a WYSIWYG editor.  The caller must have already generated
** the <form> that will contain the editor, and the call must generate the
** corresponding </form> after this routine returns.  The caller must include
** an onsubmit= attribute on the <form> element that invokes the
** wysiwygSubmit() function.
**
** There can only be a single WYSIWYG editor per frame.
*/
void wysiwygEditor(
  const char *zId,        /* ID for this editor */
  const char *zContent,   /* Initial content (HTML) */
  int w, int h            /* Initial width and height */
){

  @ <style type="text/css">
  @ .intLink { cursor: pointer; }
  @ img.intLink { border: 0; }
  @ #wysiwygBox {
  @   border: 1px #000000 solid;
  @   padding: 12px;
  @ }
  @ #editMode label { cursor: pointer; }
  @ </style>

  @ <input id="wysiwygValue" type="hidden" name="%s(zId)">
  @ <div id="editModeDiv">Edit mode:
  @   <select id="editMode" size=1>
  @ <option value="0">WYSIWYG</option>
  @ <option value="1">Raw HTML</option>
  @ </select></div>
  @ <div id="toolBar1">
  @ <select class="format" data-format="formatblock">
  @ <option selected>- formatting -</option>
  @ <option value="h1">Title 1 &lt;h1&gt;</option>
  @ <option value="h2">Title 2 &lt;h2&gt;</option>
  @ <option value="h3">Title 3 &lt;h3&gt;</option>
  @ <option value="h4">Title 4 &lt;h4&gt;</option>
  @ <option value="h5">Title 5 &lt;h5&gt;</option>
  @ <option value="h6">Subtitle &lt;h6&gt;</option>
  @ <option value="p">Paragraph &lt;p&gt;</option>
  @ <option value="pre">Preformatted &lt;pre&gt;</option>
  @ </select>
  @ <select class="format" data-format="fontname">
  @ <option class="heading" selected>- font -</option>
  @ <option>Arial</option>
  @ <option>Arial Black</option>
  @ <option>Courier New</option>
  @ <option>Times New Roman</option>
  @ </select>
  @ <select class="format" data-format="fontsize">
  @ <option class="heading" selected>- size -</option>
  @ <option value="1">Very small</option>
  @ <option value="2">A bit small</option>
  @ <option value="3">Normal</option>
  @ <option value="4">Medium-large</option>
  @ <option value="5">Big</option>
  @ <option value="6">Very big</option>
  @ <option value="7">Maximum</option>
  @ </select>
  @ <select class="format" data-format="forecolor">
  @ <option class="heading" selected>- color -</option>
  @ <option value="red">Red</option>
  @ <option value="blue">Blue</option>
  @ <option value="green">Green</option>
  @ <option value="black">Black</option>
  @ </select>
  @ </div>
  @ <div id="toolBar2">
  @ <img class="intLink" title="Undo" data-format="undo"
  @ src="data:image/gif;base64,R0lGODlhFgAWAOMKADljwliE33mOrpGjuYKl8aezxqPD+7
  @ /I19DV3NHa7P///////////////////////yH5BAEKAA8ALAAAAAAWABYAAARR8MlJq704680
  @ 7TkaYeJJBnES4EeUJvIGapWYAC0CsocQ7SDlWJkAkCA6ToMYWIARGQF3mRQVIEjkkSVLIbSfE
  @ whdRIH4fh/DZMICe3/C4nBQBADs=">

  @ <img class="intLink" title="Redo" data-format="redo"
  @ src="data:image/gif;base64,R0lGODlhFgAWAMIHAB1ChDljwl9vj1iE34Kl8aPD+7/I1/
  @ ///yH5BAEKAAcALAAAAAAWABYAAANKeLrc/jDKSesyphi7SiEgsVXZEATDICqBVJjpqWZt9Na
  @ EDNbQK1wCQsxlYnxMAImhyDoFAElJasRRvAZVRqqQXUy7Cgx4TC6bswkAOw==">

  @ <img class="intLink" title="Remove formatting" data-format="removeFormat"
  @ src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AA
  @ AABGdBTUEAALGPC/xhBQAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAOxAAADsQBlSsOGwA
  @ AAAd0SU1FB9oECQMCKPI8CIIAAAAIdEVYdENvbW1lbnQA9syWvwAAAuhJREFUOMtjYBgFxAB5
  @ 01ZWBvVaL2nHnlmk6mXCJbF69zU+Hz/9fB5O1lx+bg45qhl8/fYr5it3XrP/YWTUvvvk3VeqG
  @ Xz70TvbJy8+Wv39+2/Hz19/mGwjZzuTYjALuoBv9jImaXHeyD3H7kU8fPj2ICML8z92dlbtMz
  @ deiG3fco7J08foH1kurkm3E9iw54YvKwuTuom+LPt/BgbWf3//sf37/1/c02cCG1lB8f//f95
  @ DZx74MTMzshhoSm6szrQ/a6Ir/Z2RkfEjBxuLYFpDiDi6Af///2ckaHBp7+7wmavP5n76+P2C
  @ lrLIYl8H9W36auJCbCxM4szMTJac7Kza////R3H1w2cfWAgafPbqs5g7D95++/P1B4+ECK8tA
  @ wMDw/1H7159+/7r7ZcvPz4fOHbzEwMDwx8GBgaGnNatfHZx8zqrJ+4VJBh5CQEGOySEua/v3n
  @ 7hXmqI8WUGBgYGL3vVG7fuPK3i5GD9/fja7ZsMDAzMG/Ze52mZeSj4yu1XEq/ff7W5dvfVAS1
  @ lsXc4Db7z8C3r8p7Qjf///2dnZGxlqJuyr3rPqQd/Hhyu7oSpYWScylDQsd3kzvnH738wMDzj
  @ 5GBN1VIWW4c3KDon7VOvm7S3paB9u5qsU5/x5KUnlY+eexQbkLNsErK61+++VnAJcfkyMTIwf
  @ fj0QwZbJDKjcETs1Y8evyd48toz8y/ffzv//vPP4veffxpX77z6l5JewHPu8MqTDAwMDLzyrj
  @ b/mZm0JcT5Lj+89+Ybm6zz95oMh7s4XbygN3Sluq4Mj5K8iKMgP4f0////fv77//8nLy+7MCc
  @ XmyYDAwODS9jM9tcvPypd35pne3ljdjvj26+H2dhYpuENikgfvQeXNmSl3tqepxXsqhXPyc66
  @ 6s+fv1fMdKR3TK72zpix8nTc7bdfhfkEeVbC9KhbK/9iYWHiErbu6MWbY/7//8/4//9/pgOnH
  @ 6jGVazvFDRtq2VgiBIZrUTIBgCk+ivHvuEKwAAAAABJRU5ErkJggg==">

  @ <img class="intLink" title="Bold" data-format="bold"
  @ src="data:image/gif;base64,R0lGODlhFgAWAID/AMDAwAAAACH5BAEAAAAALAAAAAAWAB
  @ YAQAInhI+pa+H9mJy0LhdgtrxzDG5WGFVk6aXqyk6Y9kXvKKNuLbb6zgMFADs=" />

  @ <img class="intLink" title="Italic" data-format="italic"
  @ src="data:image/gif;base64,R0lGODlhFgAWAKEDAAAAAF9vj5WIbf///yH5BAEAAAMALA
  @ AAAAAWABYAAAIjnI+py+0Po5x0gXvruEKHrF2BB1YiCWgbMFIYpsbyTNd2UwAAOw==" />

  @ <img class="intLink" title="Underline" data-format="underline"
  @ src="data:image/gif;base64,R0lGODlhFgAWAKECAAAAAF9vj////////yH5BAEAAAIALA
  @ AAAAAWABYAAAIrlI+py+0Po5zUgAsEzvEeL4Ea15EiJJ5PSqJmuwKBEKgxVuXWtun+DwxCCgA
  @ 7" />

  @ <img class="intLink" title="Left align" data-format="justifyleft"
  @ src="data:image/gif;base64,R0lGODlhFgAWAID/AMDAwAAAACH5BAEAAAAALAAAAAAWAB
  @ YAQAIghI+py+0Po5y02ouz3jL4D4JMGELkGYxo+qzl4nKyXAAAOw==" />

  @ <img class="intLink" title="Center align" data-format="justifycenter"
  @ src="data:image/gif;base64,R0lGODlhFgAWAID/AMDAwAAAACH5BAEAAAAALAAAAAAWAB
  @ YAQAIfhI+py+0Po5y02ouz3jL4D4JOGI7kaZ5Bqn4sycVbAQA7" />

  @ <img class="intLink" title="Right align" data-format="justifyright"
  @ src="data:image/gif;base64,R0lGODlhFgAWAID/AMDAwAAAACH5BAEAAAAALAAAAAAWAB
  @ YAQAIghI+py+0Po5y02ouz3jL4D4JQGDLkGYxouqzl43JyVgAAOw==" />
  @ <img class="intLink" title="Numbered list"
  @ data-format="insertorderedlist"
  @ src="data:image/gif;base64,R0lGODlhFgAWAMIGAAAAADljwliE35GjuaezxtHa7P////
  @ ///yH5BAEAAAcALAAAAAAWABYAAAM2eLrc/jDKSespwjoRFvggCBUBoTFBeq6QIAysQnRHaEO
  @ zyaZ07Lu9lUBnC0UGQU1K52s6n5oEADs=" />

  @ <img class="intLink" title="Dotted list" 
  @ data-format="insertunorderedlist"
  @ src="data:image/gif;base64,R0lGODlhFgAWAMIGAAAAAB1ChF9vj1iE33mOrqezxv////
  @ ///yH5BAEAAAcALAAAAAAWABYAAAMyeLrc/jDKSesppNhGRlBAKIZRERBbqm6YtnbfMY7lud6
  @ 4UwiuKnigGQliQuWOyKQykgAAOw==" />

  @ <img class="intLink" title="Quote" data-format="formatblock"
  @ src="data:image/gif;base64,R0lGODlhFgAWAIQXAC1NqjFRjkBgmT9nqUJnsk9xrFJ7u2
  @ R9qmKBt1iGzHmOrm6Sz4OXw3Odz4Cl2ZSnw6KxyqO306K63bG70bTB0rDI3bvI4P/////////
  @ //////////////////////////yH5BAEKAB8ALAAAAAAWABYAAAVP4CeOZGmeaKqubEs2Cekk
  @ ErvEI1zZuOgYFlakECEZFi0GgTGKEBATFmJAVXweVOoKEQgABB9IQDCmrLpjETrQQlhHjINrT
  @ q/b7/i8fp8PAQA7" />

  @ <img class="intLink" title="Delete indentation" data-format="outdent"
  @ src="data:image/gif;base64,R0lGODlhFgAWAMIHAAAAADljwliE35GjuaezxtDV3NHa7P
  @ ///yH5BAEAAAcALAAAAAAWABYAAAM2eLrc/jDKCQG9F2i7u8agQgyK1z2EIBil+TWqEMxhMcz
  @ sYVJ3e4ahk+sFnAgtxSQDqWw6n5cEADs=" />

  @ <img class="intLink" title="Add indentation" data-format="indent"
  @ src="data:image/gif;base64,R0lGODlhFgAWAOMIAAAAADljwl9vj1iE35GjuaezxtDV3N
  @ Ha7P///////////////////////////////yH5BAEAAAgALAAAAAAWABYAAAQ7EMlJq704650
  @ B/x8gemMpgugwHJNZXodKsO5oqUOgo5KhBwWESyMQsCRDHu9VOyk5TM9zSpFSr9gsJwIAOw==">

  @ <img class="intLink" title="Hyperlink" data-format="createlink"
  @ src="data:image/gif;base64,R0lGODlhFgAWAOMKAB1ChDRLY19vj3mOrpGjuaezxrCztb
  @ /I19Ha7Pv8/f///////////////////////yH5BAEKAA8ALAAAAAAWABYAAARY8MlJq704682
  @ 7/2BYIQVhHg9pEgVGIklyDEUBy/RlE4FQF4dCj2AQXAiJQDCWQCAEBwIioEMQBgSAFhDAGghG
  @ i9XgHAhMNoSZgJkJei33UESv2+/4vD4TAQA7" />

#if 0  /* Cut/Copy/Paste requires special browser permissions for security
       ** reasons.  So omit these buttons */
  @ <img class="intLink" title="Cut" data-format="cut"
  @ src="data:image/gif;base64,R0lGODlhFgAWAIQSAB1ChBFNsRJTySJYwjljwkxwl19vj1
  @ dusYODhl6MnHmOrpqbmpGjuaezxrCztcDCxL/I18rL1P/////////////////////////////
  @ //////////////////////////yH5BAEAAB8ALAAAAAAWABYAAAVu4CeOZGmeaKqubDs6TNnE
  @ bGNApNG0kbGMi5trwcA9GArXh+FAfBAw5UexUDAQESkRsfhJPwaH4YsEGAAJGisRGAQY7UCC9
  @ ZAXBB+74LGCRxIEHwAHdWooDgGJcwpxDisQBQRjIgkDCVlfmZqbmiEAOw==" />

  @ <img class="intLink" title="Copy" data-format="copy"
  @ src="data:image/gif;base64,R0lGODlhFgAWAIQcAB1ChBFNsTRLYyJYwjljwl9vj1iE31
  @ iGzF6MnHWX9HOdz5GjuYCl2YKl8ZOt4qezxqK63aK/9KPD+7DI3b/I17LM/MrL1MLY9NHa7OP
  @ s++bx/Pv8/f///////////////yH5BAEAAB8ALAAAAAAWABYAAAWG4CeOZGmeaKqubOum1SQ/
  @ kPVOW749BeVSus2CgrCxHptLBbOQxCSNCCaF1GUqwQbBd0JGJAyGJJiobE+LnCaDcXAaEoxhQ
  @ ACgNw0FQx9kP+wmaRgYFBQNeAoGihCAJQsCkJAKOhgXEw8BLQYciooHf5o7EA+kC40qBKkAAA
  @ Grpy+wsbKzIiEAOw==" />

  @ <img class="intLink" title="Paste" data-format="paste"
  @ src="data:image/gif;base64,R0lGODlhFgAWAIQUAD04KTRLY2tXQF9vj414WZWIbXmOrp
  @ qbmpGjudClFaezxsa0cb/I1+3YitHa7PrkIPHvbuPs+/fvrvv8/f/////////////////////
  @ //////////////////////////yH5BAEAAB8ALAAAAAAWABYAAAWN4CeOZGmeaKqubGsusPvB
  @ SyFJjVDs6nJLB0khR4AkBCmfsCGBQAoCwjF5gwquVykSFbwZE+AwIBV0GhFog2EwIDchjwRiQ
  @ o9E2Fx4XD5R+B0DDAEnBXBhBhN2DgwDAQFjJYVhCQYRfgoIDGiQJAWTCQMRiwwMfgicnVcAAA
  @ MOaK+bLAOrtLUyt7i5uiUhADs=" />
#endif

  @ </div>
  @ <div id="wysiwygBox"
  @  style="resize:both;overflow:auto;width:95%%;min-height:%d(h)em;"
  @  contenteditable="true">%s(zContent)</div>
  @ <script nonce="%h(style_nonce())">
  @ var oDoc;
  @
  @ /* Initialize the document editor */
  @ function initDoc() {
  @   initEventHandlers();
  @   oDoc = document.getElementById("wysiwygBox");
  @   if (!isWysiwyg()) { setDocMode(true); }
  @ }
  @
  @ function initEventHandlers() {
  @     document.querySelector('form').onsubmit = wysiwygSubmit;
  @     document.querySelector('#editMode').onchange = function() { 
  @         setDocMode(this.selectedIndex)
  @     };
  @     var controls = document.querySelectorAll('select.format');
  @     for(var i = 0; i < controls.length; i++) {
  @         controls[i].onchange = handleDropDown;
  @     }
  @     controls = document.querySelectorAll('.intLink');
  @     for(i = 0; i < controls.length; i++) {
  @         controls[i].onclick = handleFormatButton;
  @     }
  @ 
  @     function handleDropDown() {
  @         formatDoc(this.dataset.format,this[this.selectedIndex].value);
  @         this.selectedIndex = 0;
  @     }
  @ 
  @     function handleFormatButton() {
  @         var extra;
  @         switch (this.dataset.format) {
  @             case 'createlink':
  @                 var sLnk = prompt('Target URL:','');
  @                 if(sLnk && sLnk != '')
  @                 {
  @                     extra = sLnk;
  @                 }
  @                 break;
  @             case 'formatblock':
  @                 extra = 'blockquote';
  @                 break;
  @         }
  @         formatDoc(this.dataset.format, extra);
  @     }
  @ }
  @
  @ /* Return true if the document editor is in WYSIWYG mode.  Return
  @ ** false if it is in Markup mode */
  @ function isWysiwyg() {
  @   return document.getElementById("editMode").selectedIndex==0;
  @ }
  @
  @ /* Invoke this routine prior to submitting the HTML content back
  @ ** to the server */
  @ function wysiwygSubmit() {
  @   if(oDoc.style.whiteSpace=="pre-wrap"){setDocMode(0);}
  @   document.getElementById("wysiwygValue").value=oDoc.innerHTML;
  @ }
  @
  @ /* Run the editing command if in WYSIWYG mode */
  @ function formatDoc(sCmd, sValue) {
  @   if (isWysiwyg()){
  @     try {
  @       // First, try the W3C draft standard way, which has
  @       // been working on all non-IE browsers for a while.
  @       // It is also supported by IE11 and higher.
  @       document.execCommand("styleWithCSS", false, false);
  @     } catch (e) {
  @       try {
  @         // For IE9 or IE10, this should work.
  @         document.execCommand("useCSS", 0, true);
  @       } catch (e) {
  @         // Ok, that apparently did not work, do nothing.
  @       }
  @     }
  @     document.execCommand(sCmd, false, sValue);
  @     oDoc.focus();
  @   }
  @ }
  @
  @ /* Change the editing mode.  Convert to markup if the argument
  @ ** is true and wysiwyg if the argument is false. */
  @ function setDocMode(bToMarkup) {
  @   var oContent;
  @   if (bToMarkup) {
  @     /* WYSIWYG -> Markup */
  @     var linebreak = new RegExp("</p><p>","ig");
  @     oContent = document.createTextNode(
  @                  oDoc.innerHTML.replace(linebreak,"</p>\n\n<p>"));
  @     oDoc.innerHTML = "";
  @     oDoc.style.whiteSpace = "pre-wrap";
  @     oDoc.appendChild(oContent);
  @     document.getElementById("toolBar1").style.visibility="hidden";
  @     document.getElementById("toolBar2").style.visibility="hidden";
  @   } else {
  @     /* Markup -> WYSIWYG */
  @     if (document.all) {
  @       oDoc.innerHTML = oDoc.innerText;
  @     } else {
  @       oContent = document.createRange();
  @       oContent.selectNodeContents(oDoc.firstChild);
  @       oDoc.innerHTML = oContent.toString();
  @     }
  @     oDoc.style.whiteSpace = "normal";
  @     document.getElementById("toolBar1").style.visibility="visible";
  @     document.getElementById("toolBar2").style.visibility="visible";
  @   }
  @   oDoc.focus();
  @ }
  @ initDoc();
  @ </script>

}

Changes to src/xfer.c.

37
38
39
40
41
42
43

44
45
46
47
48
49
50
51
52
53
54



55
56
57
58
59
60
61
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

55
56
57
58
59
60
61
62
63
64







+










-
+
+
+







  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 clientVersion;  /* Version of the client software */
  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
91
92
93
94
95
96
97












98
99
100
101
102
103
104
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119







+
+
+
+
+
+
+
+
+
+
+
+







    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:
337
338
339
340
341
342
343


344


345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360

361
362
363
364
365
366
367
368
369







+
+
-
+
+







      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");
  if( !isWriter ) goto end_accept_unversioned_file;
    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.
433
434
435
436
437
438
439
440

441
442
443
444
445
446
447
451
452
453
454
455
456
457

458
459
460
461
462
463
464
465







-
+







  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>=blob_size(pContent)-50 ){
    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);
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
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







-
+
+
+
+
+
+
+
+
+
+
+
+
+








-
+
















-
+







** 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( pXfer->syncPrivate==0 && isPriv ) return;
  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->clientVersion<20000 ){
  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<=blob_size(pXfer->pOut) ){
       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 ){
624
625
626
627
628
629
630
631

632
633
634
635
636
637
638
654
655
656
657
658
659
660

661
662
663
664
665
666
667
668







-
+







    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->clientVersion<20000 && db_column_bytes(&q1,0)!=HNAME_LEN_SHA1 ){
    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);
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
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







-
+















-
+




-
+







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( blob_size(pXfer->pOut)>=pXfer->mxSend ) noContent = 1;
  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->clientVersion<20000 && db_column_bytes(&q1,1)>HNAME_LEN_SHA1 ){
    if( pXfer->remoteVersion<20000 && db_column_bytes(&q1,1)>HNAME_LEN_SHA1 ){
      xfer_cannot_send_sha3_error(pXfer);
      db_reset(&q1);
      return;
    }
    if( blob_size(pXfer->pOut)>=pXfer->mxSend ){
    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{
732
733
734
735
736
737
738

739

740
741
742
743
744
745
746
762
763
764
765
766
767
768
769

770
771
772
773
774
775
776
777







+
-
+







** 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)"
    " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid) %s",
    "   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++;
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







-
+
+
+















-
+







** Check the signature on an application/x-fossil payload received by
** the HTTP server.  The signature is a line of the following form:
**
**        login LOGIN NONCE SIGNATURE
**
** The NONCE is the SHA1 hash of the remainder of the input.
** SIGNATURE is the SHA1 checksum of the NONCE concatenated
** with the users password.
** 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.
*/
int check_login(Blob *pLogin, Blob *pNonce, Blob *pSig){
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
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
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







+
+
+
+
+
+
+
+
+
+
+




+
+
+
+
+
+





-
+


-
+



-
+


-
+
+





-
+







  }
  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)"
      "   AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)%s"
      "   AND blob.rid<=%d"
      " ORDER BY blob.rid DESC",
      pXfer->resync
      zExtra /*safe-for-%s*/, pXfer->resync
    );
  }else{
    db_prepare(&q,
      "SELECT uuid FROM unclustered JOIN blob USING(rid)"
      "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)"
      "   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<blob_size(pXfer->pOut) ){
    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;
}
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
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







-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
-







** 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){
  unversioned_schema();
  if( !blob_eq(&pXfer->aToken[2], unversioned_content_hash(0)) ){
    int nUvIgot = 0;
    Stmt uvq;
    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);
  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);
      nUvIgot++;
      if( zHash==0 ){ sz = 0; zHash = "-"; }
      blob_appendf(pXfer->pOut, "uvigot %s %lld %s %d\n",
                   zName, mtime, zHash, sz);
    }
    db_finalize(&uvq);
    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){
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
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+














-
+







**   # ... 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
** 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;
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
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







+







+

















-
+


+







  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_transaction();
  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++;
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
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







-
+




















-
+



















-
+






+







-
+






+










-
+











-
+






+
+

+
-
+

+
+
+
-
+
+

+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+







    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
    **
    ** Accept a file from the client.
    ** 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
    **
    ** Accept a file from the client.
    ** 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
    **
    ** Accept an unversioned file from the client.
    ** 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.  Send it.
    ** 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.  Send it.
    ** 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 non-zero, then the file is a private file.
    ** 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_from_uuid(&xfer.aToken[1], 1, 0);
          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_from_uuid(&xfer.aToken[1], 1, 1);
          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 ){
          server_private_xfer_not_authorized();
            content_make_private(rid);
          }else{
            content_make_public(rid);
          }
        }
      }
    }else


    /*    pull  SERVERCODE  PROJECTCODE
    **    push  SERVERCODE  PROJECTCODE
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
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







-
+




















+
-
+







      ){
        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>blob_size(xfer.pOut) && seqno<=max){
        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.
    ** Check for a valid login.  This has to happen before anything else.
    ** 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;
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
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







-
+


















-
-
+
+







          break;
        }
      }
    }else

    /*    reqconfig  NAME
    **
    ** Request a configuration value
    ** 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
    **
    ** Receive a configuration value from the client.  This is only
    ** permitted for high-privilege users.
    ** 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);
1450
1451
1452
1453
1454
1455
1456
1457

1458
1459
1460
1461
1462
1463
1464
1546
1547
1548
1549
1550
1551
1552

1553
1554
1555
1556
1557
1558
1559
1560







-
+







      blob_reset(&content);
      blob_seek(xfer.pIn, 1, BLOB_SEEK_CUR);
    }else


    /*    cookie TEXT
    **
    ** A cookie contains a arbitrary-length argument that is server-defined.
    ** 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
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
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







-
+













-
+






+
+












-
+



-
+
+
+



-
+

-
+

-
+
+
+


-
-
+
+
+
+
+
+
+
+










-
-
+
+
+
+
+

-


+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+







    if( blob_eq(&xfer.aToken[0], "cookie") && xfer.nToken==2 ){
      /* Process the cookie */
    }else


    /*    private
    **
    ** This card indicates that the next "file" or "cfile" will contain
    ** 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 issue pragmas to try to influence the behavior of the
    ** 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
      **
      ** Send igot cards for all known artifacts.
      ** 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
      /*   pragma client-version VERSION ?DATE? ?TIME?
      **
      ** Let the server know what version of Fossil is running on the client.
      ** 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.clientVersion = atoi(blob_str(&xfer.aToken[2]));
      }
        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 ){
          if( g.perm.Read && g.perm.WrUnver ){
        if( !uvCatalogSent
         && g.perm.Read
         && !blob_eq_str(&xfer.aToken[2], unversioned_content_hash(0),-1)
        ){
          if( g.perm.WrUnver ){
            @ pragma uv-push-ok
            send_unversioned_catalog(&xfer);
          }else if( g.perm.Read ){
            @ pragma uv-pull-only
          }
            send_unversioned_catalog(&xfer);
          }
        }
        uvCatalogSent = 1;
          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))
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
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







+
-
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





-
+


-
+






+
+
+
+
+
-
-
-
-
+
+
+
+
+
+

+
-
+

-
+

-
+
-
-
-
+
+
+
+

-
-
+
+
+


+

+
+



+
+
+
+




-
+














-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+


















-
-
-
+
+
+
+
+










+














+




+



+
+
+
+

+


-
+
+
+
+
+
+
+
+
+
+
+
+
+

+



-
-
+
+

-
+


+
+
+
+
+
+









-
+











-
+






+
+
+
+
+
+
+
+
+











-
+



-
+





-
+
+

-
+
+
+








-
+
+

















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+








+




-
-
+
+






-
+
-












-
+
















-
-
-
+
+
+










-
+
+







    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");
  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)
  @ # timestamp %s(zNow) errors %d(nErr)
  free(zNow);

  db_end_transaction(0);
  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 is used for debugging the server.  There is a single
** argument which is the uncompressed content of an "xfer" message
** from client to server.  This command interprets that message as
** if had been received by the server.
** 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
** On the client side, run:
** using a command like:
**
**      fossil push http://bogus/ --httptrace
**     fossil push http://bogus/ --httptrace
**
** Or a similar command to provide the output.  The content of the
** The complete HTTP requests are stored in files named "http-request-N.txt".
** message will appear on standard output.  Capture this message
** into a file named (for example) out.txt.  Then run the
** server in gdb:
** 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
**
**     gdb fossil
**     r test-xfer out.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\n", cgi_extract_content());
  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           0x0001    /* push content client to server */
#define SYNC_PULL           0x0002    /* pull content server to client */
#define SYNC_CLONE          0x0004    /* clone the repository */
#define SYNC_PRIVATE        0x0008    /* Also transfer private content */
#define SYNC_VERBOSE        0x0010    /* Extra diagnostics */
#define SYNC_RESYNC         0x0020    /* --verily */
#define SYNC_UNVERSIONED    0x0040    /* Sync unversioned content */
#define SYNC_UV_REVERT      0x0080    /* Copy server unversioned to client */
#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_FROMPARENT     0x0100    /* Pull from the parent project */
#define SYNC_UV_TRACE       0x0200    /* Describe UV activities */
#define SYNC_UV_DRYRUN      0x0400    /* Do not actually exchange files */
#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 */
  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;
     && 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);
    zPCode = db_get("parent-project-code", 0);
    if( zPCode==0 || db_get("parent-project-name",0)==0 ){
    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 set in order"
                   " 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.clientVersion = RELEASE_VERSION_NUMBER;
  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 uv_tosend("
       "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;"
       "INSERT INTO uv_toSend(name,mtimeOnly)"
       "REPLACE INTO uv_tosend(name,mtimeOnly)"
       "  SELECT name, 0 FROM unversioned WHERE hash IS NOT NULL;"
    );
  }

  /*
  ** Always begin with a clone, pull, or push message
  ** The request from the client always begin with a clone, pull,
  ** or push message.
  */
  blob_appendf(&send, "pragma client-version %d\n", RELEASE_VERSION_NUMBER);
  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, zPCode);
    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();


    /* Send back the most recently received cookie.  Let the server
    ** figure out if this is a cookie that it cares about.
    /* 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);
    }

    /* Generate gimme cards for phantoms and leaf cards
    /* Client sends gimme cards for phantoms
    ** for all leaves.
    */
    if( (syncFlags & SYNC_PULL)!=0
     || ((syncFlags & SYNC_CLONE)!=0 && cloneSeqno==1)
    ){
      request_phantoms(&xfer, mxPhantomReq);
    }
    if( syncFlags & SYNC_PUSH ){
      send_unsent(&xfer);
      nCardSent += send_unclustered(&xfer);
      if( syncFlags & SYNC_PRIVATE ) send_private(&xfer);
    }

    /* Send configuration parameter requests.  On a clone, delay sending
    /* 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;
    }

    /* Send 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.
    /* 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;
    }

    /* Send configuration parameters being pushed */
    /* 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
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
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







-
+






+
+
+
+
+
+
+
+
+
+
+
+
-
+







-
+
+
+




-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+




+
+
+
+
+
+


















+


+

-
+
+
+











-
+
+









+







          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( blob_size(xfer.pOut)>xfer.mxSend ) break;
          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 message.  This makes all
    /* 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 ){
    if( (syncFlags & SYNC_VERBOSE)!=0
     && (syncFlags & SYNC_XVERBOSE)==0
    ){
      fossil_print("waiting for server...");
    }
    fflush(stdout);
    /* Exchange messages with the server */
    if( http_exchange(&send, &recv, (syncFlags & SYNC_CLONE)==0 || nCycle>0,
        MAX_REDIRECTS) ){
    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\n", RELEASE_VERSION_NUMBER);
    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, zPCode);
      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];
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
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







-
+









-
+








-
+













+
-
-
-
+
+
+





+







          fflush(stdout);
        }
      }

      /*   file HASH SIZE \n CONTENT
      **   file HASH DELTASRC SIZE \n CONTENT
      **
      ** Receive a file transmitted from the server.
      ** 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
      **
      ** Receive a compressed file transmitted from the server.
      ** 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
      **
      ** Accept an unversioned file from the server.
      ** 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.
      ** Server is requesting a file.  If the file is a manifest, assume
      ** that the server will also want to know all of the content files
      ** associated with the manifest and send those too.
      ** If 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?
2071
2072
2073
2074
2075
2076
2077



2078


2079
2080
2081
2082
2083
2084
2085
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454

2455
2456
2457
2458
2459
2460
2461
2462
2463







+
+
+
-
+
+







       && 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{
          if( !isPriv ) content_make_public(rid);
            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);
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
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







+
+
+
+
+
+
+
+
-
+












-
+
















-
+







          }
        }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 ){
        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 to use for the new database.
      ** 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
      **
      ** Receive a configuration value from the server.
      ** 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]);
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
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







-
+













-
-
+
+



















+
-
+







        blob_reset(&content);
        blob_seek(xfer.pIn, 1, BLOB_SEEK_CUR);
      }else


      /*    cookie TEXT
      **
      ** The server might include a cookie in its reply.  The client
      ** 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
      **
      ** This card indicates that the next "file" or "cfile" will contain
      ** private content.
      ** 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.
      ** Print a message.  Similar to "error" but does not stop processing.
      ** 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);
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
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+





+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+




+
-
+







+
+
+


-
-
-
+
+
+
+
+
+
+


-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-







      /*    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 unwill to accept new unversioned content (because
        ** 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") ){
          if( syncFlags & SYNC_UV_REVERT ) uvDoPush = 1;
        }else if( blob_eq(&xfer.aToken[1], "uv-push-ok") ){
          uvDoPush = 1;
        }
          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
      ** Report an error and abandon the sync session.
      ** 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 ){
        if( (syncFlags & SYNC_CLONE)==0 || nCycle>0 ){
          char *zMsg = blob_terminate(&xfer.aToken[1]);
          defossilize(zMsg);
        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);
          if( fossil_strcmp(zMsg, "login failed")==0 ){
            if( nCycle<2 ){
              g.url.passwd = 0;
              go = 1;
              if( g.cgiOutput==0 ){
                g.url.flags |= URL_PROMPT_PW;
                g.url.flags &= ~URL_PROMPTED;
                url_prompt_for_password();
                url_remember();
              }
            }else{
              nErr++;
            }
          }else{
            blob_appendf(&xfer.err, "server says: %s\n", zMsg);
            nErr++;
          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]=='<' ){
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
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







+



-
-
+
+
-





+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+




-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+









+











+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



-
+



+
+
+
+
+
+
+
+
+







      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++;

    /* If we received one or more files on the previous exchange but
    ** there are still phantoms, then go another round.
    /* 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 && nFileRecv>0 ){
      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;

    /* If we have one or more files queued to send, then go
    ** another round
    */
    if( xfer.nFileSent+xfer.nDeltaSent>0 || uvDoPush ){
      go = 1;
    }

    /* If this is a clone, the go at least two rounds */
    if( (syncFlags & SYNC_CLONE)!=0 && nCycle==1 ) go = 1;

    /* Stop the cycle if the server sends a "clone_seqno 0" card and
    ** we have gone at least two rounds.  Always go at least two rounds
    ** on a clone in order to be sure to retrieve the configuration
    ** information which is only sent on the second round.
    */
    if( cloneSeqno<=0 && nCycle>1 ) go = 0;

    /* Continue looping as long as new uvfile cards are being received
    ** and uvgimme cards are being sent. */
    if( nUvGimmeSent>0 && (nUvFileRcvd>0 || nCycle<3) ) go = 1;

    db_multi_exec("DROP TABLE onremote");
    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, sent: %lld  received: %lld  ip: %s\n",
     zOpType, nSent, nRcvd, g.zIpAddr);
      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");
    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;
}

Changes to src/xfersetup.c.

42
43
44
45
46
47
48
49

50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66

67
68
69
70
71
72
73
74
75
76

77
78

79
80
81
82
83
84
85

86
87
88
89
90

91
92
93
94
95
96
97
42
43
44
45
46
47
48

49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65

66
67
68
69
70
71
72
73
74
75

76
77

78
79
80
81
82
83
84

85
86
87
88
89

90
91
92
93
94
95
96
97







-
+
















-
+









-
+

-
+






-
+




-
+







    "Specific TH1 code to run after \"push\" transfer requests.");
  setup_menu_entry("Commit", "xfersetup_commit",
    "Specific TH1 code to run after processing a commit.");
  setup_menu_entry("Ticket", "xfersetup_ticket",
    "Specific TH1 code to run after processing a ticket change.");
  @ </table>

  url_parse(0, 0);
  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 />
    @ synchronize with the <em>%h(g.url.canonical)</em> repository now.<br>
    @ This may be useful when testing the various transfer scripts.</p>
    @ <p>You can use the <code>http -async</code> command in your scripts, but
    @ make sure the <code>th1-uri-regexp</code> setting is set first.</p>
    if( zWarning ){
      @
      @ <big><b>%h(zWarning)</b></big>
      free(zWarning);
    }
    @
    @ <form method="post" action="%s(g.zTop)/%s(g.zPath)"><div>
    @ <form method="post" action="%R/%s(g.zPath)"><div>
    login_insert_csrf_secret();
    @ <input type="submit" name="sync" value="%h(zButton)" />
    @ <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);
      client_sync(syncFlags, 0, 0, 0, 0);
      @ </pre>
    }
  }

  style_footer();
  style_finish_page();
}

/*
** Common implementation for the transfer setup editor pages.
*/
static void xfersetup_generic(
  const char *zTitle,           /* Page title */
114
115
116
117
118
119
120

121
122

123
124

125
126
127

128
129
130
131
132
133

134
135
136
137
138

139
140
141
142
143
144
145



146
147
148
149

150
151
152
153
154
155

156
157
158
159
160
161
162
114
115
116
117
118
119
120
121
122

123


124
125
126

127
128

129
130
131

132
133
134
135
136

137
138
139
140
141



142
143
144
145
146
147

148
149
150
151
152
153

154
155
156
157
158
159
160
161







+

-
+
-
-
+


-
+

-



-
+




-
+




-
-
-
+
+
+



-
+





-
+







    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 ){
  if( P("clear")!=0 && cgi_csrf_safe(2) ){
    login_verify_csrf_secret();
    db_unset(zDbField, 0);
    db_unset(zDbField/*works-like:"x"*/, 0);
    if( xRebuild ) xRebuild();
    z = zDfltValue;
  }else if( isSubmit ){
  }else if( isSubmit && cgi_csrf_safe(2) ){
    char *zErr = 0;
    login_verify_csrf_secret();
    if( xText && (zErr = xText(z))!=0 ){
      @ <p class="xfersetupError">ERROR: %h(zErr)</p>
    }else{
      db_set(zDbField, z, 0);
      db_set(zDbField/*works-like:"x"*/, z, 0);
      if( xRebuild ) xRebuild();
      cgi_redirect("xfersetup");
    }
  }
  @ <form action="%s(g.zTop)/%s(g.zPath)" method="post"><div>
  @ <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" />
  @ <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 />
    @ <hr>
    @ <h2>Default %s(zTitle)</h2>
    @ <blockquote><pre>
    @ %h(zDfltValue)
    @ </pre></blockquote>
  }
  style_footer();
  style_finish_page();
}

static const char *zDefaultXferCommon = 0;

/*
** WEBPAGE: xfersetup_com
** View or edit the TH1 script that runs prior to receiving a

Changes to src/zip.c.

15
16
17
18
19
20
21
22
23
24
25
26

27
28
29
30
31
32
33
34
15
16
17
18
19
20
21





22

23
24
25
26
27
28
29







-
-
-
-
-
+
-







**
*******************************************************************************
**
** This file contains code used to generate ZIP and SQLAR archives.
*/
#include "config.h"
#include <assert.h>
#if defined(FOSSIL_ENABLE_MINIZ)
#  define MINIZ_HEADER_FILE_ONLY
#  include "miniz.c"
#else
#  include <zlib.h>
#include <zlib.h>
#endif
#include "zip.h"

/*
** Type of archive to build.
*/
#define ARCHIVE_ZIP   0
#define ARCHIVE_SQLAR 1
69
70
71
72
73
74
75
76

77
78
79
80
81
82
83
64
65
66
67
68
69
70

71
72
73
74
75
76
77
78







-
+







  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( blob_size(pBlob)<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
141
142
143
144
145
146
147
148

149
150
151
152
153
154
155
136
137
138
139
140
141
142

143
144
145
146
147
148
149
150







-
+







  return 512;
}
static int archiveDeviceCharacteristics(sqlite3_file *pFile){
  return 0;
}

static int archiveOpen(
  sqlite3_vfs *pVfs, const char *zName, 
  sqlite3_vfs *pVfs, const char *zName,
  sqlite3_file *pFile, int flags, int *pOutFlags
){
  static struct sqlite3_io_methods methods = {
    1,             /* iVersion */
    archiveClose,
    archiveRead,
    archiveWrite,
239
240
241
242
243
244
245
246

247
248
249
250
251
252
253
254
255
256
257
258


259
260
261
262
263
264
265

266
267
268
269
270
271
272
234
235
236
237
238
239
240

241
242
243
244
245
246
247
248
249
250
251


252
253
254
255
256
257
258
259

260
261
262
263
264
265
266
267







-
+










-
-
+
+






-
+







/*
** Set the date and time from a julian day number.
*/
void zip_set_timedate(double rDate){
  char *zDate = db_text(0, "SELECT datetime(%.17g)", rDate);
  zip_set_timedate_from_str(zDate);
  fossil_free(zDate);
  unixTime = (rDate - 2440587.5)*86400.0;
  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, 
  const char *zName,
  const Blob *pFile,
  int mPerm
){
  z_stream stream;
  int nameLen;
  int toOut = 0;
  int iStart;
  int iCRC = 0;
  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];
377
378
379
380
381
382
383
384
385


386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406

407
408
409
410
411

412
413
414
415
416
417
418
419
420
421
422
423
424
425


426
427
428
429
430
431
432
372
373
374
375
376
377
378


379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400

401
402
403
404
405

406
407
408
409
410
411
412
413
414
415
416
417
418


419
420
421
422
423
424
425
426
427







-
-
+
+




















-
+




-
+












-
-
+
+







  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, 
  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, 
    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, 
    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, 
    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);
  }
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
435
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







-
+



-
+







-
-
+
+



-
+












-
-
+
+







    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, 
      sqlite3_bind_text(p->pInsert, 5,
          blob_buffer(pFile), blob_size(pFile), SQLITE_STATIC
      );
    }else{
      int nIn = blob_size(pFile);
      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>=nIn ){
        sqlite3_bind_blob(p->pInsert, 5, 
      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, 
        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, 
  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);
  }
590
591
592
593
594
595
596
597

598
599
600
601
602
603
604
585
586
587
588
589
590
591

592
593
594
595
596
597
598
599







-
+







  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,0));
    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]);
}

/*
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
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







-
+




-
+



-
+
+












-
+







-
+







** 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 UUID or "ProjectName".
** 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 checkin to build the archive from */
  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 */
  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);
  blob_zero(pZip);
  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);
  zip_open();
  if( pZip ) zip_open();

  if( zDir && zDir[0] ){
    blob_appendf(&filename, "%s/", zDir);
  }
  nPrefix = blob_size(&filename);

  pManifest = manifest_get(rid, CFTYPE_MANIFEST, 0);
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
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







+
+
-
+
-
-
+
+






+
+
-
-
-
+
+
+
+

-
-
-



+
+
+
+
+
-
-
-
+
+
+
+



-
+






-



+
+
+
-
-
-
+
+
+
+







+
-
+
+













+
+






+














+
+
+
+












-
+
+


+
-
-
+
+
+


















+
+
+
+
+



+







       && (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_folders(&sArchive, zName);
        sterilize_manifest(&mfile);
        zip_add_file(&sArchive, zName, &mfile, 0);
          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);
      }
          zip_add_folders(&sArchive, zName);
          zip_add_file(&sArchive, zName, &hash, 0);
        }
      }
      if( eflg & MFESTFLG_TAGS ){
        Blob tagslist;
        blob_zero(&tagslist);
        get_checkin_taglist(rid, &tagslist);
        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);
          zip_add_folders(&sArchive, zName);
          zip_add_file(&sArchive, zName, &tagslist, 0);
          blob_reset(&tagslist);
        }
      }
    }
    manifest_file_rewind(pManifest);
    zip_add_file(&sArchive, "", 0, 0);
    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 ){
        content_get(fid, &file);
        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);
          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);
    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, &zip, zName, pInclude, pExclude);
  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, g.argv[3]);
  blob_reset(&zip);
    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);
}

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
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







+
+
+
+
+



+











+
+
+
+
+
-
-
+
+
+

-
-
-
+
+

-
-
-
-
+
+
+
+



-
-
-
-
+
+
+
+
+
+
+

-
-
-
-
-
-
+
+
+
+
+
+
+
+







** 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 or SQL archive for the check-in specified by the "r"
** query parameter.  Return the archive as the HTTP reply content.
** 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.
**
** If the NAME contains one "/" then the part before the "/" is taken
** as the TAG and the part after the "/" becomes the true name.  Hence,
** the following URLs are all equivalent:
** The optional VERSION element defaults to "trunk" per the r= rules below.
** All of the following URLs are equivalent:
**
**     /sqlar/508c42a6398f8/download.sqlar
**     /sqlar?r=508c42a6398f8&name=download.sqlar
**     /sqlar/download.sqlar?r=508c42a6398f8
**     /sqlar?name=508c42a6398f8/download.sqlar
**      /zip/release/xyz.zip
**      /zip?r=release&name=xyz.zip
**      /zip/xyz.zip?r=release
**      /zip?name=release/xyz.zip
**
** Query parameters:
**
**   name=NAME           The base name of the output file.  The default
**                       value is a configuration parameter in the project
**                       settings.  A prefix of the name, omitting the
**                       extension, is used as the top-most directory name.
**   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               The check-in that is turned into a ZIP archive.
**                       Defaults to "trunk".  This query parameter used to
**                       be called "uuid" and the older "uuid" name is still
**                       accepted for backwards compatibility.  If this
**                       query parameter is omitted, the latest "trunk"
**                       check-in is used.
**   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 '..'.
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
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







-
+












+
+
+
-
+






-
+







  if( fossil_strcmp(g.zPath, "sqlar")==0 ){
    eType = ARCHIVE_SQLAR;
    zType = "SQL";
  }else{
    eType = ARCHIVE_ZIP;
    zType = "ZIP";
  }
  load_control();
  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 
  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 
  }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{
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
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







+


-
-
+
+

-
+


-
+


-
+








-
+

-
+


+


-
+






+








  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 />
    @ zName = "%h(zName)"<br>
    @ rid = %d(rid)<br>
    if( zInclude ){
      @ zInclude = "%h(zInclude)"<br />
      @ zInclude = "%h(zInclude)"<br>
    }
    if( zExclude ){
      @ zExclude = "%h(zExclude)"<br />
      @ zExclude = "%h(zExclude)"<br>
    }
    @ zKey = "%h(zKey)"
    style_footer();
    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" />
    @ <input type="submit" value="Download">
    @ </form>
    style_footer();
    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);
    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");
  }
}

Changes to test/amend.test.

24
25
26
27
28
29
30
31
32


33
34
35
36
37


38
39
40
41
42

43
44

45
46
47
48
49
50
51
52
53
54

55
56
57
58
59
60
61

62
63
64
65
66
67
68
69

70
71
72

73
74
75

76
77
78
79
80
81

82
83
84
85
86
87
88
24
25
26
27
28
29
30


31
32
33
34
35


36
37
38
39
40
41

42
43

44
45
46
47
48
49
50
51
52
53

54
55
56
57
58
59
60

61
62
63
64
65
66
67
68

69
70
71

72
73
74

75
76
77
78
79
80

81
82
83
84
85
86
87
88







-
-
+
+



-
-
+
+




-
+

-
+









-
+






-
+







-
+


-
+


-
+





-
+







}

proc manifest_comment {comment} {
  string map [list { } {\\s} \n {\\n} \r {\\r}] $comment
}

proc uuid_from_commit {res var} {
  upvar $var UUID
  regexp {^New_Version: ([0-9a-f]{40})[0-9a-f]*$} $res m UUID
  upvar $var HASH
  regexp {^New_Version: ([0-9a-f]{40})[0-9a-f]*$} $res m HASH
}

proc uuid_from_branch {res var} {
  upvar $var UUID
  regexp {^New branch: ([0-9a-f]{40})[0-9a-f]*$} $res m UUID
  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 UUID
  upvar $var HASH
  fossil status
  regexp {checkout:\s+([0-9a-f]{40})} $RESULT m UUID
  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 UUIDINIT]} {
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 UUID]} {
if {![uuid_from_commit $RESULT HASH]} {
  test amend-setup-failure false
  test_cleanup_then_return
}

########################################
# Test: -branch                        #
########################################
set UUIDB UUIDB
set HASHB HASHB
write_file datafile "data.file"
fossil commit -m "c2"
if {![uuid_from_commit $RESULT UUIDB]} {
if {![uuid_from_commit $RESULT HASHB]} {
  test amend-branch.setup false
}
fossil amend $UUIDB -branch amended-branch
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 $UUIDB
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                       #
100
101
102
103
104
105
106
107
108
109



110
111
112
113
114
115
116
117
118

119
120
121
122
123
124
125
126
127



128
129
130
131
132
133
134
135
136

137
138
139
140
141
142
143
144
145
146
147



148
149
150
151
152
153




154
155
156
157
158
159
160
161
162

163
164
165
166
167
168
169

170
171
172

173
174
175
176
177
178
179
180


181
182
183
184
185
186
187
188
189
190
191

192
193

194
195
196
197
198
199
200
201
202
203
204
205
206
207



208
209
210
211
212
213
214
215
216
217
218
219
220
221
222

223
224
225
226
227
228
229

230
231
232
233

234
235
236
237
238



239
240
241
242
243
244
245
246
247
248



249
250
251
252
253



254
255
256
257

258
259
260
261
262
263
264
265

266
267
268
269
270

271
272
273
274
275
276
277
278


279
280
281
282
283
284
285
100
101
102
103
104
105
106



107
108
109
110
111
112
113
114
115
116
117

118
119
120
121
122
123
124



125
126
127
128
129
130
131
132
133
134
135

136
137
138
139
140
141
142
143
144



145
146
147
148
149




150
151
152
153
154
155
156
157
158
159
160
161

162
163
164
165
166
167
168

169
170
171

172
173
174
175
176
177
178


179
180
181
182
183
184
185
186
187
188
189
190

191
192

193
194
195
196
197
198
199
200
201
202
203
204



205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221

222
223
224
225
226
227
228

229
230
231
232

233
234
235



236
237
238
239
240
241
242
243
244
245



246
247
248
249
250



251
252
253
254
255
256

257
258
259
260
261
262
263
264

265
266
267
268
269

270
271
272
273
274
275
276


277
278
279
280
281
282
283
284
285







-
-
-
+
+
+








-
+






-
-
-
+
+
+








-
+








-
-
-
+
+
+


-
-
-
-
+
+
+
+








-
+






-
+


-
+






-
-
+
+










-
+

-
+











-
-
-
+
+
+














-
+






-
+



-
+


-
-
-
+
+
+







-
-
-
+
+
+


-
-
-
+
+
+



-
+







-
+




-
+






-
-
+
+







  acf #acf
  123 #123
  #1234 #1234
  1234 1234
  123456 #123456
} {
  incr tc
  fossil amend $UUID -bgcolor $color
  test amend-bgcolor-1.$tc.a {[string match "*uuid:*$UUID*" $RESULT]}
  fossil tag list --raw $UUID
  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 $UUID* $result*" $RESULT]
      [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 $UUID -bgcolor {}
test amend-bgcolor-2.1 {[string match "*uuid:*$UUID*" $RESULT]}
fossil tag list --raw $UUID
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 $UUID*" $RESULT]}
  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 UUID2 UUID2
fossil branch new brclr $UUID
if {![uuid_from_branch $RESULT UUID2]} {
set HASH2 HASH2
fossil branch new brclr $HASH
if {![uuid_from_branch $RESULT HASH2]} {
  test amend-branchcolor.setup false
}
fossil update $UUID2
fossil amend $UUID2 -branchcolor yellow
test amend-branchcolor-1.1 {[string match "*uuid:*$UUID2*" $RESULT]}
fossil tag ls --raw $UUID2
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 $UUID2* yellow*" $RESULT]
    [string match "*T \*bgcolor $HASH2* yellow*" $RESULT]
  }
} else {
  if {$VERBOSE} { protOut "No artifact found in timeline output" }
  test amend-branchcolor-1.4 false
}

set UUIDN UUIDN
set HASHN HASHN
write_file datafile "brclr"
fossil commit -m "brclr"
if {![uuid_from_commit $RESULT UUIDN]} {
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 $UUIDN -branchcolor deadbe
test amend-branchcolor-2.1 {[string match "*uuid:*$UUIDN*" $RESULT]}
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 $UUID -author author-test
fossil amend $HASH -author author-test
test amend-author-1.1 {[string match {*comment:*(user:*author-test)*} $RESULT]}
fossil tag ls --raw $UUID
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 $UUIDINIT -date $datetime
test amend-date-1.1 {[string match "*uuid:*$UUIDINIT*$datetime*" $RESULT]}
fossil tag ls --raw $UUIDINIT
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 $UUIDINIT -date $datetime -expectError
  fossil amend $HASHINIT -date $datetime -expectError
  test amend-date-2.$sc {[string first "YYYY-MM-DD HH:MM:SS" $RESULT] != -1}
}

########################################
# Test: -hide                          #
########################################
set UUIDH UUIDH
set HASHH HASHH
fossil revert
fossil update trunk
fossil branch new tohide current
if {![uuid_from_branch $RESULT UUIDH]} {
if {![uuid_from_branch $RESULT HASHH]} {
  test amend-hide-setup false
}
fossil amend $UUIDH -hide
test amend-hide-1.1 {[string match "*uuid:*$UUIDH*" $RESULT]}
fossil tag ls --raw $UUIDH
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 UUIDC UUIDC
fossil branch new cllf $UUID
if {![uuid_from_branch $RESULT UUIDC]} {
set HASHC HASHC
fossil branch new cllf $HASH
if {![uuid_from_branch $RESULT HASHC]} {
  test amend-close.setup false
}
fossil update $UUIDC
fossil amend $UUIDC -close
test amend-close-1.1.a {[string match "*uuid:*$UUIDC*" $RESULT]}
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 $UUIDC
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 UUID3 UUID3
set HASH3 HASH3
fossil revert
fossil update trunk
write_file datafile "cb"
fossil commit -m "closed-branch" --branch "closebranch"
if {![uuid_from_commit $RESULT UUID3]} {
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 $UUID3 --close
test amend-close-3.1 {[string match "*uuid:*$UUID3*" $RESULT]}
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"
304
305
306
307
308
309
310
311
312
313
314



315
316
317




318
319
320
321

322
323
324
325
326
327
328
329
330
331

332
333
334
335
336
337
338


339
340
341
342

343
344
345
346

347
348
349
350
351
352

353
354
355
356
357
358
359
360

361
362

363
364

365
366
367
368
369
370


371
372
373
374


375
376
377
378
379


380
381
382
383
384


385
386
387

388
389
390
391
392
393
394


395
396
397
398

399
400
401
402
403
404
405
304
305
306
307
308
309
310

311
312
313
314
315
316



317
318
319
320
321
322
323

324
325
326
327
328
329
330
331
332
333

334
335
336
337
338
339


340
341
342
343
344

345
346
347
348

349
350
351
352
353
354

355
356
357
358
359
360
361
362

363
364

365
366

367
368
369
370
371


372
373
374
375


376
377
378
379
380


381
382
383
384
385


386
387
388
389

390



391
392


393
394
395
396
397

398
399
400
401
402
403
404
405







-



+
+
+
-
-
-
+
+
+
+



-
+









-
+





-
-
+
+



-
+



-
+





-
+







-
+

-
+

-
+




-
-
+
+


-
-
+
+



-
-
+
+



-
-
+
+


-
+
-
-
-


-
-
+
+



-
+







  set t5exp "*"
  foreach tag $tagt {
    lappend tags -tag $tag
    lappend cancels -cancel $tag
  }
  foreach res $result {
    append t1exp ", $res"
    append t2exp "sym-$res*"
    append t3exp "Add*tag*\"$res\".*"
    append t5exp "Cancel*tag*\"$res\".*"
  }
  foreach res [lsort -nocase $result] {
    append t2exp "sym-$res*"
  }
  eval fossil amend $UUID $tags
  test amend-tag-$tc.1 {[string match "*uuid:*$UUID*tags:*$t1exp*" $RESULT]}
  fossil tag ls --raw $UUID
  eval fossil amend $HASH $tags
  set t1exp [string trimleft $t1exp ,]
  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 $UUID $cancels
  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 UUID RESULT
  global HASH RESULT

  fossil revert
  fossil update trunk
  write_file datafile $comment
  fossil commit -m $content
  if {![uuid_from_commit $RESULT UUID]} {
    set UUID ""
  if {![uuid_from_commit $RESULT HASH]} {
    set HASH ""
  }
}

proc test-comment {name UUID comment} {
proc test-comment {name HASH comment} {
  global VERBOSE RESULT

  test amend-comment-$name.1 {
    [string match "*uuid:*$UUID*comment:*$comment*" $RESULT]
    [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 $UUID* *[manifest_comment $comment]*" $RESULT]
      [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 $UUID]*Edit*check-in*comment.*" $RESULT]
    [string match "*[short_uuid $HASH]*Edit*check-in*comment.*" $RESULT]
  }
  fossil info $UUID
  fossil info $HASH
  test amend-comment-$name.4 {
    [string match "*uuid:*$UUID*comment:*$comment*" $RESULT]
    [string match "*hash:*$HASH*comment:*$comment*" $RESULT]
  }
}

prep-test "revision 1" "revision 1"
fossil amend $UUID -comment "revised revision 1"
test-comment 1 $UUID "revised revision 1"
fossil amend $HASH -comment "revised revision 1"
test-comment 1 $HASH "revised revision 1"

prep-test "revision 2" "revision 2"
fossil amend $UUID -m "revised revision 2 with -m"
test-comment 2 $UUID "revised revision 2 with -m"
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 $UUID -message-file commitmsg
test-comment 3 $UUID "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 $UUID -M commitmsg
test-comment 4 $UUID "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]} {
if {[catch {exec which ed} result] == 0} {
  if {$VERBOSE} { protOut "Install ed for interactive comment test: $result" }
  test-comment 5 $UUID "ed required for interactive edit"
} else {
  fossil settings editor "ed -s"
  set comment "interactive edited comment"
  fossil_maybe_answer "a\n$comment\n.\nw\nq\n" amend $UUID --edit-comment
  test-comment 5 $UUID $comment
  fossil_maybe_answer "a\n$comment\n.\nw\nq\n" amend $HASH --edit-comment
  test-comment 5 $HASH $comment
}

########################################
# Test: NULL UUID                      #
# Test: NULL hash                      #
########################################
fossil amend {} -close -expectError
test amend-null-uuid {$CODE && [string first "no such check-in" $RESULT] != -1}

###############################################################################

test_cleanup

Changes to test/cmdline.test.

16
17
18
19
20
21
22
23

24
25








26
27






28
29
30

31
32







33
34
35
36
16
17
18
19
20
21
22

23
24
25
26
27
28
29
30
31
32
33


34
35
36
37
38
39
40
41
42
43


44
45
46
47
48
49
50
51
52
53
54







-
+


+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+



+
-
-
+
+
+
+
+
+
+




############################################################################
#
# Test command line parsing
#

test_setup ""

proc cmd-line {testname args} {
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\]\}"}
      fossil test-echo $cmdline
    }

    test cmd-line-$testname.$i \
        {[lrange [split $::RESULT \n] 3 end]=="\{argv\[2\] = \[$result\]\}"}

    incr i
  }
}

cmd-line 100 abc abc a\"bc a\"bc \"abc\" \"abc\"
cmd-line 101 * * *.* *.*
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

Changes to test/commit-warning.test.

48
49
50
51
52
53
54

55
56
57
58
59

60

61
62
63
64
65
66
67
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70







+





+

+







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
116
117
118
119
120
121
122



123
124
125

126
127
128
129
130
131
132
119
120
121
122
123
124
125
126
127
128
129
130

131
132
133
134
135
136
137
138







+
+
+


-
+







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
1\tline-8192\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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190

















191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257













































































258
259
260
261
262
263


264
265
266
267
268
269
270
271
272
273
274






275
276
277

278
279
280
281
282
283
284

285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301




















302
303
304
305
306
170
171
172
173
174
175
176




















177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193



































































194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270






271
272











273
274
275
276
277
278



279







280

















281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
-
-
-
+
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





# 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\tart/branching.odp\tbinary data
  1\tart/concept1.dia\tbinary data
  1\tart/concept2.dia\tbinary data
  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/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/masmx64/bld_ml64.bat\tCR/LF line endings
  1\tcompat/zlib/contrib/masmx64/gvmat64.asm\tCR/LF line endings
  1\tcompat/zlib/contrib/masmx64/inffas8664.c\tCR/LF line endings
  1\tcompat/zlib/contrib/masmx64/inffasx64.asm\tCR/LF line endings
  1\tcompat/zlib/contrib/masmx64/readme.txt\tCR/LF line endings
  1\tcompat/zlib/contrib/masmx86/bld_ml32.bat\tCR/LF line endings
  1\tcompat/zlib/contrib/masmx86/inffas32.asm\tCR/LF line endings
  1\tcompat/zlib/contrib/masmx86/match686.asm\tCR/LF line endings
  1\tcompat/zlib/contrib/masmx86/readme.txt\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\tsetup/fossil.iss\tCR/LF line endings
  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\tskins/bootstrap/css.txt\tlong lines
  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\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/CollRev1.gif\tbinary data
  1\twww/CollRev2.gif\tbinary data
  1\twww/CollRev3.gif\tbinary data
  1\twww/CollRev4.gif\tbinary data
  1\twww/apple-touch-icon.png\tbinary data
  1\twww/background.jpg\tbinary data
1\twww/apple-touch-icon.png\tbinary data
1\twww/background.jpg\tbinary data
  1\twww/branch01.gif\tbinary data
  1\twww/branch02.gif\tbinary data
  1\twww/branch03.gif\tbinary data
  1\twww/branch04.gif\tbinary data
  1\twww/branch05.gif\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/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/concept1.gif\tbinary data
  1\twww/concept2.gif\tbinary data
  1\twww/copyright-release.pdf\tbinary data
1\twww/copyright-release.pdf\tbinary data
  1\twww/delta1.gif\tbinary data
  1\twww/delta2.gif\tbinary data
  1\twww/delta3.gif\tbinary data
  1\twww/delta4.gif\tbinary data
  1\twww/delta5.gif\tbinary data
  1\twww/delta6.gif\tbinary data
  1\twww/encode1.gif\tbinary data
1\twww/encode1.gif\tbinary data
  1\twww/encode10.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/xkcd-git.gif\tbinary data
  1}]]}
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/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.

23
24
25
26
27
28
29
30

31
32
33
34
35
36
37
23
24
25
26
27
28
29

30
31
32
33
34
35
36
37







-
+







# 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 [glob $testdir/*]
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]

Changes to test/diff-test-1.wiki.

18
19
20
21
22
23
24
25




26
27
28
29
30
31
32
18
19
20
21
22
23
24

25
26
27
28
29
30
31
32
33
34
35







-
+
+
+
+







  *  <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>
     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

Changes to test/diff.test.

28
29
30
31
32
33
34
35
36
37



38
39
40
41
42
43
44
28
29
30
31
32
33
34



35
36
37
38
39
40
41
42
43
44







-
-
-
+
+
+








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 16384]"
write_file file4.dat "test file 4 (long line).[string repeat y 16384]\ntwo"
write_file file5.dat "[string repeat z 16384]\ntest file 5 (long line)."
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
54
55
56
57
58
59
60
61

62
63
64
65
66
67
68
54
55
56
57
58
59
60

61
62
63
64
65
66
67
68







-
+







==================================================================
--- file0.dat
+++ file0.dat
cannot compute difference between binary files}}

###############################################################################

write_file file1.dat [string repeat z 16384]
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}}
106
107
108
109
110
111
112
113
114
115














































116
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162










+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

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

Changes to test/fake-editor.tcl.

48
49
50
51
52
53
54





55

56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72

73
74
75
76
77
78
79
48
49
50
51
52
53
54
55
56
57
58
59

60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76

77
78
79
80
81
82
83
84







+
+
+
+
+
-
+
















-
+







  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]} then {
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} then {
  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"
      }
    }

Changes to test/fileStat.th1.

58
59
60
61
62
63
64
65

66
67
68
69
70
71
72
58
59
60
61
62
63
64

65
66
67
68
69
70
71
72







-
+








  proc getLatestTrunkCheckIn {} {
    tclEval {
      #
      # NOTE: Get the unique Id of the latest check-in on trunk.
      #
      return [lindex [regexp -line -inline -nocase -- \
          {^uuid:\s+([0-9A-F]{40}) } [eval [getFossilCommand \
          {^(?: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.

Changes to test/graph-test-1.wiki.

76
77
78
79
80
81
82




















83
84
85
86
87
88
89
90
91
92
93
94
95
96



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+














+
+
+
  *  <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
);
~~~~~

Changes to test/json.test.

21
22
23
24
25
26
27
28

29
30
31
32
33
34
35
36
37
38







39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57

58
59
60
61
62
63
64
21
22
23
24
25
26
27

28
29
30
31
32
33
34
35
36
37

38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62

63
64
65
66
67
68
69
70







-
+









-
+
+
+
+
+
+
+


















-
+







# 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"} then {
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
package require json
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] eq "FOSSIL-4102"}
             && [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} {
72
73
74
75
76
77
78
79


80
81
82
83
84
85
86
78
79
80
81
82
83
84

85
86
87
88
89
90
91
92
93







-
+
+







# 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]
  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
}
113
114
115
116
117
118
119
120

121
122

123
124
125
126
127
128
129
120
121
122
123
124
125
126

127
128
129
130
131
132
133
134
135
136
137







-
+


+







\r
}]
  }

  # handle the actual request
  flush stdout
  #exec $fossilexe
  set RESULT [fossil_maybe_answer $request http {*}$args]
  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 ""
167
168
169
170
171
172
173








174



175
176
177
178
179
180
181
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200







+
+
+
+
+
+
+
+

+
+
+







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"}

263
264
265
266
267
268
269
270

271
272
273
274
275
276
277
282
283
284
285
286
287
288

289
290
291
292
293
294
295
296







-
+







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 "hmnc" [dict get $AuthAnon capabilities]
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":{
305
306
307
308
309
310
311

312




313
314
315
316
317
318
319
320
321







322
323
324
325
326
327
328
329








330
331
332
333
334
335
336
324
325
326
327
328
329
330
331

332
333
334
335
336
337
338
339
340
341



342
343
344
345
346
347
348
349
350
351
352




353
354
355
356
357
358
359
360
361
362
363
364
365
366
367







+
-
+
+
+
+






-
-
-
+
+
+
+
+
+
+




-
-
-
-
+
+
+
+
+
+
+
+







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 \
test json-cap-POSTenv-name {[dict get $JR payload name] eq "anonymous"} knownBug
    {[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
test json-cap-GETcookie-name {[dict get $JR payload name] eq "anonymous"}
test json-cap-GETcookie-notsetup {![dict get $JR payload permissionFlags setup]}
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-0 {[string length $JR] > 0}
test_json_envelope_ok json-cap-GETcookie-env
test json-cap-GETcookie-name {[dict get $JR payload name] eq "anonymous"}
test json-cap-GETcookie-notsetup {![dict get $JR payload permissionFlags setup]}
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} {}
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







-
-
+




-
+
-
-
-
+
+
+
+
+
+

-
-
+
+

+
-
-
-
+
+
+
+
+
+
+
+
+







# 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
#catch {exec chmod 444 .rep.fossil}; # Unix. What about Win?
fossil_http_json /json/timeline/checkin $U1Cookie
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
protOut "chmod 444 repo"
if {$is_windows} then {
catch {exec chmod 444 .rep.fossil}; # Unix
catch {exec attrib +r .rep.fossil}; # Windows
fossil_http_json /json/timeline/checkin $U1Cookie -expectError
  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]} knownBug
test json-ROrepo-2-3 {![regexp {SQLITE_[A-Z]+:} $RESULT]} knownBug
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 chmod 666 .rep.fossil}; # Unix

  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
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






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







-




















+
+
+
+
+
+
#     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 open .rep.fossil

# 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/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 &#42; foo bar&#42;         |
|  3:| `a*"foo"*`     | a*"foo"*             | a&#42;&quot;foo&quot;&#42;   |
|  4:| `* a *`        | * a *                | &#42; a &#42;                |
|  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_           | &#95; foo bar&#95;           |
|  9:| `a_"foo"_`     | a_"foo"_             | a&#95;&quot;foo&quot;&#95;   |
| 10:| `foo_bar_`     | foo_bar_             | foo&#95;bar&#95;             |
| 11:| `5_6_78`       | 5_6_78               | 5&#95;6&#95;78               |
| 12:| `aa_"bb"_cc`   | aa_"bb"_cc           | aa&#95;&quot;bb&quot;&#95;cc |
| 13:| `foo-_(bar)_`  | foo-_(bar)_          | foo-<em>(bar)</em>           |
| 14:| `*(*foo`       | *(*foo               | &#42;(&#42;foo               |
| 15:| `*(*foo*)*`    | *(*foo*)*            | <em>(<em>foo</em>)</em>      |
| 16:| `*foo*bar`     | *foo*bar             | <em>foo</em>bar              |
| 17:| `_foo bar _`   | _foo bar _           | &#95;foo bar &#95;           |
| 18:| `_(_foo)`      | _(_foo)              | &#95;(&#95;foo)              |
| 19:| `_(_foo_)_`    | _(_foo_)_            | <em>(</em>foo<em>)</em>      |
| 20:| `_foo_bar`     | _foo_bar             | &#95;foo&#95;bar             |
| 21:| `_foo_bar_baz_` | _foo_bar_baz_       | <em>foo&#95;bar&#95;baz</em> |
| 22:| `foo_bar_baz`  | foo_bar_baz          | foo&#95;bar&#95;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 &#42;&#42; foo bar&#42;&#42;         |
|  3:| `a**"foo"**`     | a**"foo"**             | a&#42;&#42;&quot;foo&quot;&#42;&#42;   |
|  4:| `** a **`        | ** a **                | &#42;&#42; a &#42;&#42;                |
|  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__           | &#95;&#95; foo bar&#95;&#95;           |
|  9:| `a__"foo"__`     | a__"foo"__             | a&#95;&#95;&quot;foo&quot;&#95;&#95;   |
| 10:| `foo__bar__`     | foo__bar__             | foo&#95;&#95;bar&#95;&#95;             |
| 11:| `5__6__78`       | 5__6__78               | 5&#95;&#95;6&#95;&#95;78               |
| 12:| `aa__"bb"__cc`   | aa__"bb"__cc           | aa&#95;&#95;&quot;bb&quot;&#95;&#95;cc |
| 13:| `foo-__(bar)__`  | foo-__(bar)__          | foo-<strong>(bar)</strong>             |
| 14:| `**(**foo`       | **(**foo               | &#42;&#42;(&#42;&#42;foo               |
| 15:| `**(**foo**)**`  | **(**foo**)**          | <strong>(<strong>foo</strong>)</strong> |
| 16:| `**foo**bar`     | **foo**bar             | <strong>foo</strong>bar                |
| 17:| `__foo bar __`   | __foo bar __           | &#95;&#95;foo bar &#95;&#95;           |
| 18:| `__(__foo)`      | __(__foo)              | &#95;&#95;(&#95;&#95;foo)              |
| 19:| `__(__foo__)__`  | __(__foo__)__          | <strong>(</strong>foo<strong>)</strong> |
| 20:| `__foo__bar`     | __foo__bar             | &#95;&#95;foo&#95;&#95;bar             |
| 21:| `__foo__bar__baz__` | __foo__bar__baz__   | <strong>foo&#95;&#95;bar&#95;&#95;baz</strong> |
| 22:| `foo__bar__baz`  | foo__bar__baz          | foo&#95;&#95;bar&#95;&#95;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&quote='&nonASCII=😂&script=<script>alert('Broken!');</script>">
  here</a> and
  <a href='?a=B&quote="&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.

71
72
73
74
75
76
77
78

79
80

81
82

83
84

85
86
87
88
89
90
91

92
93

94
95

96
97

98
99
100
101
102
103

104
105

106
107
108
109
110
111
112
71
72
73
74
75
76
77

78
79

80
81

82
83

84
85
86
87
88
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 <<<<<<<<<<<<<<<
  <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 1)
  111 - This is line ONE of the demo program - 1111
  ======= COMMON ANCESTOR content follows ============================
  ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 1)
  111 - This is line one of the demo program - 1111
  ======= MERGED IN content follows ==================================
  ======= MERGED IN content follows =============================== (line 1)
  111 - This is line one OF the demo program - 1111
  >>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  >>>>>>> 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 <<<<<<<<<<<<<<<
  <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 1)
  111 - This is line one OF the demo program - 1111
  ======= COMMON ANCESTOR content follows ============================
  ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 1)
  111 - This is line one of the demo program - 1111
  ======= MERGED IN content follows ==================================
  ======= MERGED IN content follows =============================== (line 1)
  111 - This is line ONE of the demo program - 1111
  >>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  >>>>>>> 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
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
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
156
157
158
159
160
161
162
163
164


165
166

167
168
169

170
171
172
173
174
175
176

177
178
179

180
181
182


183
184
185
186
187
188

189
190

191
192
193
194
195
196
197
156
157
158
159
160
161
162


163
164
165

166
167
168

169
170
171
172
173
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 <<<<<<<<<<<<<<<
  ======= COMMON ANCESTOR content follows ============================
  <<<<<<< 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 ==================================
  ======= 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 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  >>>>>>> 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 <<<<<<<<<<<<<<<
  <<<<<<< 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 ============================
  ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 1)
  111 - This is line one of the demo program - 1111
  ======= MERGED IN content follows ==================================
  >>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  ======= 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
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
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
295
296
297
298
299
300
301
302

303
304
305
306
307
308
309
310
311

312
313
314
315
316
317
318
319
320

321
322
323
324
325
326
327
328
329

330
331
332
333
334
335

336
337
338
339
340
341
342
295
296
297
298
299
300
301

302
303
304
305
306
307
308
309
310

311
312
313
314
315
316
317
318
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 <<<<<<<<<<<<<<<
  <<<<<<< 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 ============================
  ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 2)
  efgh
  ijkl
  mnop
  qrst
  uvwx
  yzAB
  CDEF
  GHIJ
  ======= MERGED IN content follows ==================================
  ======= MERGED IN content follows =============================== (line 2)
  efgh
  ijkl
  mnop 3
  qrst 3
  uvwx 3
  yzAB 3
  CDEF
  GHIJ
  >>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  >>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  KLMN
  OPQR
  STUV
  XYZ.
}
fossil 3-way-merge t1 t2 t3 a23
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 
363
364
365
366
367
368
369
370

371
372
373
374
375
376
377
378
379

380
381
382
383
384
385
386
387
388

389
390
391
392
393
394
395
396
397

398
399
400
401
402
403

404
405
406
407
408
363
364
365
366
367
368
369

370
371
372
373
374
375
376
377
378

379
380
381
382
383
384
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 <<<<<<<<<<<<<<<
  <<<<<<< 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 ============================
  ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 2)
  efgh
  ijkl
  mnop
  qrst
  uvwx
  yzAB
  CDEF
  GHIJ
  ======= MERGED IN content follows ==================================
  ======= MERGED IN content follows =============================== (line 2)
  efgh
  ijkl
  mnop 3
  qrst 3
  uvwx 3
  yzAB 3
  CDEF
  GHIJ
  >>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  >>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  KLMN
  OPQR
  STUV
  XYZ.
}
fossil 3-way-merge t1 t2 t3 a23
fossil 3-way-merge t1 t2 t3 a23 -expectError
test merge1-7.2 {[same_file t23 a23]}

###############################################################################

test_cleanup

Changes to test/merge2.test.

16
17
18
19
20
21
22
23

24
25
26
27
28
29
30
16
17
18
19
20
21
22

23
24
25
26
27
28
29
30







-
+







############################################################################
#
# Tests of the delta mechanism.
#

test_setup ""

set filelist [glob $testdir/*]
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} {

Changes to test/merge3.test.

16
17
18
19
20
21
22
23

24
25
26
27

28

29
30
31
32
33











34
35
36
37
38
39
40
16
17
18
19
20
21
22

23
24
25
26

27
28
29





30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47







-
+



-
+

+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+







############################################################################
#
# Tests of the 3-way merge
#

test_setup ""

proc merge-test {testid basis v1 v2 result} {
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 3-way-merge t1 t2 t3 t4 {*}$fossil_args
  set x [read_file t4]
  regsub -all \
  regsub -all {<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <+} $x \
              {MINE:} x
  regsub -all {======= COMMON ANCESTOR content follows =+} $x {COM:} x
  regsub -all {======= MERGED IN content follows =+} $x {YOURS:} x
  regsub -all {>>>>>>> END MERGE CONFLICT >+} $x {END} x
    {<<<<<<< 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 {
65
66
67
68
69
70
71
72

73
74
75
76
77
78
79
80
81

82
83
84
85
86
87
88
89
90

91
92
93
94
95
96
97
98
99

100
101
102
103
104
105
106
107
108

109
110
111
112
113
114
115
116
117

118
119
120
121
122
123
124
72
73
74
75
76
77
78

79
80
81
82
83
84
85
86
87

88
89
90
91
92
93
94
95
96

97
98
99
100
101
102
103
104
105

106
107
108
109
110
111
112
113
114

115
116
117
118
119
120
121
122
123

124
125
126
127
128
129
130
131







-
+








-
+








-
+








-
+








-
+








-
+







  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
} {
138
139
140
141
142
143
144
145

146
147
148
149
150
151
152
145
146
147
148
149
150
151

152
153
154
155
156
157
158
159







-
+







  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
} {
193
194
195
196
197
198
199
200

201
202
203
204
205
206
207
208
209

210
211
212
213
214
215
216
200
201
202
203
204
205
206

207
208
209
210
211
212
213
214
215

216
217
218
219
220
221
222
223







-
+








-
+







  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
248
249
250
251
252
253
254
255

256
257
258
259
260
261
262
263
264

265
266
267
268
269
270
271
255
256
257
258
259
260
261

262
263
264
265
266
267
268
269
270

271
272
273
274
275
276
277
278







-
+








-
+







  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
303
304
305
306
307
308
309
310

311
312
313
314
315
316
317
318
319

320
321
322
323
324
325
326
310
311
312
313
314
315
316

317
318
319
320
321
322
323
324
325

326
327
328
329
330
331
332
333







-
+








-
+







  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
357
358
359
360
361
362
363
364

365
366
367
368
369
370
371
372
373

374
375
376
377
378
379
380
364
365
366
367
368
369
370

371
372
373
374
375
376
377
378
379

380
381
382
383
384
385
386
387







-
+








-
+







  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
412
413
414
415
416
417
418
419

420
421
422
423
424
425
426
427
428

429
430
431
432
433
434
435
419
420
421
422
423
424
425

426
427
428
429
430
431
432
433
434

435
436
437
438
439
440
441
442







-
+








-
+







  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
467
468
469
470
471
472
473
474

475
476
477
478
479
480
481
482
483

484
485
486
487
488
489
490
474
475
476
477
478
479
480

481
482
483
484
485
486
487
488
489

490
491
492
493
494
495
496
497







-
+








-
+







  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
522
523
524
525
526
527
528
529

530
531
532
533
534
535
536
537
538

539
540
541
542
543
544
545
529
530
531
532
533
534
535

536
537
538
539
540
541
542
543
544

545
546
547
548
549
550
551
552







-
+








-
+







  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
577
578
579
580
581
582
583
584

585
586
587
588
589
590
591
592
593

594
595
596
597
598
599
600
584
585
586
587
588
589
590

591
592
593
594
595
596
597
598
599

600
601
602
603
604
605
606
607







-
+








-
+







  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
623
624
625
626
627
628
629
630

631
632
633
634
635
636
637
638
639

640
641
642
643
630
631
632
633
634
635
636

637
638
639
640
641
642
643
644
645

646
647
648
649
650







-
+








-
+




  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.

16
17
18
19
20
21
22
23

24
25
26
27
28


29
30
31


32
33
34
35
36


37
38
39
40
41
42
43

44
45
46
47
48
49

50
51

52
53
54
55
56
57
58
59
60
61
62
63
64
65
66

67
68
69
70
71
72
73
16
17
18
19
20
21
22

23
24
25
26


27
28
29


30
31
32
33
34


35
36
37
38
39
40
41
42

43
44
45
46
47
48

49
50

51
52
53
54
55
56
57
58
59
60
61
62
63
64
65

66
67
68
69
70
71
72
73







-
+



-
-
+
+

-
-
+
+



-
-
+
+






-
+





-
+

-
+














-
+







############################################################################
#
# Tests of the 3-way merge
#

test_setup ""

proc merge-test {testid basis v1 v2 result1 result2} {
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 3-way-merge t1 t3 t2 t5
  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.*<<} $x {>} x
  regsub -all {=======.*=======} $x {=} x
  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.*<<} $y {>} y
  regsub -all {=======.*=======} $y {=} y
  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 merge3-$testid 0
    test merge4-$testid 0
  } else {
    set result2 [string trim $result2]
    if {$y!=$result2} {
      protOut "  Expected \[$result2\]"
      protOut "       Got \[$y\]"
      test merge3-$testid 0
      test merge4-$testid 0
    } else {
      test merge3-$testid 1
      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
} {
81
82
83
84
85
86
87
88

89
90
91
92
93
94
95
81
82
83
84
85
86
87

88
89
90
91
92
93
94
95







-
+







  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
} {

Changes to test/merge5.test.

14
15
16
17
18
19
20

21


22
23
24
25
26

27
28
29
30
31
32
33
14
15
16
17
18
19
20
21

22
23
24
25
26
27

28
29
30
31
32
33
34
35







+
-
+
+




-
+







#   http://www.hwaci.com/drh/
#
############################################################################
#
# Tests of the "merge" command
#

if {! $::QUIET} {
puts "Skipping Merge5 tests"
  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 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] {

Changes to test/merge_renames.test.

260
261
262
263
264
265
266
267

268

269

270
271
272
273
274

275

276

277
278
279
280
281
282
283
260
261
262
263
264
265
266

267
268
269

270
271
272
273
274

275
276
277

278
279
280
281
282
283
284
285







-
+

+
-
+




-
+

+
-
+







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
fossil merge b2 -expectError
test_status_list merge_renames-8-1 $RESULT {
  MERGE f1
  WARNING: no common ancestor for f1
  WARNING: 1 merge conflicts
}

fossil revert
fossil merge --integrate b1
fossil merge b2
fossil merge b2 -expectError
test_status_list merge_renames-8-2 $RESULT {
  MERGE f1
  WARNING: no common ancestor for f1
  WARNING: 1 merge conflicts
}

#############################################
#  Test 9                                   #
#  Merging a delete/rename/add combination  #
#############################################

306
307
308
309
310
311
312
313
314


315
316
317
318
319
320
321
322
323
324
325

326
327
328
329
330
331
332
308
309
310
311
312
313
314


315
316
317
318
319
320
321
322
323
324
325
326

327
328
329
330
331
332
333
334







-
-
+
+










-
+







  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 f2
  DELETED f2 (overwritten by rename)
  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
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 ""}

###################################################################
366
367
368
369
370
371
372
373
374


375
376
377
378
379
380
381
368
369
370
371
372
373
374


375
376
377
378
379
380
381
382
383







-
-
+
+







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
  RENAMED f2
  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
493
494
495
496
497
498
499
500

501
502
503
504
505
506
507
495
496
497
498
499
500
501

502
503
504
505
506
507
508
509







-
+







  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 $repoDir/f2
  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
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
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







-
-






-





-
-






-













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
  DELETE f2n
  ADDED f2n
}
test_file_contents merge_renames-13-7 f2n "line1"

fossil revert
test_status_list merge_renames-13-8 $RESULT {
  REVERT f1
  REVERT f2n
}
fossil update trunk
fossil merge --integrate b
test_status_list merge_renames-13-9 $RESULT {
  UPDATE f1
  DELETE f2n
  ADDED f2n
}
test_file_contents merge_renames-13-10 f2n "line1"

fossil revert
test_status_list merge_renames-13-11 $RESULT {
  REVERT f1
  REVERT f2n
}

######################################
#
# 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

Changes to test/merge_warn.test.

38
39
40
41
42
43
44
45

46

47

48
49
50
51



52
53
54
55
56
57
58
59
38
39
40
41
42
43
44

45
46
47

48
49



50
51
52

53
54
55
56
57
58
59







-
+

+
-
+

-
-
-
+
+
+
-







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
fossil merge --integrate mrg -expectError
test_status_list merge_warn-1 $RESULT {
  WARNING: 1 unmanaged files were overwritten
  WARNING: no common ancestor for f2
  WARNING: 2 merge conflicts
  DELETE f1
  WARNING: local edits lost for f1
  ADDED f3 (overwrites an unmanaged file)
  WARNING: 1 merge conflicts
  MERGE f2
  ADDED f3 (overwrites an unmanaged file), original copy backed up locally
  WARNING: local edits lost for f1
  WARNING: 1 unmanaged files were overwritten
}
test merge_warn-2 {
  [string first "ignoring --integrate: mrg is not a leaf" $RESULT]>=0
}

###############################################################################

Changes to test/mv-rm.test.

48
49
50
51
52
53
54



55

56
57
58
59
60
61
62
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66







+
+
+

+







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]
411
412
413
414
415
416
417
418

419
420
421
422
423
424
425
426
427
428

429
430
431
432
433
434
435


























436
437
438
439
440
415
416
417
418
419
420
421

422
423
424
425
426
427
428
429
430
431

432
433
434
435
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







-
+









-
+







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+






############################################
# Test 18: Move Directory to New Directory #
############################################

fossil mv --hard subdirC subdirD
test mv-file-new-directory-7 {
  [normalize_result] eq "RENAME subdirC subdirD\nMOVED_FILE ${rootDir}/subdirC"
  [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 "REVERT   subdirC/f10\nREVERT   subdirC/f11${undoMsg}"
  [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

Changes to 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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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>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:
<blockquote><b>
fossil test-name-changes --debug  b120bc8b262ac 374920b20944b
</b></blockquote>
<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
58
59
60
61
62
63
64
65

66
67
68
69




70
71
72
73

74
75
76
77
78
79
80
81
82
83
84

85







86
87
88
89
90
91
92
93
94
95
96
72
73
74
75
76
77
78

79
80
81
82
83
84
85
86
87
88
89
90

91
92
93
94
95
96
97
98
99
100
101

102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121







-
+




+
+
+
+



-
+










-
+

+
+
+
+
+
+
+











<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).
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://www.fossil-scm.org/index.html/vdiff?from=release&to=trunk&sbs=1|all code changes since the previous release], paying particular
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://www.fossil-scm.org/] website for at least 48 hours (without
[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

Changes to test/revert.test.

96
97
98
99
100
101
102
103

104
105
106
107
108
109
110
111
112
113

114
115
116
117
118
119
120
121

122
123
124
125
126
127
128
96
97
98
99
100
101
102

103
104
105
106
107
108
109
110
111
112

113
114
115
116
117
118
119
120

121
122
123
124
125
126
127
128







-
+









-
+







-
+







# Test with a single filename argument
#
revert-test 1-2 f0 {
  UNMANAGE f0
} -changes {
  DELETED f1
  EDITED f2
  RENAMED f3n
  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 f3n
  RENAMED f3  ->  f3n
} -exists {f0 f1 f2 f3n} -notexists f3

revert-test 1-4 f2 {
  REVERT   f2
} -changes {
  ADDED f0
  DELETED f1
  RENAMED f3n
  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
184
185
186
187
188
189
190
191




































































































































































192
193
194
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358








+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



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"
}

Changes to test/set-manifest.test.

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38









39
40
41
42
43
44
45
46
47
48

49
50
51
52
53
54
55
56
57
58

59
60
61
62
63
64
65
14
15
16
17
18
19
20






21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

51
52
53
54
55
56
57
58
59
60

61
62
63
64
65
66
67
68







-
-
-
-
-
-












+
+
+
+
+
+
+
+
+









-
+









-
+







#   http://www.hwaci.com/drh/
#
############################################################################
#
# Test manifest setting
#

# 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
package require sha1

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 [glob -nocomplain manifest*]
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 [glob manifest*]
  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
83
84
85
86
87
88
89
90

91
92
93
94
95
96
97
98
99
100

101
102
103
104
105
106
107
108
109
110
111
112
113
114

115
116
117
118
119
120
121
86
87
88
89
90
91
92

93
94
95
96
97
98
99
100
101
102

103
104
105
106
107
108
109
110
111
112
113
114
115
116

117
118
119
120
121
122
123
124







-
+









-
+













-
+







# 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 [glob -nocomplain manifest*]
  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 [glob -nocomplain manifest*]
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 [glob manifest*]
  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

Changes to test/settings-repo.test.

38
39
40
41
42
43
44



45


46
47
48
49
50
51
52
53
54
55
56
57
58

59
60
61
62
63
64

65
66
67
68
69
70
71
72

73
74
75
76
77
78

79
80
81
82
83
84
85
86

87
88
89
90
91
92
93

94
95
96
97
98
99
100
101
102

103
104
105
106
107
108
109

110
111
112
113
114
115
116
38
39
40
41
42
43
44
45
46
47

48
49
50
51
52
53
54
55
56
57
58
59
60
61

62
63
64
65
66
67

68
69
70
71
72
73
74
75

76
77
78
79
80
81

82
83
84
85
86
87
88
89

90
91
92
93
94
95
96

97
98
99
100
101
102
103
104
105

106
107
108
109
110
111
112

113
114
115
116
117
118
119
120







+
+
+
-
+
+












-
+





-
+







-
+





-
+







-
+






-
+








-
+






-
+







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 --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
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
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
fossil unset bad-setting -expectError

test settings-unset-bad-local {
  [normalize_result] eq "no such setting: bad-setting"
}

fossil unset bad-setting --global
fossil unset bad-setting --global -expectError

test settings-unset-bad-global {
  [normalize_result] eq "no such setting: bad-setting"
}

###############################################################################

fossil settings ssl some_value
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
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
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
fossil unset ssl --global -expectError

test settings-unset-ambiguous-global {
  [normalize_result] eq
  "ambiguous setting \"ssl\" - might be: ssl-ca-location ssl-identity"
}

###############################################################################
240
241
242
243
244
245
246
247

248
249
250
251
252
253
254
244
245
246
247
248
249
250

251
252
253
254
255
256
257
258







-
+







    [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 $value
    $data eq ""
  }

  file delete $fileName

  fossil settings $name --exact
  set data [normalize_result]

Changes to test/settings.test.

37
38
39
40
41
42
43
44

45
46
47
48
49
50
51
37
38
39
40
41
42
43

44
45
46
47
48
49
50
51







-
+







#       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] {
      -all -line -inline -- {^([a-z][a-z0-9\-]*) ?.*$} $data] {
    lappend names $name
  }

  return $names
}

###############################################################################
90
91
92
93
94
95
96



97


98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114

115
116
117
118
119
120

121
122
123
124
125
126
127
128
90
91
92
93
94
95
96
97
98
99

100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117

118
119
120
121
122
123

124
125
126
127
128
129
130
131
132







+
+
+
-
+
+
















-
+





-
+








  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
    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
fossil settings bad-setting -expectError

test settings-query-bad-local {
  [normalize_result] eq "no such setting: bad-setting"
}

fossil settings bad-setting --global
fossil settings bad-setting --global -expectError

test settings-query-bad-global {
  [normalize_result] eq "no such setting: bad-setting"
}

###############################################################################

test_cleanup

Changes to test/stash.test.

139
140
141
142
143
144
145
146

147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164

165
166
167
168
169
170
171
172
173
174
175
176




177
178
179
180
181
182
183
184
185
186
187
188
189
190

191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216






217
218
219
220
221
222
223
139
140
141
142
143
144
145

146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163

164
165
166
167
168
169
170
171
172




173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189

190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210






211
212
213
214
215
216
217
218
219
220
221
222
223







-
+

















-
+








-
-
-
-
+
+
+
+













-
+




















-
-
-
-
-
-
+
+
+
+
+
+







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
+++ 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
==================================================================
--- 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 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    f3n
  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 {save -m "Reported 2016-02-09"} {
  REVERT   f1
  DELETE   f1new
} -changes {
} -addremove {
} -exists {f1 f2 f3} -notexists {f1new} -knownbugs {-code -result}
#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
263
264
265
266
267
268
269
270

271
272
273
274
275
276
277
263
264
265
266
267
268
269

270
271
272
273
274
275
276
277







-
+







  ADDED f3
} -exists {f1 f2 f3} -notexists {}
#fossil status
fossil stash show
test stash-3-1-show {[normalize_result] eq {ADDED f3
Index: f3
==================================================================
--- f3
--- /dev/null
+++ f3
@@ -0,0 +1,1 @@
+f3}}
stash-test 3-1-pop {pop} {
  ADDED f3
} -changes {
  ADDED f3
290
291
292
293
294
295
296
297

298
299
300

301
302
303
304




305
306

307
308
309
310
311
312

313
314
315
316
317
318
319
290
291
292
293
294
295
296

297
298
299
300
301




302
303
304
305
306

307
308
309
310
311
312

313
314
315
316
317
318
319
320







-
+



+
-
-
-
-
+
+
+
+

-
+





-
+







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 f2n
  RENAMED f2  ->  f2n
} -addremove {
} -exists {f1 f2n} -notexists {f2}

fossil stash save -m f2n
stash-test 3-2 {save -m f2n} {
  REVERT f2
  DELETE f2n
} -exists {f1 f2} -notexists {f2n} -knownbugs {-result}
#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-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    f2n
  RENAMED    f2  ->  f2n
} -addremove {
} -exists {f1 f2n} -notexists {f2}



########
# fossil stash snapshot ?-m|--comment COMMENT? ?FILES...?
366
367
368
369
370
371
372
373

374
375
376
377
378
379
380
367
368
369
370
371
372
373

374
375
376
377
378
379
380
381







-
+







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    f3n
  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]}

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.

Changes to test/symlinks.test.

16
17
18
19
20
21
22
23

24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39



40
41
42
43
44
45
46
16
17
18
19
20
21
22

23
24
25
26
27







28
29
30
31
32
33
34
35
36
37
38
39
40
41
42







-
+




-
-
-
-
-
-
-





+
+
+







############################################################################
#
# Symbolic link tests.
#

set path [file dirname [info script]]

if {$tcl_platform(platform) eq "windows"} {
if {$is_windows} {
  puts "Symlinks are not supported on Windows."
  test_cleanup_then_return
}

fossil test-th-eval --open-config "setting allow-symlinks"

if {![string is true -strict [normalize_result]]} {
  puts "Symlinks are not enabled."
  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
60
61
62
63
64
65
66



67




68
69
70
71
72

73
74
75
76
77
78

79
80
81
82
83
84
85
86
87
88
89
90
91
92

93
94
95
96
97
98


99
100
101
102
103
104
105
106
107
108
109
56
57
58
59
60
61
62
63
64
65

66
67
68
69
70
71
72
73

74
75
76
77
78
79

80
81
82
83
84
85
86
87
88
89
90
91
92
93

94
95
96
97
98


99
100
101
102
103
104
105
106
107
108
109
110
111







+
+
+
-
+
+
+
+




-
+





-
+













-
+




-
-
+
+












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"
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 "symdirA/f1.txt"}
test symlinks-dir-5 {[normalize_result] eq ""}

###############################################################################

fossil extras
test symlinks-dir-6 {[normalize_result] eq \
"subdirA/f1.txt\nsubdirA/f2.txt\nsymdirA/f2.txt"}
"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 $repository
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 1}
test symlinks-dir-14 {[file exists [file join $rootDir symdirA f1.txt]] eq 1}
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

Changes to test/tester.tcl.

18
19
20
21
22
23
24





25
26
27
28
29
30
31
32
33
34

35
36
37
38
39
40
41
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47







+
+
+
+
+










+







# 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 {
168
169
170
171
172
173
174






175
176
177
178
179
180




181
182
183
184
185
186







187
188

189
190
191
192
193





194
195

196







197
198
199

200
201
202
203





204
205
206
207
208
209
210
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196






197
198
199
200
201
202
203


204





205
206
207
208
209


210
211
212
213
214
215
216
217
218
219
220
221
222




223
224
225
226
227
228
229
230
231
232
233
234







+
+
+
+
+
+






+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
+
-
-
-
-
-
+
+
+
+
+
-
-
+

+
+
+
+
+
+
+



+
-
-
-
-
+
+
+
+
+







  }
  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
    if {$keepNewline} {
      set rc [catch {eval exec -keepnewline $cmd <$prompt_file} result]
    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
    } else {
      set rc [catch {eval exec $cmd <$prompt_file} result]
      set rc [catch $execCmd result]
    }
    file delete $prompt_file
  } else {
    if {$keepNewline} {
      set rc [catch {eval exec -keepnewline $cmd} result]
      file delete $prompt_file
    } else {
      set execCmd [list eval exec]
      if {$keepNewline} {lappend execCmd -keepnewline}
      lappend execCmd $cmd
    } else {
      set rc [catch {eval exec $cmd} result]
      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: $result" 1
  } elseif {$::VERBOSE} {
    protOut "RESULT: $result"
    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} {
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248

249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273


274
275
276



277
278







279
280

281
282

283

284
285

286
287
288











289
290
291



292
293
294

295
296
297
298

299

300

301

302
303

304

305

306

307
308



309

310
311
312

313
314


315
316
317
318
319
320
321
255
256
257
258
259
260
261

262
263
264
265
266
267
268
269
270

271








272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382







-









-
+
-
-
-
-
-
-
-
-

















+
+



+
+
+


+
+
+
+
+
+
+


+


+

+


+



+
+
+
+
+
+
+
+
+
+
+



+
+
+



+




+

+

+

+


+

+

+

+


+
+
+

+



+


+
+







#
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 \
      allow-symlinks \
      binary-glob \
      clean-glob \
      crlf-glob \
      crnl-glob \
      dotfiles \
      empty-dirs \
      encoding-glob \
      ignore-glob \
      keep-glob \
      manifest \
      manifest]
      th1-setup \
      th1-uri-regexp]

  fossil test-th-eval "hasfeature tcl"

  if {[normalize_result] eq "1"} {
    lappend result tcl-setup
  }

  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 \
      default-skin \
      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
345
346
347
348
349
350
351

352








353
354
355
356
357
358
359
360
361
362
363
364
365

366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384



385
386
387
388
389
390
391
406
407
408
409
410
411
412
413

414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433

434
435
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 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 [expr {$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} then {
  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 {use --repository} $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\",\
415
416
417
418
419
420
421


422
423
424
425
426
427





428
429
430
431
432
433
434
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







+
+





-
+
+
+
+
+







    }
    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} {return}; # All cleanup disabled?
  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."
449
450
451
452
453
454
455
456

457
458
459
460
461
462
463
526
527
528
529
530
531
532

533
534
535
536
537
538
539
540







-
+







  # 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} {
  if {$::is_windows || $::is_cygwin} {
    robust_delete [file join $::tempHomePath _fossil]
  } else {
    robust_delete [file join $::tempHomePath .fossil]
  }
  robust_delete $::tempHomePath
}

560
561
562
563
564
565
566
567

568
569
570
571
572
573
574
637
638
639
640
641
642
643

644
645
646
647
648
649
650
651







-
+







#
# 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 {
    run_in_checkout $rscript
    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
776
777
778
779
780
781
782

783
784
785
786
787
788
789
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867







+







      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} {
938
939
940
941
942
943
944


945


946
947
948
949
950
951
952
1016
1017
1018
1019
1020
1021
1022
1023
1024

1025
1026
1027
1028
1029
1030
1031
1032
1033







+
+
-
+
+







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 \
  fossil http $inFileName $outFileName 127.0.0.1 $repository --localauth
      $repository --localauth --th-trace -expectError

  set result [expr {[file exists $outFileName] ? [read_file $outFileName] : ""}]

  if {1} {
    catch {file delete $inFileName}
    catch {file delete $outFileName}
  }

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







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







+
+
+
+
+
+
+
+
+










+
+
+
+
+











+

+
+
+
+














+
+
+
+
+
+
+
} 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
}

Changes to test/th1-docs.test.

28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

46
47
48
49
50
51
52
53
54
55

56
57
58
59
60
61
62
63
64









65
66
67
68
69
70
71



72
73

74
75
76
77
78
79
28
29
30
31
32
33
34








35
36

37
38
39
40
41
42
43
44
45


46

47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66




67
68
69


70
71
72
73
74
75
76







-
-
-
-
-
-
-
-


-
+








-
-
+
-








+
+
+
+
+
+
+
+
+



-
-
-
-
+
+
+
-
-
+






fossil test-th-eval "hasfeature tcl"

if {[normalize_result] ne "1"} {
  puts "Fossil was not compiled with Tcl support."
  test_cleanup_then_return
}

if {$::outside_fossil_repo} {
  puts "Skipping th1-docs-* tests: not in Fossil repo checkout."
  test_cleanup_then_return
} elseif ($::dirty_ckout) {
  puts "Skipping th1-docs-* tests: uncommitted changes in Fossil checkout."
  test_cleanup_then_return
}

###############################################################################

test_setup ""
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.

###############################################################################

run_in_checkout {
  set data [fossil info]
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"

###############################################################################

run_in_checkout {
  set RESULT [test_fossil_http \
      $repository $dataFileName /doc/trunk/test/fileStat.th1]
}
set RESULT [test_fossil_http \
  $repository $dataFileName /doc/trunk/fileStat.th1]


test th1-docs-1a {[regexp {<title>Fossil: test/fileStat.th1</title>} $RESULT]}
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

Changes to test/th1-hooks.test.

124
125
126
127
128
129
130

131

132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150

151
152
153
154
155
156
157
124
125
126
127
128
129
130
131

132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150

151
152
153
154
155
156
157
158







+
-
+


















-
+







  error "unable to locate repository"
}

set dataFileName [file join $::testdir th1-hooks-input.txt]

###############################################################################

set savedTh1Setup [fossil settings th1-setup]
saveTh1SetupFile; writeTh1SetupFile $testTh1Setup
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; # NOTE: Bad "WHEN" argument.
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}}

###############################################################################

193
194
195
196
197
198
199
200

201
202
203
204
205
206
207
194
195
196
197
198
199
200

201
202
203
204
205
206
207
208







-
+








fossil test3
test th1-custom-cmd-3a {[string trim $RESULT] eq \
    {<h1><b>command_hook test3</b></h1>}}

###############################################################################

fossil test4
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]]}

225
226
227
228
229
230
231
232

233
234
235
236
226
227
228
229
230
231
232

233
234
235
236
237







-
+




test th1-custom-web-1a {[next_to_last_data_line] eq $repository}

test th1-custom-web-1b {[last_data_line] eq \
    {<h1><b>command_hook http webpage_hook test1 webpage_notify test1</b></h1>}}

###############################################################################

restoreTh1SetupFile
fossil settings th1-setup $savedTh1Setup

###############################################################################

test_cleanup

Changes to test/th1-tcl.test.

75
76
77
78
79
80
81
82

83
84
85
86
87
88
89
90

91
92
93
94
95
96
97
98
99


100
101
102
103
104
105
106
107

108
109
110
111
112
113
114
115

116
117
118
119
120
121
122
123
124


125
126

127
128
129
130
131
132
133







134

135
136
137
138
139
140
141
75
76
77
78
79
80
81

82
83
84
85
86
87
88
89

90
91
92
93
94
95
96
97


98
99
100
101
102
103
104
105
106

107
108
109
110
111
112
113
114

115
116
117
118
119
120
121
122


123
124
125

126
127
128
129
130
131
132
133
134
135
136
137
138
139
140

141
142
143
144
145
146
147
148







-
+







-
+







-
-
+
+







-
+







-
+







-
-
+
+

-
+







+
+
+
+
+
+
+
-
+







}

###############################################################################

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:\
test th1-tcl-3 {$RESULT eq {<hr><p class="thmainError">ERROR:\
invalid command name &quot;bad_command&quot;</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:\
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\
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 &quot;bad_command&quot;</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:\
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:\
test th1-tcl-7 {$RESULT eq {<hr><p class="thmainError">ERROR:\
syntax error in expression: &quot;2**0&quot;</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\
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\
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 $fossilexe] 3 \
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 \

Changes to test/th1.test.

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
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







+
+
-
+
+
+



+












+






-

+








+







  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 \
foreach perm [list a b c d e f g h i j k l m n o p q r s t u v w x y z] {
    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}}
    unset ::env(TH1_TEST_USER_CAPS)

    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)
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
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







-
+
+
+
+
+
+

-
+

-
+



-
+

-
-
+
+











-
+
+
+
+
+
+

-
+

-
+



-
+

-
-
+
+



+
+
+
+
+
+
+
+
+
+
+
+
+
+







###############################################################################

fossil test-th-eval "trace {}"
test th1-trace-1 {$RESULT eq {}}

###############################################################################

fossil test-th-eval --th-trace "trace {}"
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 {[normalize_result] eq \
  test th1-trace-2 {$normalized_result eq \
{------------------ BEGIN TRACE LOG ------------------
th1-init 0x0 => 0x0<br />
th1-init 0x0 => 0x0<br>

------------------- END TRACE LOG -------------------}}
} else {
  test th1-trace-2 {[normalize_result] eq \
  test th1-trace-2 {$normalized_result eq \
      {------------------ BEGIN TRACE LOG ------------------
th1-init 0x0 => 0x0<br />
th1-setup {} => TH_OK<br />
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.}"
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 {[normalize_result] eq \
  test th1-trace-4 {$normalized_result eq \
      {------------------ BEGIN TRACE LOG ------------------
th1-init 0x0 => 0x0<br />
th1-init 0x0 => 0x0<br>
this is a trace message.
------------------- END TRACE LOG -------------------}}
} else {
  test th1-trace-4 {[normalize_result] eq \
  test th1-trace-4 {$normalized_result eq \
      {------------------ BEGIN TRACE LOG ------------------
th1-init 0x0 => 0x0<br />
th1-setup {} => TH_OK<br />
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}}

###############################################################################
784
785
786
787
788
789
790
791

792
793
794
795
796
797
798
815
816
817
818
819
820
821

822
823
824
825
826
827
828
829







-
+








fossil test-th-eval --open-config "styleFooter"
test th1-footer-2 {$RESULT eq {}}

###############################################################################

fossil test-th-eval --open-config --cgi "styleHeader {}; styleFooter"
test th1-footer-3 {[regexp -- {</body></html>} $RESULT]}
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?"}}

990
991
992
993
994
995
996
997

998
999
1000
1001
1002
1003
1004
1021
1022
1023
1024
1025
1026
1027

1028
1029
1030
1031
1032
1033
1034
1035







-
+







###############################################################################

fossil test-th-eval "globalState vfs"
test th1-globalState-14 {[string length $RESULT] == 0}

###############################################################################

if {$tcl_platform(platform) eq "windows"} {
if {$is_windows || $is_cygwin} {
  set altVfs win32-longpath
} else {
  set altVfs unix-dotfile
}

###############################################################################

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
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







-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+






-







# 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 catch\
      checkout combobox continue date decorate dir enable_output encode64\
      error expr for getParameter glob_match globalState hascap hasfeature\
      html htmlize http httpize if info insertCsrf lindex linecount list\
      llength lsearch markdown proc puts query randhex redirect regexp\
      reinitialize rename render repository return searchable set\
      setParameter setting stime string styleFooter styleHeader tclReady\
      trace unset unversioned uplevel upvar utime verifyCsrf wiki}
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 \

Changes to test/unversioned.test.

16
17
18
19
20
21
22
23

24
25
26
27
28
29
30



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50







51

52
53

54
55
56
57
58
59
60
16
17
18
19
20
21
22

23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

61
62

63
64
65
66
67
68
69
70







-
+







+
+
+




















+
+
+
+
+
+
+
-
+

-
+







############################################################################
#
# The "unversioned" command.
#

set path [file dirname [info script]]

if {[catch {package require sha1}] != 0} then {
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
fossil unversioned -expectError
test unversioned-1 {[normalize_result] eq \
[string map [list %fossil% [file nativename $fossilexe]] {Usage: %fossil%\
[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 {}}

117
118
119
120
121
122
123
124

125
126
127
128
129
130
131
127
128
129
130
131
132
133

134
135
136
137
138
139
140
141







-
+







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 \
{names of unversioned files may not contain whitespace}}
{unversioned filenames may not contain whitespace: 'unversioned space.txt'}}

###############################################################################

fossil unversioned add "unversioned space.txt" --as unversioned3.txt
test unversioned-15 {[normalize_result] eq {}}

###############################################################################
310
311
312
313
314
315
316

317


318
319
320
321
322
323
324
325
326

327


328
329
330
331
332
333


334
335
336
337
338
339
340
341
342
343
344
345
346
347

348

349
350
351
352
353
354
355
320
321
322
323
324
325
326
327

328
329
330
331
332
333
334
335
336
337
338
339

340
341
342
343
344
345


346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362

363
364
365
366
367
368
369
370







+
-
+
+









+
-
+
+




-
-
+
+














+
-
+








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 \".]
  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] \".]
  puts [appendArgs "Now in client directory \"" [pwd] \".]
}
write_file unversioned-client1.txt "This is unversioned client file #1."

###############################################################################

fossil_maybe_answer y clone $remote uvrepo.fossil
fossil open uvrepo.fossil
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)} \
\n? done, sent: \d+  received: \d+  ip: 127.0.0.1} [normalize_result]]}
[normalize_result]]}

###############################################################################

fossil unversioned ls
test unversioned-47 {[normalize_result] eq {unversioned2.txt
unversioned5.txt}}

386
387
388
389
390
391
392

393

394
395
396
397
398
399
400
401
402
403
404
405
406
407
408

409
410
411
412
413
414
415
416







+
-
+








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)} \
\n? done, sent: \d+  received: \d+  ip: 127.0.0.1} [normalize_result]]}
[normalize_result]]}

###############################################################################

fossil unversioned list
test unversioned-53 {[regexp \
{^[0-9a-f]{12} 2016-10-01 00:00:00       30       30\
unversioned2\.txt
410
411
412
413
414
415
416

417

418
419
420
421
422
423
424
425
426
427

428


429
430
431
432
433

434

435
436
437

438
439
440
441
442
443
444
426
427
428
429
430
431
432
433

434
435
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







+
-
+










+
-
+
+





+
-
+



+








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)} \
\n? done, sent: \d+  received: \d+  ip: 127.0.0.1} [normalize_result]]}
[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] \".]
  puts [appendArgs "Now in server directory \"" [pwd] \".]
}

###############################################################################

set stopped [test_stop_server $stopArg $pid $outTmpFile]

if {! $::QUIET} {
puts [appendArgs \
  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

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

Changes to test/utf.test.

33
34
35
36
37
38
39
40

41
42
43
44
45
46
47
33
34
35
36
37
38
39

40
41
42
43
44
45
46
47







-
+







proc utf-check {testname args} {
  global tempPath
  set i 1
  foreach {fileName result} $args {
    set fileName [file join $tempPath $fileName]
    fossil test-looks-like-utf $fileName
    set result [string map [list %TEMP% $tempPath \r\n \n] $result]
    # if {$::RESULT ne $result} {puts stdout $::RESULT}
    # if {$::RESULT ne $result} {puts stdout $::RESULT; exit}
    test utf-check-$testname.$i {$::RESULT eq $result}
    incr i
  }
}

unset -nocomplain enc
array set enc [list     \
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
































178
179
180
181
182
183
184
139
140
141
142
143
144
145
































146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







     73 [appendArgs \x00 AB\x00\n]            \
     74 [appendArgs \x00 ABC\x00\n]           \
     75 [appendArgs \x00 ABCD\x00\n]          \
     76 [appendArgs \x00 A\x00\r\n]           \
     77 [appendArgs \x00 AB\x00\r\n]          \
     78 [appendArgs \x00 ABC\x00\r\n]         \
     79 [appendArgs \x00 ABCD\x00\r\n]        \
     80 [string repeat A 8193]                \
     81 [string repeat A 8193]\r              \
     82 [string repeat A 8193]\n              \
     83 [string repeat A 8193]\r\n            \
     84 [string repeat ABCD 2049]             \
     85 [string repeat ABCD 2049]\r           \
     86 [string repeat ABCD 2049]\n           \
     87 [string repeat ABCD 2049]\r\n         \
     88 \x00[string repeat A 8193]            \
     89 \x00[string repeat A 8193]\r          \
     90 \x00[string repeat A 8193]\n          \
     91 \x00[string repeat A 8193]\r\n        \
     92 \x00[string repeat ABCD 2049]         \
     93 \x00[string repeat ABCD 2049]\r       \
     94 \x00[string repeat ABCD 2049]\n       \
     95 \x00[string repeat ABCD 2049]\r\n     \
     96 [string repeat A 8193]\x00            \
     97 [string repeat A 8193]\x00\r          \
     98 [string repeat A 8193]\x00\n          \
     99 [string repeat A 8193]\x00\r\n        \
    100 [string repeat ABCD 2049]\x00         \
    101 [string repeat ABCD 2049]\x00\r       \
    102 [string repeat ABCD 2049]\x00\n       \
    103 [string repeat ABCD 2049]\x00\r\n     \
    104 \x00[string repeat A 8193]\x00        \
    105 \x00[string repeat A 8193]\x00\r      \
    106 \x00[string repeat A 8193]\x00\n      \
    107 \x00[string repeat A 8193]\x00\r\n    \
    108 \x00[string repeat ABCD 2049]\x00     \
    109 \x00[string repeat ABCD 2049]\x00\r   \
    110 \x00[string repeat ABCD 2049]\x00\n   \
    111 \x00[string repeat ABCD 2049]\x00\r\n \
     80 [string repeat A 32769]               \
     81 [string repeat A 32769]\r             \
     82 [string repeat A 32769]\n             \
     83 [string repeat A 32769]\r\n           \
     84 [string repeat ABCD 8196]             \
     85 [string repeat ABCD 8196]\r           \
     86 [string repeat ABCD 8196]\n           \
     87 [string repeat ABCD 8196]\r\n         \
     88 \x00[string repeat A 32769]           \
     89 \x00[string repeat A 32769]\r         \
     90 \x00[string repeat A 32769]\n         \
     91 \x00[string repeat A 32769]\r\n       \
     92 \x00[string repeat ABCD 8196]         \
     93 \x00[string repeat ABCD 8196]\r       \
     94 \x00[string repeat ABCD 8196]\n       \
     95 \x00[string repeat ABCD 8196]\r\n     \
     96 [string repeat A 32769]\x00           \
     97 [string repeat A 32769]\x00\r         \
     98 [string repeat A 32769]\x00\n         \
     99 [string repeat A 32769]\x00\r\n       \
    100 [string repeat ABCD 8196]\x00         \
    101 [string repeat ABCD 8196]\x00\r       \
    102 [string repeat ABCD 8196]\x00\n       \
    103 [string repeat ABCD 8196]\x00\r\n     \
    104 \x00[string repeat A 32769]\x00       \
    105 \x00[string repeat A 32769]\x00\r     \
    106 \x00[string repeat A 32769]\x00\n     \
    107 \x00[string repeat A 32769]\x00\r\n   \
    108 \x00[string repeat ABCD 8196]\x00     \
    109 \x00[string repeat ABCD 8196]\x00\r   \
    110 \x00[string repeat ABCD 8196]\x00\n   \
    111 \x00[string repeat ABCD 8196]\x00\r\n \
    112 \u000A\u000D                          \
    113 \u0A00\u0D00                          \
    114 \u000D\u000A                          \
    115 \u0D00\u0A00                          \
    116 \x00\u000A\u000D                      \
    117 \x00\u0A00\u0D00                      \
    118 \x00\u000D\u000A                      \
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
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







-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+







Has flag LOOK_CRLF: yes
Has flag LOOK_LONG: no
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 260 utf-check-260-0-80-0.jnk \
{File "%TEMP%/utf-check-260-0-80-0.jnk" has 8193 bytes.
{File "%TEMP%/utf-check-260-0-80-0.jnk" has 32769 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: no
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 261 utf-check-261-0-80-1.jnk \
{File "%TEMP%/utf-check-261-0-80-1.jnk" has 8194 bytes.
{File "%TEMP%/utf-check-261-0-80-1.jnk" has 32770 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: no
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 262 utf-check-262-0-81-0.jnk \
{File "%TEMP%/utf-check-262-0-81-0.jnk" has 8194 bytes.
{File "%TEMP%/utf-check-262-0-81-0.jnk" has 32770 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: no
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 263 utf-check-263-0-81-1.jnk \
{File "%TEMP%/utf-check-263-0-81-1.jnk" has 8195 bytes.
{File "%TEMP%/utf-check-263-0-81-1.jnk" has 32771 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: no
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 264 utf-check-264-0-82-0.jnk \
{File "%TEMP%/utf-check-264-0-82-0.jnk" has 8194 bytes.
{File "%TEMP%/utf-check-264-0-82-0.jnk" has 32770 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: no
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 265 utf-check-265-0-82-1.jnk \
{File "%TEMP%/utf-check-265-0-82-1.jnk" has 8195 bytes.
{File "%TEMP%/utf-check-265-0-82-1.jnk" has 32771 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: no
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 266 utf-check-266-0-83-0.jnk \
{File "%TEMP%/utf-check-266-0-83-0.jnk" has 8195 bytes.
{File "%TEMP%/utf-check-266-0-83-0.jnk" has 32771 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: no
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: yes
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 267 utf-check-267-0-83-1.jnk \
{File "%TEMP%/utf-check-267-0-83-1.jnk" has 8196 bytes.
{File "%TEMP%/utf-check-267-0-83-1.jnk" has 32772 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: no
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: yes
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 268 utf-check-268-0-84-0.jnk \
{File "%TEMP%/utf-check-268-0-84-0.jnk" has 8196 bytes.
{File "%TEMP%/utf-check-268-0-84-0.jnk" has 32784 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: no
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 269 utf-check-269-0-84-1.jnk \
{File "%TEMP%/utf-check-269-0-84-1.jnk" has 8197 bytes.
{File "%TEMP%/utf-check-269-0-84-1.jnk" has 32785 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: no
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 270 utf-check-270-0-85-0.jnk \
{File "%TEMP%/utf-check-270-0-85-0.jnk" has 8197 bytes.
{File "%TEMP%/utf-check-270-0-85-0.jnk" has 32785 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: no
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 271 utf-check-271-0-85-1.jnk \
{File "%TEMP%/utf-check-271-0-85-1.jnk" has 8198 bytes.
{File "%TEMP%/utf-check-271-0-85-1.jnk" has 32786 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: no
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 272 utf-check-272-0-86-0.jnk \
{File "%TEMP%/utf-check-272-0-86-0.jnk" has 8197 bytes.
{File "%TEMP%/utf-check-272-0-86-0.jnk" has 32785 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: no
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 273 utf-check-273-0-86-1.jnk \
{File "%TEMP%/utf-check-273-0-86-1.jnk" has 8198 bytes.
{File "%TEMP%/utf-check-273-0-86-1.jnk" has 32786 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: no
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 274 utf-check-274-0-87-0.jnk \
{File "%TEMP%/utf-check-274-0-87-0.jnk" has 8198 bytes.
{File "%TEMP%/utf-check-274-0-87-0.jnk" has 32786 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: no
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: yes
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 275 utf-check-275-0-87-1.jnk \
{File "%TEMP%/utf-check-275-0-87-1.jnk" has 8199 bytes.
{File "%TEMP%/utf-check-275-0-87-1.jnk" has 32787 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: no
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: yes
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 276 utf-check-276-0-88-0.jnk \
{File "%TEMP%/utf-check-276-0-88-0.jnk" has 8194 bytes.
{File "%TEMP%/utf-check-276-0-88-0.jnk" has 32770 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 277 utf-check-277-0-88-1.jnk \
{File "%TEMP%/utf-check-277-0-88-1.jnk" has 8195 bytes.
{File "%TEMP%/utf-check-277-0-88-1.jnk" has 32771 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 278 utf-check-278-0-89-0.jnk \
{File "%TEMP%/utf-check-278-0-89-0.jnk" has 8195 bytes.
{File "%TEMP%/utf-check-278-0-89-0.jnk" has 32771 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 279 utf-check-279-0-89-1.jnk \
{File "%TEMP%/utf-check-279-0-89-1.jnk" has 8196 bytes.
{File "%TEMP%/utf-check-279-0-89-1.jnk" has 32772 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 280 utf-check-280-0-90-0.jnk \
{File "%TEMP%/utf-check-280-0-90-0.jnk" has 8195 bytes.
{File "%TEMP%/utf-check-280-0-90-0.jnk" has 32771 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 281 utf-check-281-0-90-1.jnk \
{File "%TEMP%/utf-check-281-0-90-1.jnk" has 8196 bytes.
{File "%TEMP%/utf-check-281-0-90-1.jnk" has 32772 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 282 utf-check-282-0-91-0.jnk \
{File "%TEMP%/utf-check-282-0-91-0.jnk" has 8196 bytes.
{File "%TEMP%/utf-check-282-0-91-0.jnk" has 32772 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: yes
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 283 utf-check-283-0-91-1.jnk \
{File "%TEMP%/utf-check-283-0-91-1.jnk" has 8197 bytes.
{File "%TEMP%/utf-check-283-0-91-1.jnk" has 32773 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: yes
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 284 utf-check-284-0-92-0.jnk \
{File "%TEMP%/utf-check-284-0-92-0.jnk" has 8197 bytes.
{File "%TEMP%/utf-check-284-0-92-0.jnk" has 32785 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 285 utf-check-285-0-92-1.jnk \
{File "%TEMP%/utf-check-285-0-92-1.jnk" has 8198 bytes.
{File "%TEMP%/utf-check-285-0-92-1.jnk" has 32786 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 286 utf-check-286-0-93-0.jnk \
{File "%TEMP%/utf-check-286-0-93-0.jnk" has 8198 bytes.
{File "%TEMP%/utf-check-286-0-93-0.jnk" has 32786 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 287 utf-check-287-0-93-1.jnk \
{File "%TEMP%/utf-check-287-0-93-1.jnk" has 8199 bytes.
{File "%TEMP%/utf-check-287-0-93-1.jnk" has 32787 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 288 utf-check-288-0-94-0.jnk \
{File "%TEMP%/utf-check-288-0-94-0.jnk" has 8198 bytes.
{File "%TEMP%/utf-check-288-0-94-0.jnk" has 32786 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 289 utf-check-289-0-94-1.jnk \
{File "%TEMP%/utf-check-289-0-94-1.jnk" has 8199 bytes.
{File "%TEMP%/utf-check-289-0-94-1.jnk" has 32787 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 290 utf-check-290-0-95-0.jnk \
{File "%TEMP%/utf-check-290-0-95-0.jnk" has 8199 bytes.
{File "%TEMP%/utf-check-290-0-95-0.jnk" has 32787 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: yes
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 291 utf-check-291-0-95-1.jnk \
{File "%TEMP%/utf-check-291-0-95-1.jnk" has 8200 bytes.
{File "%TEMP%/utf-check-291-0-95-1.jnk" has 32788 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: yes
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 292 utf-check-292-0-96-0.jnk \
{File "%TEMP%/utf-check-292-0-96-0.jnk" has 8194 bytes.
{File "%TEMP%/utf-check-292-0-96-0.jnk" has 32770 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 293 utf-check-293-0-96-1.jnk \
{File "%TEMP%/utf-check-293-0-96-1.jnk" has 8195 bytes.
{File "%TEMP%/utf-check-293-0-96-1.jnk" has 32771 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 294 utf-check-294-0-97-0.jnk \
{File "%TEMP%/utf-check-294-0-97-0.jnk" has 8195 bytes.
{File "%TEMP%/utf-check-294-0-97-0.jnk" has 32771 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 295 utf-check-295-0-97-1.jnk \
{File "%TEMP%/utf-check-295-0-97-1.jnk" has 8196 bytes.
{File "%TEMP%/utf-check-295-0-97-1.jnk" has 32772 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 296 utf-check-296-0-98-0.jnk \
{File "%TEMP%/utf-check-296-0-98-0.jnk" has 8195 bytes.
{File "%TEMP%/utf-check-296-0-98-0.jnk" has 32771 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 297 utf-check-297-0-98-1.jnk \
{File "%TEMP%/utf-check-297-0-98-1.jnk" has 8196 bytes.
{File "%TEMP%/utf-check-297-0-98-1.jnk" has 32772 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 298 utf-check-298-0-99-0.jnk \
{File "%TEMP%/utf-check-298-0-99-0.jnk" has 8196 bytes.
{File "%TEMP%/utf-check-298-0-99-0.jnk" has 32772 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: yes
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 299 utf-check-299-0-99-1.jnk \
{File "%TEMP%/utf-check-299-0-99-1.jnk" has 8197 bytes.
{File "%TEMP%/utf-check-299-0-99-1.jnk" has 32773 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: yes
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 300 utf-check-300-0-100-0.jnk \
{File "%TEMP%/utf-check-300-0-100-0.jnk" has 8197 bytes.
{File "%TEMP%/utf-check-300-0-100-0.jnk" has 32785 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 301 utf-check-301-0-100-1.jnk \
{File "%TEMP%/utf-check-301-0-100-1.jnk" has 8198 bytes.
{File "%TEMP%/utf-check-301-0-100-1.jnk" has 32786 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 302 utf-check-302-0-101-0.jnk \
{File "%TEMP%/utf-check-302-0-101-0.jnk" has 8198 bytes.
{File "%TEMP%/utf-check-302-0-101-0.jnk" has 32786 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 303 utf-check-303-0-101-1.jnk \
{File "%TEMP%/utf-check-303-0-101-1.jnk" has 8199 bytes.
{File "%TEMP%/utf-check-303-0-101-1.jnk" has 32787 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 304 utf-check-304-0-102-0.jnk \
{File "%TEMP%/utf-check-304-0-102-0.jnk" has 8198 bytes.
{File "%TEMP%/utf-check-304-0-102-0.jnk" has 32786 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 305 utf-check-305-0-102-1.jnk \
{File "%TEMP%/utf-check-305-0-102-1.jnk" has 8199 bytes.
{File "%TEMP%/utf-check-305-0-102-1.jnk" has 32787 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 306 utf-check-306-0-103-0.jnk \
{File "%TEMP%/utf-check-306-0-103-0.jnk" has 8199 bytes.
{File "%TEMP%/utf-check-306-0-103-0.jnk" has 32787 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: yes
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 307 utf-check-307-0-103-1.jnk \
{File "%TEMP%/utf-check-307-0-103-1.jnk" has 8200 bytes.
{File "%TEMP%/utf-check-307-0-103-1.jnk" has 32788 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: yes
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 308 utf-check-308-0-104-0.jnk \
{File "%TEMP%/utf-check-308-0-104-0.jnk" has 8195 bytes.
{File "%TEMP%/utf-check-308-0-104-0.jnk" has 32771 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 309 utf-check-309-0-104-1.jnk \
{File "%TEMP%/utf-check-309-0-104-1.jnk" has 8196 bytes.
{File "%TEMP%/utf-check-309-0-104-1.jnk" has 32772 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 310 utf-check-310-0-105-0.jnk \
{File "%TEMP%/utf-check-310-0-105-0.jnk" has 8196 bytes.
{File "%TEMP%/utf-check-310-0-105-0.jnk" has 32772 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 311 utf-check-311-0-105-1.jnk \
{File "%TEMP%/utf-check-311-0-105-1.jnk" has 8197 bytes.
{File "%TEMP%/utf-check-311-0-105-1.jnk" has 32773 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 312 utf-check-312-0-106-0.jnk \
{File "%TEMP%/utf-check-312-0-106-0.jnk" has 8196 bytes.
{File "%TEMP%/utf-check-312-0-106-0.jnk" has 32772 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 313 utf-check-313-0-106-1.jnk \
{File "%TEMP%/utf-check-313-0-106-1.jnk" has 8197 bytes.
{File "%TEMP%/utf-check-313-0-106-1.jnk" has 32773 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 314 utf-check-314-0-107-0.jnk \
{File "%TEMP%/utf-check-314-0-107-0.jnk" has 8197 bytes.
{File "%TEMP%/utf-check-314-0-107-0.jnk" has 32773 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: yes
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 315 utf-check-315-0-107-1.jnk \
{File "%TEMP%/utf-check-315-0-107-1.jnk" has 8198 bytes.
{File "%TEMP%/utf-check-315-0-107-1.jnk" has 32774 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: yes
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 316 utf-check-316-0-108-0.jnk \
{File "%TEMP%/utf-check-316-0-108-0.jnk" has 8198 bytes.
{File "%TEMP%/utf-check-316-0-108-0.jnk" has 32786 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 317 utf-check-317-0-108-1.jnk \
{File "%TEMP%/utf-check-317-0-108-1.jnk" has 8199 bytes.
{File "%TEMP%/utf-check-317-0-108-1.jnk" has 32787 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 318 utf-check-318-0-109-0.jnk \
{File "%TEMP%/utf-check-318-0-109-0.jnk" has 8199 bytes.
{File "%TEMP%/utf-check-318-0-109-0.jnk" has 32787 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 319 utf-check-319-0-109-1.jnk \
{File "%TEMP%/utf-check-319-0-109-1.jnk" has 8200 bytes.
{File "%TEMP%/utf-check-319-0-109-1.jnk" has 32788 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 320 utf-check-320-0-110-0.jnk \
{File "%TEMP%/utf-check-320-0-110-0.jnk" has 8199 bytes.
{File "%TEMP%/utf-check-320-0-110-0.jnk" has 32787 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 321 utf-check-321-0-110-1.jnk \
{File "%TEMP%/utf-check-321-0-110-1.jnk" has 8200 bytes.
{File "%TEMP%/utf-check-321-0-110-1.jnk" has 32788 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 322 utf-check-322-0-111-0.jnk \
{File "%TEMP%/utf-check-322-0-111-0.jnk" has 8200 bytes.
{File "%TEMP%/utf-check-322-0-111-0.jnk" has 32788 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: yes
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 323 utf-check-323-0-111-1.jnk \
{File "%TEMP%/utf-check-323-0-111-1.jnk" has 8201 bytes.
{File "%TEMP%/utf-check-323-0-111-1.jnk" has 32789 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
9464
9465
9466
9467
9468
9469
9470
9471

9472
9473
9474
9475
9476
9477
9478
9479
9480
9481
9482
9483
9484
9485
9486
9487

9488
9489
9490
9491
9492
9493
9494
9495
9496
9497
9498
9499
9500
9501
9502
9503

9504
9505
9506
9507
9508
9509
9510
9511
9512
9513
9514
9515
9516
9517
9518
9519

9520
9521
9522
9523
9524
9525
9526
9527
9528
9529
9530
9531
9532
9533
9534
9535

9536
9537
9538
9539
9540
9541
9542
9543
9544
9545
9546
9547
9548
9549
9550
9551

9552
9553
9554
9555
9556
9557
9558
9559
9560
9561
9562
9563
9564
9565
9566
9567

9568
9569
9570
9571
9572
9573
9574
9575
9576
9577
9578
9579
9580
9581
9582
9583

9584
9585
9586
9587
9588
9589
9590
9591
9592
9593
9594
9595
9596
9597
9598
9599

9600
9601
9602
9603
9604
9605
9606
9607
9608
9609
9610
9611
9612
9613
9614
9615

9616
9617
9618
9619
9620
9621
9622
9623
9624
9625
9626
9627
9628
9629
9630
9631

9632
9633
9634
9635
9636
9637
9638
9639
9640
9641
9642
9643
9644
9645
9646
9647

9648
9649
9650
9651
9652
9653
9654
9655
9656
9657
9658
9659
9660
9661
9662
9663

9664
9665
9666
9667
9668
9669
9670
9671
9672
9673
9674
9675
9676
9677
9678
9679

9680
9681
9682
9683
9684
9685
9686
9687
9688
9689
9690
9691
9692
9693
9694
9695

9696
9697
9698
9699
9700
9701
9702
9703
9704
9705
9706
9707
9708
9709
9710
9711

9712
9713
9714
9715
9716
9717
9718
9719
9720
9721
9722
9723
9724
9725
9726
9727

9728
9729
9730
9731
9732
9733
9734
9735
9736
9737
9738
9739
9740
9741
9742
9743

9744
9745
9746
9747
9748
9749
9750
9751
9752
9753
9754
9755
9756
9757
9758
9759

9760
9761
9762
9763
9764
9765
9766
9767
9768
9769
9770
9771
9772
9773
9774
9775

9776
9777
9778
9779
9780
9781
9782
9783
9784
9785
9786
9787
9788
9789
9790
9791

9792
9793
9794
9795
9796
9797
9798
9799
9800
9801
9802
9803
9804
9805
9806
9807

9808
9809
9810
9811
9812
9813
9814
9815
9816
9817
9818
9819
9820
9821
9822
9823

9824
9825
9826
9827
9828
9829
9830
9831
9832
9833
9834
9835
9836
9837
9838
9839

9840
9841
9842
9843
9844
9845
9846
9847
9848
9849
9850
9851
9852
9853
9854
9855

9856
9857
9858
9859
9860
9861
9862
9863
9864
9865
9866
9867
9868
9869
9870
9871

9872
9873
9874
9875
9876
9877
9878
9879
9880
9881
9882
9883
9884
9885
9886
9887

9888
9889
9890
9891
9892
9893
9894
9895
9896
9897
9898
9899
9900
9901
9902
9903

9904
9905
9906
9907
9908
9909
9910
9911
9912
9913
9914
9915
9916
9917
9918
9919

9920
9921
9922
9923
9924
9925
9926
9927
9928
9929
9930
9931
9932
9933
9934
9935

9936
9937
9938
9939
9940
9941
9942
9943
9944
9945
9946
9947
9948
9949
9950
9951

9952
9953
9954
9955
9956
9957
9958
9959
9960
9961
9962
9963
9964
9965
9966
9967

9968
9969
9970
9971
9972
9973
9974
9975
9976
9977
9978
9979
9980
9981
9982
9983

9984
9985
9986
9987
9988
9989
9990
9991
9992
9993
9994
9995
9996
9997
9998
9999

10000
10001
10002
10003
10004
10005
10006
10007
10008
10009
10010
10011
10012
10013
10014
10015

10016
10017
10018
10019
10020
10021
10022
10023
10024
10025
10026
10027
10028
10029
10030
10031

10032
10033
10034
10035
10036
10037
10038
10039
10040
10041
10042
10043
10044
10045
10046
10047

10048
10049
10050
10051
10052
10053
10054
10055
10056
10057
10058
10059
10060
10061
10062
10063

10064
10065
10066
10067
10068
10069
10070
10071
10072
10073
10074
10075
10076
10077
10078
10079

10080
10081
10082
10083
10084
10085
10086
10087
10088
10089
10090
10091
10092
10093
10094
10095

10096
10097
10098
10099
10100
10101
10102
10103
10104
10105
10106
10107
10108
10109
10110
10111

10112
10113
10114
10115
10116
10117
10118
10119
10120
10121
10122
10123
10124
10125
10126
10127

10128
10129
10130
10131
10132
10133
10134
10135
10136
10137
10138
10139
10140
10141
10142
10143

10144
10145
10146
10147
10148
10149
10150
10151
10152
10153
10154
10155
10156
10157
10158
10159

10160
10161
10162
10163
10164
10165
10166
10167
10168
10169
10170
10171
10172
10173
10174
10175

10176
10177
10178
10179
10180
10181
10182
10183
10184
10185
10186
10187
10188
10189
10190
10191

10192
10193
10194
10195
10196
10197
10198
10199
10200
10201
10202
10203
10204
10205
10206
10207

10208
10209
10210
10211
10212
10213
10214
10215
10216
10217
10218
10219
10220
10221
10222
10223

10224
10225
10226
10227
10228
10229
10230
10231
10232
10233
10234
10235
10236
10237
10238
10239

10240
10241
10242
10243
10244
10245
10246
10247
10248
10249
10250
10251
10252
10253
10254
10255

10256
10257
10258
10259
10260
10261
10262
10263
10264
10265
10266
10267
10268
10269
10270
10271

10272
10273
10274
10275
10276
10277
10278
10279
10280
10281
10282
10283
10284
10285
10286
10287

10288
10289
10290
10291
10292
10293
10294
10295
10296
10297
10298
10299
10300
10301
10302
10303

10304
10305
10306
10307
10308
10309
10310
10311
10312
10313
10314
10315
10316
10317
10318
10319

10320
10321
10322
10323
10324
10325
10326
10327
10328
10329
10330
10331
10332
10333
10334
10335

10336
10337
10338
10339
10340
10341
10342
10343
10344
10345
10346
10347
10348
10349
10350
10351

10352
10353
10354
10355
10356
10357
10358
10359
10360
10361
10362
10363
10364
10365
10366
10367

10368
10369
10370
10371
10372
10373
10374
10375
10376
10377
10378
10379
10380
10381
10382
10383

10384
10385
10386
10387
10388
10389
10390
10391
10392
10393
10394
10395
10396
10397
10398
10399

10400
10401
10402
10403
10404
10405
10406
10407
10408
10409
10410
10411
10412
10413
10414
10415

10416
10417
10418
10419
10420
10421
10422
10423
10424
10425
10426
10427
10428
10429
10430
10431

10432
10433
10434
10435
10436
10437
10438
10439
10440
10441
10442
10443
10444
10445
10446
10447

10448
10449
10450
10451
10452
10453
10454
10455
10456
10457
10458
10459
10460
10461
10462
10463

10464
10465
10466
10467
10468
10469
10470
10471
10472
10473
10474
10475
10476
10477
10478
10479

10480
10481
10482
10483
10484
10485
10486
9464
9465
9466
9467
9468
9469
9470

9471
9472
9473
9474
9475
9476
9477
9478
9479
9480
9481
9482
9483
9484
9485
9486

9487
9488
9489
9490
9491
9492
9493
9494
9495
9496
9497
9498
9499
9500
9501
9502

9503
9504
9505
9506
9507
9508
9509
9510
9511
9512
9513
9514
9515
9516
9517
9518

9519
9520
9521
9522
9523
9524
9525
9526
9527
9528
9529
9530
9531
9532
9533
9534

9535
9536
9537
9538
9539
9540
9541
9542
9543
9544
9545
9546
9547
9548
9549
9550

9551
9552
9553
9554
9555
9556
9557
9558
9559
9560
9561
9562
9563
9564
9565
9566

9567
9568
9569
9570
9571
9572
9573
9574
9575
9576
9577
9578
9579
9580
9581
9582

9583
9584
9585
9586
9587
9588
9589
9590
9591
9592
9593
9594
9595
9596
9597
9598

9599
9600
9601
9602
9603
9604
9605
9606
9607
9608
9609
9610
9611
9612
9613
9614

9615
9616
9617
9618
9619
9620
9621
9622
9623
9624
9625
9626
9627
9628
9629
9630

9631
9632
9633
9634
9635
9636
9637
9638
9639
9640
9641
9642
9643
9644
9645
9646

9647
9648
9649
9650
9651
9652
9653
9654
9655
9656
9657
9658
9659
9660
9661
9662

9663
9664
9665
9666
9667
9668
9669
9670
9671
9672
9673
9674
9675
9676
9677
9678

9679
9680
9681
9682
9683
9684
9685
9686
9687
9688
9689
9690
9691
9692
9693
9694

9695
9696
9697
9698
9699
9700
9701
9702
9703
9704
9705
9706
9707
9708
9709
9710

9711
9712
9713
9714
9715
9716
9717
9718
9719
9720
9721
9722
9723
9724
9725
9726

9727
9728
9729
9730
9731
9732
9733
9734
9735
9736
9737
9738
9739
9740
9741
9742

9743
9744
9745
9746
9747
9748
9749
9750
9751
9752
9753
9754
9755
9756
9757
9758

9759
9760
9761
9762
9763
9764
9765
9766
9767
9768
9769
9770
9771
9772
9773
9774

9775
9776
9777
9778
9779
9780
9781
9782
9783
9784
9785
9786
9787
9788
9789
9790

9791
9792
9793
9794
9795
9796
9797
9798
9799
9800
9801
9802
9803
9804
9805
9806

9807
9808
9809
9810
9811
9812
9813
9814
9815
9816
9817
9818
9819
9820
9821
9822

9823
9824
9825
9826
9827
9828
9829
9830
9831
9832
9833
9834
9835
9836
9837
9838

9839
9840
9841
9842
9843
9844
9845
9846
9847
9848
9849
9850
9851
9852
9853
9854

9855
9856
9857
9858
9859
9860
9861
9862
9863
9864
9865
9866
9867
9868
9869
9870

9871
9872
9873
9874
9875
9876
9877
9878
9879
9880
9881
9882
9883
9884
9885
9886

9887
9888
9889
9890
9891
9892
9893
9894
9895
9896
9897
9898
9899
9900
9901
9902

9903
9904
9905
9906
9907
9908
9909
9910
9911
9912
9913
9914
9915
9916
9917
9918

9919
9920
9921
9922
9923
9924
9925
9926
9927
9928
9929
9930
9931
9932
9933
9934

9935
9936
9937
9938
9939
9940
9941
9942
9943
9944
9945
9946
9947
9948
9949
9950

9951
9952
9953
9954
9955
9956
9957
9958
9959
9960
9961
9962
9963
9964
9965
9966

9967
9968
9969
9970
9971
9972
9973
9974
9975
9976
9977
9978
9979
9980
9981
9982

9983
9984
9985
9986
9987
9988
9989
9990
9991
9992
9993
9994
9995
9996
9997
9998

9999
10000
10001
10002
10003
10004
10005
10006
10007
10008
10009
10010
10011
10012
10013
10014

10015
10016
10017
10018
10019
10020
10021
10022
10023
10024
10025
10026
10027
10028
10029
10030

10031
10032
10033
10034
10035
10036
10037
10038
10039
10040
10041
10042
10043
10044
10045
10046

10047
10048
10049
10050
10051
10052
10053
10054
10055
10056
10057
10058
10059
10060
10061
10062

10063
10064
10065
10066
10067
10068
10069
10070
10071
10072
10073
10074
10075
10076
10077
10078

10079
10080
10081
10082
10083
10084
10085
10086
10087
10088
10089
10090
10091
10092
10093
10094

10095
10096
10097
10098
10099
10100
10101
10102
10103
10104
10105
10106
10107
10108
10109
10110

10111
10112
10113
10114
10115
10116
10117
10118
10119
10120
10121
10122
10123
10124
10125
10126

10127
10128
10129
10130
10131
10132
10133
10134
10135
10136
10137
10138
10139
10140
10141
10142

10143
10144
10145
10146
10147
10148
10149
10150
10151
10152
10153
10154
10155
10156
10157
10158

10159
10160
10161
10162
10163
10164
10165
10166
10167
10168
10169
10170
10171
10172
10173
10174

10175
10176
10177
10178
10179
10180
10181
10182
10183
10184
10185
10186
10187
10188
10189
10190

10191
10192
10193
10194
10195
10196
10197
10198
10199
10200
10201
10202
10203
10204
10205
10206

10207
10208
10209
10210
10211
10212
10213
10214
10215
10216
10217
10218
10219
10220
10221
10222

10223
10224
10225
10226
10227
10228
10229
10230
10231
10232
10233
10234
10235
10236
10237
10238

10239
10240
10241
10242
10243
10244
10245
10246
10247
10248
10249
10250
10251
10252
10253
10254

10255
10256
10257
10258
10259
10260
10261
10262
10263
10264
10265
10266
10267
10268
10269
10270

10271
10272
10273
10274
10275
10276
10277
10278
10279
10280
10281
10282
10283
10284
10285
10286

10287
10288
10289
10290
10291
10292
10293
10294
10295
10296
10297
10298
10299
10300
10301
10302

10303
10304
10305
10306
10307
10308
10309
10310
10311
10312
10313
10314
10315
10316
10317
10318

10319
10320
10321
10322
10323
10324
10325
10326
10327
10328
10329
10330
10331
10332
10333
10334

10335
10336
10337
10338
10339
10340
10341
10342
10343
10344
10345
10346
10347
10348
10349
10350

10351
10352
10353
10354
10355
10356
10357
10358
10359
10360
10361
10362
10363
10364
10365
10366

10367
10368
10369
10370
10371
10372
10373
10374
10375
10376
10377
10378
10379
10380
10381
10382

10383
10384
10385
10386
10387
10388
10389
10390
10391
10392
10393
10394
10395
10396
10397
10398

10399
10400
10401
10402
10403
10404
10405
10406
10407
10408
10409
10410
10411
10412
10413
10414

10415
10416
10417
10418
10419
10420
10421
10422
10423
10424
10425
10426
10427
10428
10429
10430

10431
10432
10433
10434
10435
10436
10437
10438
10439
10440
10441
10442
10443
10444
10445
10446

10447
10448
10449
10450
10451
10452
10453
10454
10455
10456
10457
10458
10459
10460
10461
10462

10463
10464
10465
10466
10467
10468
10469
10470
10471
10472
10473
10474
10475
10476
10477
10478

10479
10480
10481
10482
10483
10484
10485
10486







-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+







Has flag LOOK_CRLF: yes
Has flag LOOK_LONG: no
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 670 utf-check-670-1-80-0.jnk \
{File "%TEMP%/utf-check-670-1-80-0.jnk" has 8196 bytes.
{File "%TEMP%/utf-check-670-1-80-0.jnk" has 32772 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: no
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 671 utf-check-671-1-80-1.jnk \
{File "%TEMP%/utf-check-671-1-80-1.jnk" has 8197 bytes.
{File "%TEMP%/utf-check-671-1-80-1.jnk" has 32773 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: no
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 672 utf-check-672-1-81-0.jnk \
{File "%TEMP%/utf-check-672-1-81-0.jnk" has 8197 bytes.
{File "%TEMP%/utf-check-672-1-81-0.jnk" has 32773 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: no
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 673 utf-check-673-1-81-1.jnk \
{File "%TEMP%/utf-check-673-1-81-1.jnk" has 8198 bytes.
{File "%TEMP%/utf-check-673-1-81-1.jnk" has 32774 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: no
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 674 utf-check-674-1-82-0.jnk \
{File "%TEMP%/utf-check-674-1-82-0.jnk" has 8197 bytes.
{File "%TEMP%/utf-check-674-1-82-0.jnk" has 32773 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: no
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 675 utf-check-675-1-82-1.jnk \
{File "%TEMP%/utf-check-675-1-82-1.jnk" has 8198 bytes.
{File "%TEMP%/utf-check-675-1-82-1.jnk" has 32774 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: no
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 676 utf-check-676-1-83-0.jnk \
{File "%TEMP%/utf-check-676-1-83-0.jnk" has 8198 bytes.
{File "%TEMP%/utf-check-676-1-83-0.jnk" has 32774 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: no
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: yes
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 677 utf-check-677-1-83-1.jnk \
{File "%TEMP%/utf-check-677-1-83-1.jnk" has 8199 bytes.
{File "%TEMP%/utf-check-677-1-83-1.jnk" has 32775 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: no
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: yes
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 678 utf-check-678-1-84-0.jnk \
{File "%TEMP%/utf-check-678-1-84-0.jnk" has 8199 bytes.
{File "%TEMP%/utf-check-678-1-84-0.jnk" has 32787 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: no
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 679 utf-check-679-1-84-1.jnk \
{File "%TEMP%/utf-check-679-1-84-1.jnk" has 8200 bytes.
{File "%TEMP%/utf-check-679-1-84-1.jnk" has 32788 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: no
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 680 utf-check-680-1-85-0.jnk \
{File "%TEMP%/utf-check-680-1-85-0.jnk" has 8200 bytes.
{File "%TEMP%/utf-check-680-1-85-0.jnk" has 32788 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: no
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 681 utf-check-681-1-85-1.jnk \
{File "%TEMP%/utf-check-681-1-85-1.jnk" has 8201 bytes.
{File "%TEMP%/utf-check-681-1-85-1.jnk" has 32789 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: no
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 682 utf-check-682-1-86-0.jnk \
{File "%TEMP%/utf-check-682-1-86-0.jnk" has 8200 bytes.
{File "%TEMP%/utf-check-682-1-86-0.jnk" has 32788 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: no
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 683 utf-check-683-1-86-1.jnk \
{File "%TEMP%/utf-check-683-1-86-1.jnk" has 8201 bytes.
{File "%TEMP%/utf-check-683-1-86-1.jnk" has 32789 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: no
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 684 utf-check-684-1-87-0.jnk \
{File "%TEMP%/utf-check-684-1-87-0.jnk" has 8201 bytes.
{File "%TEMP%/utf-check-684-1-87-0.jnk" has 32789 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: no
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: yes
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 685 utf-check-685-1-87-1.jnk \
{File "%TEMP%/utf-check-685-1-87-1.jnk" has 8202 bytes.
{File "%TEMP%/utf-check-685-1-87-1.jnk" has 32790 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: no
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: yes
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 686 utf-check-686-1-88-0.jnk \
{File "%TEMP%/utf-check-686-1-88-0.jnk" has 8197 bytes.
{File "%TEMP%/utf-check-686-1-88-0.jnk" has 32773 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 687 utf-check-687-1-88-1.jnk \
{File "%TEMP%/utf-check-687-1-88-1.jnk" has 8198 bytes.
{File "%TEMP%/utf-check-687-1-88-1.jnk" has 32774 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 688 utf-check-688-1-89-0.jnk \
{File "%TEMP%/utf-check-688-1-89-0.jnk" has 8198 bytes.
{File "%TEMP%/utf-check-688-1-89-0.jnk" has 32774 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 689 utf-check-689-1-89-1.jnk \
{File "%TEMP%/utf-check-689-1-89-1.jnk" has 8199 bytes.
{File "%TEMP%/utf-check-689-1-89-1.jnk" has 32775 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 690 utf-check-690-1-90-0.jnk \
{File "%TEMP%/utf-check-690-1-90-0.jnk" has 8198 bytes.
{File "%TEMP%/utf-check-690-1-90-0.jnk" has 32774 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 691 utf-check-691-1-90-1.jnk \
{File "%TEMP%/utf-check-691-1-90-1.jnk" has 8199 bytes.
{File "%TEMP%/utf-check-691-1-90-1.jnk" has 32775 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 692 utf-check-692-1-91-0.jnk \
{File "%TEMP%/utf-check-692-1-91-0.jnk" has 8199 bytes.
{File "%TEMP%/utf-check-692-1-91-0.jnk" has 32775 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: yes
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 693 utf-check-693-1-91-1.jnk \
{File "%TEMP%/utf-check-693-1-91-1.jnk" has 8200 bytes.
{File "%TEMP%/utf-check-693-1-91-1.jnk" has 32776 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: yes
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 694 utf-check-694-1-92-0.jnk \
{File "%TEMP%/utf-check-694-1-92-0.jnk" has 8200 bytes.
{File "%TEMP%/utf-check-694-1-92-0.jnk" has 32788 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 695 utf-check-695-1-92-1.jnk \
{File "%TEMP%/utf-check-695-1-92-1.jnk" has 8201 bytes.
{File "%TEMP%/utf-check-695-1-92-1.jnk" has 32789 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 696 utf-check-696-1-93-0.jnk \
{File "%TEMP%/utf-check-696-1-93-0.jnk" has 8201 bytes.
{File "%TEMP%/utf-check-696-1-93-0.jnk" has 32789 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 697 utf-check-697-1-93-1.jnk \
{File "%TEMP%/utf-check-697-1-93-1.jnk" has 8202 bytes.
{File "%TEMP%/utf-check-697-1-93-1.jnk" has 32790 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 698 utf-check-698-1-94-0.jnk \
{File "%TEMP%/utf-check-698-1-94-0.jnk" has 8201 bytes.
{File "%TEMP%/utf-check-698-1-94-0.jnk" has 32789 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 699 utf-check-699-1-94-1.jnk \
{File "%TEMP%/utf-check-699-1-94-1.jnk" has 8202 bytes.
{File "%TEMP%/utf-check-699-1-94-1.jnk" has 32790 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 700 utf-check-700-1-95-0.jnk \
{File "%TEMP%/utf-check-700-1-95-0.jnk" has 8202 bytes.
{File "%TEMP%/utf-check-700-1-95-0.jnk" has 32790 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: yes
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 701 utf-check-701-1-95-1.jnk \
{File "%TEMP%/utf-check-701-1-95-1.jnk" has 8203 bytes.
{File "%TEMP%/utf-check-701-1-95-1.jnk" has 32791 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: yes
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 702 utf-check-702-1-96-0.jnk \
{File "%TEMP%/utf-check-702-1-96-0.jnk" has 8197 bytes.
{File "%TEMP%/utf-check-702-1-96-0.jnk" has 32773 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 703 utf-check-703-1-96-1.jnk \
{File "%TEMP%/utf-check-703-1-96-1.jnk" has 8198 bytes.
{File "%TEMP%/utf-check-703-1-96-1.jnk" has 32774 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 704 utf-check-704-1-97-0.jnk \
{File "%TEMP%/utf-check-704-1-97-0.jnk" has 8198 bytes.
{File "%TEMP%/utf-check-704-1-97-0.jnk" has 32774 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 705 utf-check-705-1-97-1.jnk \
{File "%TEMP%/utf-check-705-1-97-1.jnk" has 8199 bytes.
{File "%TEMP%/utf-check-705-1-97-1.jnk" has 32775 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 706 utf-check-706-1-98-0.jnk \
{File "%TEMP%/utf-check-706-1-98-0.jnk" has 8198 bytes.
{File "%TEMP%/utf-check-706-1-98-0.jnk" has 32774 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 707 utf-check-707-1-98-1.jnk \
{File "%TEMP%/utf-check-707-1-98-1.jnk" has 8199 bytes.
{File "%TEMP%/utf-check-707-1-98-1.jnk" has 32775 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 708 utf-check-708-1-99-0.jnk \
{File "%TEMP%/utf-check-708-1-99-0.jnk" has 8199 bytes.
{File "%TEMP%/utf-check-708-1-99-0.jnk" has 32775 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: yes
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 709 utf-check-709-1-99-1.jnk \
{File "%TEMP%/utf-check-709-1-99-1.jnk" has 8200 bytes.
{File "%TEMP%/utf-check-709-1-99-1.jnk" has 32776 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: yes
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 710 utf-check-710-1-100-0.jnk \
{File "%TEMP%/utf-check-710-1-100-0.jnk" has 8200 bytes.
{File "%TEMP%/utf-check-710-1-100-0.jnk" has 32788 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 711 utf-check-711-1-100-1.jnk \
{File "%TEMP%/utf-check-711-1-100-1.jnk" has 8201 bytes.
{File "%TEMP%/utf-check-711-1-100-1.jnk" has 32789 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 712 utf-check-712-1-101-0.jnk \
{File "%TEMP%/utf-check-712-1-101-0.jnk" has 8201 bytes.
{File "%TEMP%/utf-check-712-1-101-0.jnk" has 32789 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 713 utf-check-713-1-101-1.jnk \
{File "%TEMP%/utf-check-713-1-101-1.jnk" has 8202 bytes.
{File "%TEMP%/utf-check-713-1-101-1.jnk" has 32790 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 714 utf-check-714-1-102-0.jnk \
{File "%TEMP%/utf-check-714-1-102-0.jnk" has 8201 bytes.
{File "%TEMP%/utf-check-714-1-102-0.jnk" has 32789 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 715 utf-check-715-1-102-1.jnk \
{File "%TEMP%/utf-check-715-1-102-1.jnk" has 8202 bytes.
{File "%TEMP%/utf-check-715-1-102-1.jnk" has 32790 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 716 utf-check-716-1-103-0.jnk \
{File "%TEMP%/utf-check-716-1-103-0.jnk" has 8202 bytes.
{File "%TEMP%/utf-check-716-1-103-0.jnk" has 32790 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: yes
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 717 utf-check-717-1-103-1.jnk \
{File "%TEMP%/utf-check-717-1-103-1.jnk" has 8203 bytes.
{File "%TEMP%/utf-check-717-1-103-1.jnk" has 32791 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: yes
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 718 utf-check-718-1-104-0.jnk \
{File "%TEMP%/utf-check-718-1-104-0.jnk" has 8198 bytes.
{File "%TEMP%/utf-check-718-1-104-0.jnk" has 32774 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 719 utf-check-719-1-104-1.jnk \
{File "%TEMP%/utf-check-719-1-104-1.jnk" has 8199 bytes.
{File "%TEMP%/utf-check-719-1-104-1.jnk" has 32775 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 720 utf-check-720-1-105-0.jnk \
{File "%TEMP%/utf-check-720-1-105-0.jnk" has 8199 bytes.
{File "%TEMP%/utf-check-720-1-105-0.jnk" has 32775 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 721 utf-check-721-1-105-1.jnk \
{File "%TEMP%/utf-check-721-1-105-1.jnk" has 8200 bytes.
{File "%TEMP%/utf-check-721-1-105-1.jnk" has 32776 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 722 utf-check-722-1-106-0.jnk \
{File "%TEMP%/utf-check-722-1-106-0.jnk" has 8199 bytes.
{File "%TEMP%/utf-check-722-1-106-0.jnk" has 32775 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 723 utf-check-723-1-106-1.jnk \
{File "%TEMP%/utf-check-723-1-106-1.jnk" has 8200 bytes.
{File "%TEMP%/utf-check-723-1-106-1.jnk" has 32776 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 724 utf-check-724-1-107-0.jnk \
{File "%TEMP%/utf-check-724-1-107-0.jnk" has 8200 bytes.
{File "%TEMP%/utf-check-724-1-107-0.jnk" has 32776 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: yes
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 725 utf-check-725-1-107-1.jnk \
{File "%TEMP%/utf-check-725-1-107-1.jnk" has 8201 bytes.
{File "%TEMP%/utf-check-725-1-107-1.jnk" has 32777 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: yes
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 726 utf-check-726-1-108-0.jnk \
{File "%TEMP%/utf-check-726-1-108-0.jnk" has 8201 bytes.
{File "%TEMP%/utf-check-726-1-108-0.jnk" has 32789 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 727 utf-check-727-1-108-1.jnk \
{File "%TEMP%/utf-check-727-1-108-1.jnk" has 8202 bytes.
{File "%TEMP%/utf-check-727-1-108-1.jnk" has 32790 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 728 utf-check-728-1-109-0.jnk \
{File "%TEMP%/utf-check-728-1-109-0.jnk" has 8202 bytes.
{File "%TEMP%/utf-check-728-1-109-0.jnk" has 32790 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 729 utf-check-729-1-109-1.jnk \
{File "%TEMP%/utf-check-729-1-109-1.jnk" has 8203 bytes.
{File "%TEMP%/utf-check-729-1-109-1.jnk" has 32791 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 730 utf-check-730-1-110-0.jnk \
{File "%TEMP%/utf-check-730-1-110-0.jnk" has 8202 bytes.
{File "%TEMP%/utf-check-730-1-110-0.jnk" has 32790 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 731 utf-check-731-1-110-1.jnk \
{File "%TEMP%/utf-check-731-1-110-1.jnk" has 8203 bytes.
{File "%TEMP%/utf-check-731-1-110-1.jnk" has 32791 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 732 utf-check-732-1-111-0.jnk \
{File "%TEMP%/utf-check-732-1-111-0.jnk" has 8203 bytes.
{File "%TEMP%/utf-check-732-1-111-0.jnk" has 32791 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: yes
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 733 utf-check-733-1-111-1.jnk \
{File "%TEMP%/utf-check-733-1-111-1.jnk" has 8204 bytes.
{File "%TEMP%/utf-check-733-1-111-1.jnk" has 32792 bytes.
Starts with UTF-8 BOM: yes
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
16024
16025
16026
16027
16028
16029
16030
16031

16032
16033
16034
16035
16036
16037
16038
16039
16040
16041
16042
16043
16044
16045
16046
16047

16048
16049
16050
16051
16052
16053
16054
16055
16056
16057
16058
16059
16060
16061
16062
16063

16064
16065
16066
16067
16068
16069
16070
16071
16072
16073
16074
16075
16076
16077
16078
16079

16080
16081
16082
16083
16084
16085
16086
16087
16088
16089
16090
16091
16092
16093
16094
16095

16096
16097
16098
16099
16100
16101
16102
16103
16104
16105
16106
16107
16108
16109
16110
16111

16112
16113
16114
16115
16116
16117
16118
16119
16120
16121
16122
16123
16124
16125
16126
16127

16128
16129
16130
16131
16132
16133
16134
16135
16136
16137
16138
16139
16140
16141
16142
16143

16144
16145
16146
16147
16148
16149
16150
16151
16152
16153
16154
16155
16156
16157
16158
16159

16160
16161
16162
16163
16164
16165
16166
16167
16168
16169
16170
16171
16172
16173
16174
16175

16176
16177
16178
16179
16180
16181
16182
16183
16184
16185
16186
16187
16188
16189
16190
16191

16192
16193
16194
16195
16196
16197
16198
16199
16200
16201
16202
16203
16204
16205
16206
16207

16208
16209
16210
16211
16212
16213
16214
16215
16216
16217
16218
16219
16220
16221
16222
16223

16224
16225
16226
16227
16228
16229
16230
16231
16232
16233
16234
16235
16236
16237
16238
16239

16240
16241
16242
16243
16244
16245
16246
16247
16248
16249
16250
16251
16252
16253
16254
16255

16256
16257
16258
16259
16260
16261
16262
16263
16264
16265
16266
16267
16268
16269
16270
16271

16272
16273
16274
16275
16276
16277
16278
16279
16280
16281
16282
16283
16284
16285
16286
16287

16288
16289
16290
16291
16292
16293
16294
16295
16296
16297
16298
16299
16300
16301
16302
16303

16304
16305
16306
16307
16308
16309
16310
16311
16312
16313
16314
16315
16316
16317
16318
16319

16320
16321
16322
16323
16324
16325
16326
16327
16328
16329
16330
16331
16332
16333
16334
16335

16336
16337
16338
16339
16340
16341
16342
16343
16344
16345
16346
16347
16348
16349
16350
16351

16352
16353
16354
16355
16356
16357
16358
16359
16360
16361
16362
16363
16364
16365
16366
16367

16368
16369
16370
16371
16372
16373
16374
16375
16376
16377
16378
16379
16380
16381
16382
16383

16384
16385
16386
16387
16388
16389
16390
16391
16392
16393
16394
16395
16396
16397
16398
16399

16400
16401
16402
16403
16404
16405
16406
16407
16408
16409
16410
16411
16412
16413
16414
16415

16416
16417
16418
16419
16420
16421
16422
16423
16424
16425
16426
16427
16428
16429
16430
16431

16432
16433
16434
16435
16436
16437
16438
16439
16440
16441
16442
16443
16444
16445
16446
16447

16448
16449
16450
16451
16452
16453
16454
16455
16456
16457
16458
16459
16460
16461
16462
16463

16464
16465
16466
16467
16468
16469
16470
16471
16472
16473
16474
16475
16476
16477
16478
16479

16480
16481
16482
16483
16484
16485
16486
16487
16488
16489
16490
16491
16492
16493
16494
16495

16496
16497
16498
16499
16500
16501
16502
16503
16504
16505
16506
16507
16508
16509
16510
16511

16512
16513
16514
16515
16516
16517
16518
16519
16520
16521
16522
16523
16524
16525
16526
16527

16528
16529
16530
16531
16532
16533
16534
16535
16536
16537
16538
16539
16540
16541
16542
16543

16544
16545
16546
16547
16548
16549
16550
16551
16552
16553
16554
16555
16556
16557
16558
16559

16560
16561
16562
16563
16564
16565
16566
16567
16568
16569
16570
16571
16572
16573
16574
16575

16576
16577
16578
16579
16580
16581
16582
16583
16584
16585
16586
16587
16588
16589
16590
16591

16592
16593
16594
16595
16596
16597
16598
16599
16600
16601
16602
16603
16604
16605
16606
16607

16608
16609
16610
16611
16612
16613
16614
16615
16616
16617
16618
16619
16620
16621
16622
16623

16624
16625
16626
16627
16628
16629
16630
16631
16632
16633
16634
16635
16636
16637
16638
16639

16640
16641
16642
16643
16644
16645
16646
16647
16648
16649
16650
16651
16652
16653
16654
16655

16656
16657
16658
16659
16660
16661
16662
16663
16664
16665
16666
16667
16668
16669
16670
16671

16672
16673
16674
16675
16676
16677
16678
16679
16680
16681
16682
16683
16684
16685
16686
16687

16688
16689
16690
16691
16692
16693
16694
16695
16696
16697
16698
16699
16700
16701
16702
16703

16704
16705
16706
16707
16708
16709
16710
16711
16712
16713
16714
16715
16716
16717
16718
16719

16720
16721
16722
16723
16724
16725
16726
16727
16728
16729
16730
16731
16732
16733
16734
16735

16736
16737
16738
16739
16740
16741
16742
16743
16744
16745
16746
16747
16748
16749
16750
16751

16752
16753
16754
16755
16756
16757
16758
16759
16760
16761
16762
16763
16764
16765
16766
16767

16768
16769
16770
16771
16772
16773
16774
16775
16776
16777
16778
16779
16780
16781
16782
16783

16784
16785
16786
16787
16788
16789
16790
16791
16792
16793
16794
16795
16796
16797
16798
16799

16800
16801
16802
16803
16804
16805
16806
16807
16808
16809
16810
16811
16812
16813
16814
16815

16816
16817
16818
16819
16820
16821
16822
16823
16824
16825
16826
16827
16828
16829
16830
16831

16832
16833
16834
16835
16836
16837
16838
16839
16840
16841
16842
16843
16844
16845
16846
16847

16848
16849
16850
16851
16852
16853
16854
16855
16856
16857
16858
16859
16860
16861
16862
16863

16864
16865
16866
16867
16868
16869
16870
16871
16872
16873
16874
16875
16876
16877
16878
16879

16880
16881
16882
16883
16884
16885
16886
16887
16888
16889
16890
16891
16892
16893
16894
16895

16896
16897
16898
16899
16900
16901
16902
16903
16904
16905
16906
16907
16908
16909
16910
16911

16912
16913
16914
16915
16916
16917
16918
16919
16920
16921
16922
16923
16924
16925
16926
16927

16928
16929
16930
16931
16932
16933
16934
16935
16936
16937
16938
16939
16940
16941
16942
16943

16944
16945
16946
16947
16948
16949
16950
16951
16952
16953
16954
16955
16956
16957
16958
16959

16960
16961
16962
16963
16964
16965
16966
16967
16968
16969
16970
16971
16972
16973
16974
16975

16976
16977
16978
16979
16980
16981
16982
16983
16984
16985
16986
16987
16988
16989
16990
16991

16992
16993
16994
16995
16996
16997
16998
16999
17000
17001
17002
17003
17004
17005
17006
17007

17008
17009
17010
17011
17012
17013
17014
17015
17016
17017
17018
17019
17020
17021
17022
17023

17024
17025
17026
17027
17028
17029
17030
17031
17032
17033
17034
17035
17036
17037
17038
17039

17040
17041
17042
17043
17044
17045
17046
16024
16025
16026
16027
16028
16029
16030

16031
16032
16033
16034
16035
16036
16037
16038
16039
16040
16041
16042
16043
16044
16045
16046

16047
16048
16049
16050
16051
16052
16053
16054
16055
16056
16057
16058
16059
16060
16061
16062

16063
16064
16065
16066
16067
16068
16069
16070
16071
16072
16073
16074
16075
16076
16077
16078

16079
16080
16081
16082
16083
16084
16085
16086
16087
16088
16089
16090
16091
16092
16093
16094

16095
16096
16097
16098
16099
16100
16101
16102
16103
16104
16105
16106
16107
16108
16109
16110

16111
16112
16113
16114
16115
16116
16117
16118
16119
16120
16121
16122
16123
16124
16125
16126

16127
16128
16129
16130
16131
16132
16133
16134
16135
16136
16137
16138
16139
16140
16141
16142

16143
16144
16145
16146
16147
16148
16149
16150
16151
16152
16153
16154
16155
16156
16157
16158

16159
16160
16161
16162
16163
16164
16165
16166
16167
16168
16169
16170
16171
16172
16173
16174

16175
16176
16177
16178
16179
16180
16181
16182
16183
16184
16185
16186
16187
16188
16189
16190

16191
16192
16193
16194
16195
16196
16197
16198
16199
16200
16201
16202
16203
16204
16205
16206

16207
16208
16209
16210
16211
16212
16213
16214
16215
16216
16217
16218
16219
16220
16221
16222

16223
16224
16225
16226
16227
16228
16229
16230
16231
16232
16233
16234
16235
16236
16237
16238

16239
16240
16241
16242
16243
16244
16245
16246
16247
16248
16249
16250
16251
16252
16253
16254

16255
16256
16257
16258
16259
16260
16261
16262
16263
16264
16265
16266
16267
16268
16269
16270

16271
16272
16273
16274
16275
16276
16277
16278
16279
16280
16281
16282
16283
16284
16285
16286

16287
16288
16289
16290
16291
16292
16293
16294
16295
16296
16297
16298
16299
16300
16301
16302

16303
16304
16305
16306
16307
16308
16309
16310
16311
16312
16313
16314
16315
16316
16317
16318

16319
16320
16321
16322
16323
16324
16325
16326
16327
16328
16329
16330
16331
16332
16333
16334

16335
16336
16337
16338
16339
16340
16341
16342
16343
16344
16345
16346
16347
16348
16349
16350

16351
16352
16353
16354
16355
16356
16357
16358
16359
16360
16361
16362
16363
16364
16365
16366

16367
16368
16369
16370
16371
16372
16373
16374
16375
16376
16377
16378
16379
16380
16381
16382

16383
16384
16385
16386
16387
16388
16389
16390
16391
16392
16393
16394
16395
16396
16397
16398

16399
16400
16401
16402
16403
16404
16405
16406
16407
16408
16409
16410
16411
16412
16413
16414

16415
16416
16417
16418
16419
16420
16421
16422
16423
16424
16425
16426
16427
16428
16429
16430

16431
16432
16433
16434
16435
16436
16437
16438
16439
16440
16441
16442
16443
16444
16445
16446

16447
16448
16449
16450
16451
16452
16453
16454
16455
16456
16457
16458
16459
16460
16461
16462

16463
16464
16465
16466
16467
16468
16469
16470
16471
16472
16473
16474
16475
16476
16477
16478

16479
16480
16481
16482
16483
16484
16485
16486
16487
16488
16489
16490
16491
16492
16493
16494

16495
16496
16497
16498
16499
16500
16501
16502
16503
16504
16505
16506
16507
16508
16509
16510

16511
16512
16513
16514
16515
16516
16517
16518
16519
16520
16521
16522
16523
16524
16525
16526

16527
16528
16529
16530
16531
16532
16533
16534
16535
16536
16537
16538
16539
16540
16541
16542

16543
16544
16545
16546
16547
16548
16549
16550
16551
16552
16553
16554
16555
16556
16557
16558

16559
16560
16561
16562
16563
16564
16565
16566
16567
16568
16569
16570
16571
16572
16573
16574

16575
16576
16577
16578
16579
16580
16581
16582
16583
16584
16585
16586
16587
16588
16589
16590

16591
16592
16593
16594
16595
16596
16597
16598
16599
16600
16601
16602
16603
16604
16605
16606

16607
16608
16609
16610
16611
16612
16613
16614
16615
16616
16617
16618
16619
16620
16621
16622

16623
16624
16625
16626
16627
16628
16629
16630
16631
16632
16633
16634
16635
16636
16637
16638

16639
16640
16641
16642
16643
16644
16645
16646
16647
16648
16649
16650
16651
16652
16653
16654

16655
16656
16657
16658
16659
16660
16661
16662
16663
16664
16665
16666
16667
16668
16669
16670

16671
16672
16673
16674
16675
16676
16677
16678
16679
16680
16681
16682
16683
16684
16685
16686

16687
16688
16689
16690
16691
16692
16693
16694
16695
16696
16697
16698
16699
16700
16701
16702

16703
16704
16705
16706
16707
16708
16709
16710
16711
16712
16713
16714
16715
16716
16717
16718

16719
16720
16721
16722
16723
16724
16725
16726
16727
16728
16729
16730
16731
16732
16733
16734

16735
16736
16737
16738
16739
16740
16741
16742
16743
16744
16745
16746
16747
16748
16749
16750

16751
16752
16753
16754
16755
16756
16757
16758
16759
16760
16761
16762
16763
16764
16765
16766

16767
16768
16769
16770
16771
16772
16773
16774
16775
16776
16777
16778
16779
16780
16781
16782

16783
16784
16785
16786
16787
16788
16789
16790
16791
16792
16793
16794
16795
16796
16797
16798

16799
16800
16801
16802
16803
16804
16805
16806
16807
16808
16809
16810
16811
16812
16813
16814

16815
16816
16817
16818
16819
16820
16821
16822
16823
16824
16825
16826
16827
16828
16829
16830

16831
16832
16833
16834
16835
16836
16837
16838
16839
16840
16841
16842
16843
16844
16845
16846

16847
16848
16849
16850
16851
16852
16853
16854
16855
16856
16857
16858
16859
16860
16861
16862

16863
16864
16865
16866
16867
16868
16869
16870
16871
16872
16873
16874
16875
16876
16877
16878

16879
16880
16881
16882
16883
16884
16885
16886
16887
16888
16889
16890
16891
16892
16893
16894

16895
16896
16897
16898
16899
16900
16901
16902
16903
16904
16905
16906
16907
16908
16909
16910

16911
16912
16913
16914
16915
16916
16917
16918
16919
16920
16921
16922
16923
16924
16925
16926

16927
16928
16929
16930
16931
16932
16933
16934
16935
16936
16937
16938
16939
16940
16941
16942

16943
16944
16945
16946
16947
16948
16949
16950
16951
16952
16953
16954
16955
16956
16957
16958

16959
16960
16961
16962
16963
16964
16965
16966
16967
16968
16969
16970
16971
16972
16973
16974

16975
16976
16977
16978
16979
16980
16981
16982
16983
16984
16985
16986
16987
16988
16989
16990

16991
16992
16993
16994
16995
16996
16997
16998
16999
17000
17001
17002
17003
17004
17005
17006

17007
17008
17009
17010
17011
17012
17013
17014
17015
17016
17017
17018
17019
17020
17021
17022

17023
17024
17025
17026
17027
17028
17029
17030
17031
17032
17033
17034
17035
17036
17037
17038

17039
17040
17041
17042
17043
17044
17045
17046







-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+







Has flag LOOK_CRLF: no
Has flag LOOK_LONG: no
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1080 utf-check-1080-2-80-0.jnk \
{File "%TEMP%/utf-check-1080-2-80-0.jnk" has 16388 bytes.
{File "%TEMP%/utf-check-1080-2-80-0.jnk" has 65540 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: yes
Looks like UTF-16: no
Has flag LOOK_NUL: no
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1081 utf-check-1081-2-80-1.jnk \
{File "%TEMP%/utf-check-1081-2-80-1.jnk" has 16389 bytes.
{File "%TEMP%/utf-check-1081-2-80-1.jnk" has 65541 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: yes
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1082 utf-check-1082-2-81-0.jnk \
{File "%TEMP%/utf-check-1082-2-81-0.jnk" has 16390 bytes.
{File "%TEMP%/utf-check-1082-2-81-0.jnk" has 65542 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: yes
Looks like UTF-16: no
Has flag LOOK_NUL: no
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1083 utf-check-1083-2-81-1.jnk \
{File "%TEMP%/utf-check-1083-2-81-1.jnk" has 16391 bytes.
{File "%TEMP%/utf-check-1083-2-81-1.jnk" has 65543 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: yes
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1084 utf-check-1084-2-82-0.jnk \
{File "%TEMP%/utf-check-1084-2-82-0.jnk" has 16390 bytes.
{File "%TEMP%/utf-check-1084-2-82-0.jnk" has 65542 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: yes
Looks like UTF-16: no
Has flag LOOK_NUL: no
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1085 utf-check-1085-2-82-1.jnk \
{File "%TEMP%/utf-check-1085-2-82-1.jnk" has 16391 bytes.
{File "%TEMP%/utf-check-1085-2-82-1.jnk" has 65543 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: yes
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1086 utf-check-1086-2-83-0.jnk \
{File "%TEMP%/utf-check-1086-2-83-0.jnk" has 16392 bytes.
{File "%TEMP%/utf-check-1086-2-83-0.jnk" has 65544 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: yes
Looks like UTF-16: no
Has flag LOOK_NUL: no
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: yes
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1087 utf-check-1087-2-83-1.jnk \
{File "%TEMP%/utf-check-1087-2-83-1.jnk" has 16393 bytes.
{File "%TEMP%/utf-check-1087-2-83-1.jnk" has 65545 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: yes
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1088 utf-check-1088-2-84-0.jnk \
{File "%TEMP%/utf-check-1088-2-84-0.jnk" has 16394 bytes.
{File "%TEMP%/utf-check-1088-2-84-0.jnk" has 65570 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: yes
Looks like UTF-16: no
Has flag LOOK_NUL: no
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1089 utf-check-1089-2-84-1.jnk \
{File "%TEMP%/utf-check-1089-2-84-1.jnk" has 16395 bytes.
{File "%TEMP%/utf-check-1089-2-84-1.jnk" has 65571 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: yes
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1090 utf-check-1090-2-85-0.jnk \
{File "%TEMP%/utf-check-1090-2-85-0.jnk" has 16396 bytes.
{File "%TEMP%/utf-check-1090-2-85-0.jnk" has 65572 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: yes
Looks like UTF-16: no
Has flag LOOK_NUL: no
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1091 utf-check-1091-2-85-1.jnk \
{File "%TEMP%/utf-check-1091-2-85-1.jnk" has 16397 bytes.
{File "%TEMP%/utf-check-1091-2-85-1.jnk" has 65573 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: yes
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1092 utf-check-1092-2-86-0.jnk \
{File "%TEMP%/utf-check-1092-2-86-0.jnk" has 16396 bytes.
{File "%TEMP%/utf-check-1092-2-86-0.jnk" has 65572 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: yes
Looks like UTF-16: no
Has flag LOOK_NUL: no
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1093 utf-check-1093-2-86-1.jnk \
{File "%TEMP%/utf-check-1093-2-86-1.jnk" has 16397 bytes.
{File "%TEMP%/utf-check-1093-2-86-1.jnk" has 65573 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: yes
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1094 utf-check-1094-2-87-0.jnk \
{File "%TEMP%/utf-check-1094-2-87-0.jnk" has 16398 bytes.
{File "%TEMP%/utf-check-1094-2-87-0.jnk" has 65574 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: yes
Looks like UTF-16: no
Has flag LOOK_NUL: no
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: yes
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1095 utf-check-1095-2-87-1.jnk \
{File "%TEMP%/utf-check-1095-2-87-1.jnk" has 16399 bytes.
{File "%TEMP%/utf-check-1095-2-87-1.jnk" has 65575 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: yes
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1096 utf-check-1096-2-88-0.jnk \
{File "%TEMP%/utf-check-1096-2-88-0.jnk" has 16390 bytes.
{File "%TEMP%/utf-check-1096-2-88-0.jnk" has 65542 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1097 utf-check-1097-2-88-1.jnk \
{File "%TEMP%/utf-check-1097-2-88-1.jnk" has 16391 bytes.
{File "%TEMP%/utf-check-1097-2-88-1.jnk" has 65543 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1098 utf-check-1098-2-89-0.jnk \
{File "%TEMP%/utf-check-1098-2-89-0.jnk" has 16392 bytes.
{File "%TEMP%/utf-check-1098-2-89-0.jnk" has 65544 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1099 utf-check-1099-2-89-1.jnk \
{File "%TEMP%/utf-check-1099-2-89-1.jnk" has 16393 bytes.
{File "%TEMP%/utf-check-1099-2-89-1.jnk" has 65545 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1100 utf-check-1100-2-90-0.jnk \
{File "%TEMP%/utf-check-1100-2-90-0.jnk" has 16392 bytes.
{File "%TEMP%/utf-check-1100-2-90-0.jnk" has 65544 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1101 utf-check-1101-2-90-1.jnk \
{File "%TEMP%/utf-check-1101-2-90-1.jnk" has 16393 bytes.
{File "%TEMP%/utf-check-1101-2-90-1.jnk" has 65545 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1102 utf-check-1102-2-91-0.jnk \
{File "%TEMP%/utf-check-1102-2-91-0.jnk" has 16394 bytes.
{File "%TEMP%/utf-check-1102-2-91-0.jnk" has 65546 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1103 utf-check-1103-2-91-1.jnk \
{File "%TEMP%/utf-check-1103-2-91-1.jnk" has 16395 bytes.
{File "%TEMP%/utf-check-1103-2-91-1.jnk" has 65547 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1104 utf-check-1104-2-92-0.jnk \
{File "%TEMP%/utf-check-1104-2-92-0.jnk" has 16396 bytes.
{File "%TEMP%/utf-check-1104-2-92-0.jnk" has 65572 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1105 utf-check-1105-2-92-1.jnk \
{File "%TEMP%/utf-check-1105-2-92-1.jnk" has 16397 bytes.
{File "%TEMP%/utf-check-1105-2-92-1.jnk" has 65573 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1106 utf-check-1106-2-93-0.jnk \
{File "%TEMP%/utf-check-1106-2-93-0.jnk" has 16398 bytes.
{File "%TEMP%/utf-check-1106-2-93-0.jnk" has 65574 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1107 utf-check-1107-2-93-1.jnk \
{File "%TEMP%/utf-check-1107-2-93-1.jnk" has 16399 bytes.
{File "%TEMP%/utf-check-1107-2-93-1.jnk" has 65575 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1108 utf-check-1108-2-94-0.jnk \
{File "%TEMP%/utf-check-1108-2-94-0.jnk" has 16398 bytes.
{File "%TEMP%/utf-check-1108-2-94-0.jnk" has 65574 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1109 utf-check-1109-2-94-1.jnk \
{File "%TEMP%/utf-check-1109-2-94-1.jnk" has 16399 bytes.
{File "%TEMP%/utf-check-1109-2-94-1.jnk" has 65575 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1110 utf-check-1110-2-95-0.jnk \
{File "%TEMP%/utf-check-1110-2-95-0.jnk" has 16400 bytes.
{File "%TEMP%/utf-check-1110-2-95-0.jnk" has 65576 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1111 utf-check-1111-2-95-1.jnk \
{File "%TEMP%/utf-check-1111-2-95-1.jnk" has 16401 bytes.
{File "%TEMP%/utf-check-1111-2-95-1.jnk" has 65577 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1112 utf-check-1112-2-96-0.jnk \
{File "%TEMP%/utf-check-1112-2-96-0.jnk" has 16390 bytes.
{File "%TEMP%/utf-check-1112-2-96-0.jnk" has 65542 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: yes
Looks like UTF-16: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1113 utf-check-1113-2-96-1.jnk \
{File "%TEMP%/utf-check-1113-2-96-1.jnk" has 16391 bytes.
{File "%TEMP%/utf-check-1113-2-96-1.jnk" has 65543 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: yes
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1114 utf-check-1114-2-97-0.jnk \
{File "%TEMP%/utf-check-1114-2-97-0.jnk" has 16392 bytes.
{File "%TEMP%/utf-check-1114-2-97-0.jnk" has 65544 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: yes
Looks like UTF-16: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1115 utf-check-1115-2-97-1.jnk \
{File "%TEMP%/utf-check-1115-2-97-1.jnk" has 16393 bytes.
{File "%TEMP%/utf-check-1115-2-97-1.jnk" has 65545 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: yes
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1116 utf-check-1116-2-98-0.jnk \
{File "%TEMP%/utf-check-1116-2-98-0.jnk" has 16392 bytes.
{File "%TEMP%/utf-check-1116-2-98-0.jnk" has 65544 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: yes
Looks like UTF-16: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1117 utf-check-1117-2-98-1.jnk \
{File "%TEMP%/utf-check-1117-2-98-1.jnk" has 16393 bytes.
{File "%TEMP%/utf-check-1117-2-98-1.jnk" has 65545 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: yes
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1118 utf-check-1118-2-99-0.jnk \
{File "%TEMP%/utf-check-1118-2-99-0.jnk" has 16394 bytes.
{File "%TEMP%/utf-check-1118-2-99-0.jnk" has 65546 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: yes
Looks like UTF-16: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: yes
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1119 utf-check-1119-2-99-1.jnk \
{File "%TEMP%/utf-check-1119-2-99-1.jnk" has 16395 bytes.
{File "%TEMP%/utf-check-1119-2-99-1.jnk" has 65547 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: yes
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1120 utf-check-1120-2-100-0.jnk \
{File "%TEMP%/utf-check-1120-2-100-0.jnk" has 16396 bytes.
{File "%TEMP%/utf-check-1120-2-100-0.jnk" has 65572 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: yes
Looks like UTF-16: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1121 utf-check-1121-2-100-1.jnk \
{File "%TEMP%/utf-check-1121-2-100-1.jnk" has 16397 bytes.
{File "%TEMP%/utf-check-1121-2-100-1.jnk" has 65573 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: yes
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1122 utf-check-1122-2-101-0.jnk \
{File "%TEMP%/utf-check-1122-2-101-0.jnk" has 16398 bytes.
{File "%TEMP%/utf-check-1122-2-101-0.jnk" has 65574 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: yes
Looks like UTF-16: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1123 utf-check-1123-2-101-1.jnk \
{File "%TEMP%/utf-check-1123-2-101-1.jnk" has 16399 bytes.
{File "%TEMP%/utf-check-1123-2-101-1.jnk" has 65575 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: yes
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1124 utf-check-1124-2-102-0.jnk \
{File "%TEMP%/utf-check-1124-2-102-0.jnk" has 16398 bytes.
{File "%TEMP%/utf-check-1124-2-102-0.jnk" has 65574 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: yes
Looks like UTF-16: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1125 utf-check-1125-2-102-1.jnk \
{File "%TEMP%/utf-check-1125-2-102-1.jnk" has 16399 bytes.
{File "%TEMP%/utf-check-1125-2-102-1.jnk" has 65575 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: yes
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1126 utf-check-1126-2-103-0.jnk \
{File "%TEMP%/utf-check-1126-2-103-0.jnk" has 16400 bytes.
{File "%TEMP%/utf-check-1126-2-103-0.jnk" has 65576 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: yes
Looks like UTF-16: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: yes
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1127 utf-check-1127-2-103-1.jnk \
{File "%TEMP%/utf-check-1127-2-103-1.jnk" has 16401 bytes.
{File "%TEMP%/utf-check-1127-2-103-1.jnk" has 65577 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: yes
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1128 utf-check-1128-2-104-0.jnk \
{File "%TEMP%/utf-check-1128-2-104-0.jnk" has 16392 bytes.
{File "%TEMP%/utf-check-1128-2-104-0.jnk" has 65544 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1129 utf-check-1129-2-104-1.jnk \
{File "%TEMP%/utf-check-1129-2-104-1.jnk" has 16393 bytes.
{File "%TEMP%/utf-check-1129-2-104-1.jnk" has 65545 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1130 utf-check-1130-2-105-0.jnk \
{File "%TEMP%/utf-check-1130-2-105-0.jnk" has 16394 bytes.
{File "%TEMP%/utf-check-1130-2-105-0.jnk" has 65546 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1131 utf-check-1131-2-105-1.jnk \
{File "%TEMP%/utf-check-1131-2-105-1.jnk" has 16395 bytes.
{File "%TEMP%/utf-check-1131-2-105-1.jnk" has 65547 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1132 utf-check-1132-2-106-0.jnk \
{File "%TEMP%/utf-check-1132-2-106-0.jnk" has 16394 bytes.
{File "%TEMP%/utf-check-1132-2-106-0.jnk" has 65546 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1133 utf-check-1133-2-106-1.jnk \
{File "%TEMP%/utf-check-1133-2-106-1.jnk" has 16395 bytes.
{File "%TEMP%/utf-check-1133-2-106-1.jnk" has 65547 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1134 utf-check-1134-2-107-0.jnk \
{File "%TEMP%/utf-check-1134-2-107-0.jnk" has 16396 bytes.
{File "%TEMP%/utf-check-1134-2-107-0.jnk" has 65548 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1135 utf-check-1135-2-107-1.jnk \
{File "%TEMP%/utf-check-1135-2-107-1.jnk" has 16397 bytes.
{File "%TEMP%/utf-check-1135-2-107-1.jnk" has 65549 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1136 utf-check-1136-2-108-0.jnk \
{File "%TEMP%/utf-check-1136-2-108-0.jnk" has 16398 bytes.
{File "%TEMP%/utf-check-1136-2-108-0.jnk" has 65574 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1137 utf-check-1137-2-108-1.jnk \
{File "%TEMP%/utf-check-1137-2-108-1.jnk" has 16399 bytes.
{File "%TEMP%/utf-check-1137-2-108-1.jnk" has 65575 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1138 utf-check-1138-2-109-0.jnk \
{File "%TEMP%/utf-check-1138-2-109-0.jnk" has 16400 bytes.
{File "%TEMP%/utf-check-1138-2-109-0.jnk" has 65576 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1139 utf-check-1139-2-109-1.jnk \
{File "%TEMP%/utf-check-1139-2-109-1.jnk" has 16401 bytes.
{File "%TEMP%/utf-check-1139-2-109-1.jnk" has 65577 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1140 utf-check-1140-2-110-0.jnk \
{File "%TEMP%/utf-check-1140-2-110-0.jnk" has 16400 bytes.
{File "%TEMP%/utf-check-1140-2-110-0.jnk" has 65576 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1141 utf-check-1141-2-110-1.jnk \
{File "%TEMP%/utf-check-1141-2-110-1.jnk" has 16401 bytes.
{File "%TEMP%/utf-check-1141-2-110-1.jnk" has 65577 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1142 utf-check-1142-2-111-0.jnk \
{File "%TEMP%/utf-check-1142-2-111-0.jnk" has 16402 bytes.
{File "%TEMP%/utf-check-1142-2-111-0.jnk" has 65578 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: yes
Has flag LOOK_LONE_LF: yes
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1143 utf-check-1143-2-111-1.jnk \
{File "%TEMP%/utf-check-1143-2-111-1.jnk" has 16403 bytes.
{File "%TEMP%/utf-check-1143-2-111-1.jnk" has 65579 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: yes
Has flag LOOK_LONE_CR: yes
Has flag LOOK_LF: yes
17611
17612
17613
17614
17615
17616
17617
17618
17619


17620
17621
17622
17623
17624
17625
17626
17611
17612
17613
17614
17615
17616
17617


17618
17619
17620
17621
17622
17623
17624
17625
17626







-
-
+
+







Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1179 utf-check-1179-2-129-1.jnk \
{File "%TEMP%/utf-check-1179-2-129-1.jnk" has 7 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: yes
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Looks like UTF-8: yes
Has flag LOOK_NUL: no
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: no
Has flag LOOK_INVALID: yes
22584
22585
22586
22587
22588
22589
22590
22591

22592
22593
22594
22595
22596
22597
22598
22599
22600
22601
22602
22603
22604
22605
22606
22607

22608
22609
22610
22611
22612
22613
22614
22615
22616
22617
22618
22619
22620
22621
22622
22623

22624
22625
22626
22627
22628
22629
22630
22631
22632
22633
22634
22635
22636
22637
22638
22639

22640
22641
22642
22643
22644
22645
22646
22647
22648
22649
22650
22651
22652
22653
22654
22655

22656
22657
22658
22659
22660
22661
22662
22663
22664
22665
22666
22667
22668
22669
22670
22671

22672
22673
22674
22675
22676
22677
22678
22679
22680
22681
22682
22683
22684
22685
22686
22687

22688
22689
22690
22691
22692
22693
22694
22695
22696
22697
22698
22699
22700
22701
22702
22703

22704
22705
22706
22707
22708
22709
22710
22711
22712
22713
22714
22715
22716
22717
22718
22719

22720
22721
22722
22723
22724
22725
22726
22727
22728
22729
22730
22731
22732
22733
22734
22735

22736
22737
22738
22739
22740
22741
22742
22743
22744
22745
22746
22747
22748
22749
22750
22751

22752
22753
22754
22755
22756
22757
22758
22759
22760
22761
22762
22763
22764
22765
22766
22767

22768
22769
22770
22771
22772
22773
22774
22775
22776
22777
22778
22779
22780
22781
22782
22783

22784
22785
22786
22787
22788
22789
22790
22791
22792
22793
22794
22795
22796
22797
22798
22799

22800
22801
22802
22803
22804
22805
22806
22807
22808
22809
22810
22811
22812
22813
22814
22815

22816
22817
22818
22819
22820
22821
22822
22823
22824
22825
22826
22827
22828
22829
22830
22831

22832
22833
22834
22835
22836
22837
22838
22839
22840
22841
22842
22843
22844
22845
22846
22847

22848
22849
22850
22851
22852
22853
22854
22855
22856
22857
22858
22859
22860
22861
22862
22863

22864
22865
22866
22867
22868
22869
22870
22871
22872
22873
22874
22875
22876
22877
22878
22879

22880
22881
22882
22883
22884
22885
22886
22887
22888
22889
22890
22891
22892
22893
22894
22895

22896
22897
22898
22899
22900
22901
22902
22903
22904
22905
22906
22907
22908
22909
22910
22911

22912
22913
22914
22915
22916
22917
22918
22919
22920
22921
22922
22923
22924
22925
22926
22927

22928
22929
22930
22931
22932
22933
22934
22935
22936
22937
22938
22939
22940
22941
22942
22943

22944
22945
22946
22947
22948
22949
22950
22951
22952
22953
22954
22955
22956
22957
22958
22959

22960
22961
22962
22963
22964
22965
22966
22967
22968
22969
22970
22971
22972
22973
22974
22975

22976
22977
22978
22979
22980
22981
22982
22983
22984
22985
22986
22987
22988
22989
22990
22991

22992
22993
22994
22995
22996
22997
22998
22999
23000
23001
23002
23003
23004
23005
23006
23007

23008
23009
23010
23011
23012
23013
23014
23015
23016
23017
23018
23019
23020
23021
23022
23023

23024
23025
23026
23027
23028
23029
23030
23031
23032
23033
23034
23035
23036
23037
23038
23039

23040
23041
23042
23043
23044
23045
23046
23047
23048
23049
23050
23051
23052
23053
23054
23055

23056
23057
23058
23059
23060
23061
23062
23063
23064
23065
23066
23067
23068
23069
23070
23071

23072
23073
23074
23075
23076
23077
23078
23079
23080
23081
23082
23083
23084
23085
23086
23087

23088
23089
23090
23091
23092
23093
23094
23095
23096
23097
23098
23099
23100
23101
23102
23103

23104
23105
23106
23107
23108
23109
23110
23111
23112
23113
23114
23115
23116
23117
23118
23119

23120
23121
23122
23123
23124
23125
23126
23127
23128
23129
23130
23131
23132
23133
23134
23135

23136
23137
23138
23139
23140
23141
23142
23143
23144
23145
23146
23147
23148
23149
23150
23151

23152
23153
23154
23155
23156
23157
23158
23159
23160
23161
23162
23163
23164
23165
23166
23167

23168
23169
23170
23171
23172
23173
23174
23175
23176
23177
23178
23179
23180
23181
23182
23183

23184
23185
23186
23187
23188
23189
23190
23191
23192
23193
23194
23195
23196
23197
23198
23199

23200
23201
23202
23203
23204
23205
23206
23207
23208
23209
23210
23211
23212
23213
23214
23215

23216
23217
23218
23219
23220
23221
23222
23223
23224
23225
23226
23227
23228
23229
23230
23231

23232
23233
23234
23235
23236
23237
23238
23239
23240
23241
23242
23243
23244
23245
23246
23247

23248
23249
23250
23251
23252
23253
23254
23255
23256
23257
23258
23259
23260
23261
23262
23263

23264
23265
23266
23267
23268
23269
23270
23271
23272
23273
23274
23275
23276
23277
23278
23279

23280
23281
23282
23283
23284
23285
23286
23287
23288
23289
23290
23291
23292
23293
23294
23295

23296
23297
23298
23299
23300
23301
23302
23303
23304
23305
23306
23307
23308
23309
23310
23311

23312
23313
23314
23315
23316
23317
23318
23319
23320
23321
23322
23323
23324
23325
23326
23327

23328
23329
23330
23331
23332
23333
23334
23335
23336
23337
23338
23339
23340
23341
23342
23343

23344
23345
23346
23347
23348
23349
23350
23351
23352
23353
23354
23355
23356
23357
23358
23359

23360
23361
23362
23363
23364
23365
23366
23367
23368
23369
23370
23371
23372
23373
23374
23375

23376
23377
23378
23379
23380
23381
23382
23383
23384
23385
23386
23387
23388
23389
23390
23391

23392
23393
23394
23395
23396
23397
23398
23399
23400
23401
23402
23403
23404
23405
23406
23407

23408
23409
23410
23411
23412
23413
23414
23415
23416
23417
23418
23419
23420
23421
23422
23423

23424
23425
23426
23427
23428
23429
23430
23431
23432
23433
23434
23435
23436
23437
23438
23439

23440
23441
23442
23443
23444
23445
23446
23447
23448
23449
23450
23451
23452
23453
23454
23455

23456
23457
23458
23459
23460
23461
23462
23463
23464
23465
23466
23467
23468
23469
23470
23471

23472
23473
23474
23475
23476
23477
23478
23479
23480
23481
23482
23483
23484
23485
23486
23487

23488
23489
23490
23491
23492
23493
23494
23495
23496
23497
23498
23499
23500
23501
23502
23503

23504
23505
23506
23507
23508
23509
23510
23511
23512
23513
23514
23515
23516
23517
23518
23519

23520
23521
23522
23523
23524
23525
23526
23527
23528
23529
23530
23531
23532
23533
23534
23535

23536
23537
23538
23539
23540
23541
23542
23543
23544
23545
23546
23547
23548
23549
23550
23551

23552
23553
23554
23555
23556
23557
23558
23559
23560
23561
23562
23563
23564
23565
23566
23567

23568
23569
23570
23571
23572
23573
23574
23575
23576
23577
23578
23579
23580
23581
23582
23583

23584
23585
23586
23587
23588
23589
23590
23591
23592
23593
23594
23595
23596
23597
23598
23599

23600
23601
23602
23603
23604
23605
23606
22584
22585
22586
22587
22588
22589
22590

22591
22592
22593
22594
22595
22596
22597
22598
22599
22600
22601
22602
22603
22604
22605
22606

22607
22608
22609
22610
22611
22612
22613
22614
22615
22616
22617
22618
22619
22620
22621
22622

22623
22624
22625
22626
22627
22628
22629
22630
22631
22632
22633
22634
22635
22636
22637
22638

22639
22640
22641
22642
22643
22644
22645
22646
22647
22648
22649
22650
22651
22652
22653
22654

22655
22656
22657
22658
22659
22660
22661
22662
22663
22664
22665
22666
22667
22668
22669
22670

22671
22672
22673
22674
22675
22676
22677
22678
22679
22680
22681
22682
22683
22684
22685
22686

22687
22688
22689
22690
22691
22692
22693
22694
22695
22696
22697
22698
22699
22700
22701
22702

22703
22704
22705
22706
22707
22708
22709
22710
22711
22712
22713
22714
22715
22716
22717
22718

22719
22720
22721
22722
22723
22724
22725
22726
22727
22728
22729
22730
22731
22732
22733
22734

22735
22736
22737
22738
22739
22740
22741
22742
22743
22744
22745
22746
22747
22748
22749
22750

22751
22752
22753
22754
22755
22756
22757
22758
22759
22760
22761
22762
22763
22764
22765
22766

22767
22768
22769
22770
22771
22772
22773
22774
22775
22776
22777
22778
22779
22780
22781
22782

22783
22784
22785
22786
22787
22788
22789
22790
22791
22792
22793
22794
22795
22796
22797
22798

22799
22800
22801
22802
22803
22804
22805
22806
22807
22808
22809
22810
22811
22812
22813
22814

22815
22816
22817
22818
22819
22820
22821
22822
22823
22824
22825
22826
22827
22828
22829
22830

22831
22832
22833
22834
22835
22836
22837
22838
22839
22840
22841
22842
22843
22844
22845
22846

22847
22848
22849
22850
22851
22852
22853
22854
22855
22856
22857
22858
22859
22860
22861
22862

22863
22864
22865
22866
22867
22868
22869
22870
22871
22872
22873
22874
22875
22876
22877
22878

22879
22880
22881
22882
22883
22884
22885
22886
22887
22888
22889
22890
22891
22892
22893
22894

22895
22896
22897
22898
22899
22900
22901
22902
22903
22904
22905
22906
22907
22908
22909
22910

22911
22912
22913
22914
22915
22916
22917
22918
22919
22920
22921
22922
22923
22924
22925
22926

22927
22928
22929
22930
22931
22932
22933
22934
22935
22936
22937
22938
22939
22940
22941
22942

22943
22944
22945
22946
22947
22948
22949
22950
22951
22952
22953
22954
22955
22956
22957
22958

22959
22960
22961
22962
22963
22964
22965
22966
22967
22968
22969
22970
22971
22972
22973
22974

22975
22976
22977
22978
22979
22980
22981
22982
22983
22984
22985
22986
22987
22988
22989
22990

22991
22992
22993
22994
22995
22996
22997
22998
22999
23000
23001
23002
23003
23004
23005
23006

23007
23008
23009
23010
23011
23012
23013
23014
23015
23016
23017
23018
23019
23020
23021
23022

23023
23024
23025
23026
23027
23028
23029
23030
23031
23032
23033
23034
23035
23036
23037
23038

23039
23040
23041
23042
23043
23044
23045
23046
23047
23048
23049
23050
23051
23052
23053
23054

23055
23056
23057
23058
23059
23060
23061
23062
23063
23064
23065
23066
23067
23068
23069
23070

23071
23072
23073
23074
23075
23076
23077
23078
23079
23080
23081
23082
23083
23084
23085
23086

23087
23088
23089
23090
23091
23092
23093
23094
23095
23096
23097
23098
23099
23100
23101
23102

23103
23104
23105
23106
23107
23108
23109
23110
23111
23112
23113
23114
23115
23116
23117
23118

23119
23120
23121
23122
23123
23124
23125
23126
23127
23128
23129
23130
23131
23132
23133
23134

23135
23136
23137
23138
23139
23140
23141
23142
23143
23144
23145
23146
23147
23148
23149
23150

23151
23152
23153
23154
23155
23156
23157
23158
23159
23160
23161
23162
23163
23164
23165
23166

23167
23168
23169
23170
23171
23172
23173
23174
23175
23176
23177
23178
23179
23180
23181
23182

23183
23184
23185
23186
23187
23188
23189
23190
23191
23192
23193
23194
23195
23196
23197
23198

23199
23200
23201
23202
23203
23204
23205
23206
23207
23208
23209
23210
23211
23212
23213
23214

23215
23216
23217
23218
23219
23220
23221
23222
23223
23224
23225
23226
23227
23228
23229
23230

23231
23232
23233
23234
23235
23236
23237
23238
23239
23240
23241
23242
23243
23244
23245
23246

23247
23248
23249
23250
23251
23252
23253
23254
23255
23256
23257
23258
23259
23260
23261
23262

23263
23264
23265
23266
23267
23268
23269
23270
23271
23272
23273
23274
23275
23276
23277
23278

23279
23280
23281
23282
23283
23284
23285
23286
23287
23288
23289
23290
23291
23292
23293
23294

23295
23296
23297
23298
23299
23300
23301
23302
23303
23304
23305
23306
23307
23308
23309
23310

23311
23312
23313
23314
23315
23316
23317
23318
23319
23320
23321
23322
23323
23324
23325
23326

23327
23328
23329
23330
23331
23332
23333
23334
23335
23336
23337
23338
23339
23340
23341
23342

23343
23344
23345
23346
23347
23348
23349
23350
23351
23352
23353
23354
23355
23356
23357
23358

23359
23360
23361
23362
23363
23364
23365
23366
23367
23368
23369
23370
23371
23372
23373
23374

23375
23376
23377
23378
23379
23380
23381
23382
23383
23384
23385
23386
23387
23388
23389
23390

23391
23392
23393
23394
23395
23396
23397
23398
23399
23400
23401
23402
23403
23404
23405
23406

23407
23408
23409
23410
23411
23412
23413
23414
23415
23416
23417
23418
23419
23420
23421
23422

23423
23424
23425
23426
23427
23428
23429
23430
23431
23432
23433
23434
23435
23436
23437
23438

23439
23440
23441
23442
23443
23444
23445
23446
23447
23448
23449
23450
23451
23452
23453
23454

23455
23456
23457
23458
23459
23460
23461
23462
23463
23464
23465
23466
23467
23468
23469
23470

23471
23472
23473
23474
23475
23476
23477
23478
23479
23480
23481
23482
23483
23484
23485
23486

23487
23488
23489
23490
23491
23492
23493
23494
23495
23496
23497
23498
23499
23500
23501
23502

23503
23504
23505
23506
23507
23508
23509
23510
23511
23512
23513
23514
23515
23516
23517
23518

23519
23520
23521
23522
23523
23524
23525
23526
23527
23528
23529
23530
23531
23532
23533
23534

23535
23536
23537
23538
23539
23540
23541
23542
23543
23544
23545
23546
23547
23548
23549
23550

23551
23552
23553
23554
23555
23556
23557
23558
23559
23560
23561
23562
23563
23564
23565
23566

23567
23568
23569
23570
23571
23572
23573
23574
23575
23576
23577
23578
23579
23580
23581
23582

23583
23584
23585
23586
23587
23588
23589
23590
23591
23592
23593
23594
23595
23596
23597
23598

23599
23600
23601
23602
23603
23604
23605
23606







-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+















-
+







Has flag LOOK_CRLF: no
Has flag LOOK_LONG: no
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1490 utf-check-1490-3-80-0.jnk \
{File "%TEMP%/utf-check-1490-3-80-0.jnk" has 16388 bytes.
{File "%TEMP%/utf-check-1490-3-80-0.jnk" has 65540 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1491 utf-check-1491-3-80-1.jnk \
{File "%TEMP%/utf-check-1491-3-80-1.jnk" has 16389 bytes.
{File "%TEMP%/utf-check-1491-3-80-1.jnk" has 65541 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1492 utf-check-1492-3-81-0.jnk \
{File "%TEMP%/utf-check-1492-3-81-0.jnk" has 16390 bytes.
{File "%TEMP%/utf-check-1492-3-81-0.jnk" has 65542 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1493 utf-check-1493-3-81-1.jnk \
{File "%TEMP%/utf-check-1493-3-81-1.jnk" has 16391 bytes.
{File "%TEMP%/utf-check-1493-3-81-1.jnk" has 65543 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1494 utf-check-1494-3-82-0.jnk \
{File "%TEMP%/utf-check-1494-3-82-0.jnk" has 16390 bytes.
{File "%TEMP%/utf-check-1494-3-82-0.jnk" has 65542 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1495 utf-check-1495-3-82-1.jnk \
{File "%TEMP%/utf-check-1495-3-82-1.jnk" has 16391 bytes.
{File "%TEMP%/utf-check-1495-3-82-1.jnk" has 65543 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1496 utf-check-1496-3-83-0.jnk \
{File "%TEMP%/utf-check-1496-3-83-0.jnk" has 16392 bytes.
{File "%TEMP%/utf-check-1496-3-83-0.jnk" has 65544 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1497 utf-check-1497-3-83-1.jnk \
{File "%TEMP%/utf-check-1497-3-83-1.jnk" has 16393 bytes.
{File "%TEMP%/utf-check-1497-3-83-1.jnk" has 65545 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1498 utf-check-1498-3-84-0.jnk \
{File "%TEMP%/utf-check-1498-3-84-0.jnk" has 16394 bytes.
{File "%TEMP%/utf-check-1498-3-84-0.jnk" has 65570 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1499 utf-check-1499-3-84-1.jnk \
{File "%TEMP%/utf-check-1499-3-84-1.jnk" has 16395 bytes.
{File "%TEMP%/utf-check-1499-3-84-1.jnk" has 65571 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1500 utf-check-1500-3-85-0.jnk \
{File "%TEMP%/utf-check-1500-3-85-0.jnk" has 16396 bytes.
{File "%TEMP%/utf-check-1500-3-85-0.jnk" has 65572 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1501 utf-check-1501-3-85-1.jnk \
{File "%TEMP%/utf-check-1501-3-85-1.jnk" has 16397 bytes.
{File "%TEMP%/utf-check-1501-3-85-1.jnk" has 65573 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1502 utf-check-1502-3-86-0.jnk \
{File "%TEMP%/utf-check-1502-3-86-0.jnk" has 16396 bytes.
{File "%TEMP%/utf-check-1502-3-86-0.jnk" has 65572 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1503 utf-check-1503-3-86-1.jnk \
{File "%TEMP%/utf-check-1503-3-86-1.jnk" has 16397 bytes.
{File "%TEMP%/utf-check-1503-3-86-1.jnk" has 65573 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1504 utf-check-1504-3-87-0.jnk \
{File "%TEMP%/utf-check-1504-3-87-0.jnk" has 16398 bytes.
{File "%TEMP%/utf-check-1504-3-87-0.jnk" has 65574 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1505 utf-check-1505-3-87-1.jnk \
{File "%TEMP%/utf-check-1505-3-87-1.jnk" has 16399 bytes.
{File "%TEMP%/utf-check-1505-3-87-1.jnk" has 65575 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1506 utf-check-1506-3-88-0.jnk \
{File "%TEMP%/utf-check-1506-3-88-0.jnk" has 16390 bytes.
{File "%TEMP%/utf-check-1506-3-88-0.jnk" has 65542 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1507 utf-check-1507-3-88-1.jnk \
{File "%TEMP%/utf-check-1507-3-88-1.jnk" has 16391 bytes.
{File "%TEMP%/utf-check-1507-3-88-1.jnk" has 65543 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1508 utf-check-1508-3-89-0.jnk \
{File "%TEMP%/utf-check-1508-3-89-0.jnk" has 16392 bytes.
{File "%TEMP%/utf-check-1508-3-89-0.jnk" has 65544 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1509 utf-check-1509-3-89-1.jnk \
{File "%TEMP%/utf-check-1509-3-89-1.jnk" has 16393 bytes.
{File "%TEMP%/utf-check-1509-3-89-1.jnk" has 65545 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1510 utf-check-1510-3-90-0.jnk \
{File "%TEMP%/utf-check-1510-3-90-0.jnk" has 16392 bytes.
{File "%TEMP%/utf-check-1510-3-90-0.jnk" has 65544 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1511 utf-check-1511-3-90-1.jnk \
{File "%TEMP%/utf-check-1511-3-90-1.jnk" has 16393 bytes.
{File "%TEMP%/utf-check-1511-3-90-1.jnk" has 65545 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1512 utf-check-1512-3-91-0.jnk \
{File "%TEMP%/utf-check-1512-3-91-0.jnk" has 16394 bytes.
{File "%TEMP%/utf-check-1512-3-91-0.jnk" has 65546 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1513 utf-check-1513-3-91-1.jnk \
{File "%TEMP%/utf-check-1513-3-91-1.jnk" has 16395 bytes.
{File "%TEMP%/utf-check-1513-3-91-1.jnk" has 65547 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1514 utf-check-1514-3-92-0.jnk \
{File "%TEMP%/utf-check-1514-3-92-0.jnk" has 16396 bytes.
{File "%TEMP%/utf-check-1514-3-92-0.jnk" has 65572 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1515 utf-check-1515-3-92-1.jnk \
{File "%TEMP%/utf-check-1515-3-92-1.jnk" has 16397 bytes.
{File "%TEMP%/utf-check-1515-3-92-1.jnk" has 65573 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1516 utf-check-1516-3-93-0.jnk \
{File "%TEMP%/utf-check-1516-3-93-0.jnk" has 16398 bytes.
{File "%TEMP%/utf-check-1516-3-93-0.jnk" has 65574 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1517 utf-check-1517-3-93-1.jnk \
{File "%TEMP%/utf-check-1517-3-93-1.jnk" has 16399 bytes.
{File "%TEMP%/utf-check-1517-3-93-1.jnk" has 65575 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1518 utf-check-1518-3-94-0.jnk \
{File "%TEMP%/utf-check-1518-3-94-0.jnk" has 16398 bytes.
{File "%TEMP%/utf-check-1518-3-94-0.jnk" has 65574 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1519 utf-check-1519-3-94-1.jnk \
{File "%TEMP%/utf-check-1519-3-94-1.jnk" has 16399 bytes.
{File "%TEMP%/utf-check-1519-3-94-1.jnk" has 65575 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1520 utf-check-1520-3-95-0.jnk \
{File "%TEMP%/utf-check-1520-3-95-0.jnk" has 16400 bytes.
{File "%TEMP%/utf-check-1520-3-95-0.jnk" has 65576 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1521 utf-check-1521-3-95-1.jnk \
{File "%TEMP%/utf-check-1521-3-95-1.jnk" has 16401 bytes.
{File "%TEMP%/utf-check-1521-3-95-1.jnk" has 65577 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1522 utf-check-1522-3-96-0.jnk \
{File "%TEMP%/utf-check-1522-3-96-0.jnk" has 16390 bytes.
{File "%TEMP%/utf-check-1522-3-96-0.jnk" has 65542 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1523 utf-check-1523-3-96-1.jnk \
{File "%TEMP%/utf-check-1523-3-96-1.jnk" has 16391 bytes.
{File "%TEMP%/utf-check-1523-3-96-1.jnk" has 65543 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1524 utf-check-1524-3-97-0.jnk \
{File "%TEMP%/utf-check-1524-3-97-0.jnk" has 16392 bytes.
{File "%TEMP%/utf-check-1524-3-97-0.jnk" has 65544 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1525 utf-check-1525-3-97-1.jnk \
{File "%TEMP%/utf-check-1525-3-97-1.jnk" has 16393 bytes.
{File "%TEMP%/utf-check-1525-3-97-1.jnk" has 65545 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1526 utf-check-1526-3-98-0.jnk \
{File "%TEMP%/utf-check-1526-3-98-0.jnk" has 16392 bytes.
{File "%TEMP%/utf-check-1526-3-98-0.jnk" has 65544 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1527 utf-check-1527-3-98-1.jnk \
{File "%TEMP%/utf-check-1527-3-98-1.jnk" has 16393 bytes.
{File "%TEMP%/utf-check-1527-3-98-1.jnk" has 65545 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1528 utf-check-1528-3-99-0.jnk \
{File "%TEMP%/utf-check-1528-3-99-0.jnk" has 16394 bytes.
{File "%TEMP%/utf-check-1528-3-99-0.jnk" has 65546 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1529 utf-check-1529-3-99-1.jnk \
{File "%TEMP%/utf-check-1529-3-99-1.jnk" has 16395 bytes.
{File "%TEMP%/utf-check-1529-3-99-1.jnk" has 65547 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1530 utf-check-1530-3-100-0.jnk \
{File "%TEMP%/utf-check-1530-3-100-0.jnk" has 16396 bytes.
{File "%TEMP%/utf-check-1530-3-100-0.jnk" has 65572 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1531 utf-check-1531-3-100-1.jnk \
{File "%TEMP%/utf-check-1531-3-100-1.jnk" has 16397 bytes.
{File "%TEMP%/utf-check-1531-3-100-1.jnk" has 65573 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1532 utf-check-1532-3-101-0.jnk \
{File "%TEMP%/utf-check-1532-3-101-0.jnk" has 16398 bytes.
{File "%TEMP%/utf-check-1532-3-101-0.jnk" has 65574 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1533 utf-check-1533-3-101-1.jnk \
{File "%TEMP%/utf-check-1533-3-101-1.jnk" has 16399 bytes.
{File "%TEMP%/utf-check-1533-3-101-1.jnk" has 65575 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1534 utf-check-1534-3-102-0.jnk \
{File "%TEMP%/utf-check-1534-3-102-0.jnk" has 16398 bytes.
{File "%TEMP%/utf-check-1534-3-102-0.jnk" has 65574 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1535 utf-check-1535-3-102-1.jnk \
{File "%TEMP%/utf-check-1535-3-102-1.jnk" has 16399 bytes.
{File "%TEMP%/utf-check-1535-3-102-1.jnk" has 65575 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1536 utf-check-1536-3-103-0.jnk \
{File "%TEMP%/utf-check-1536-3-103-0.jnk" has 16400 bytes.
{File "%TEMP%/utf-check-1536-3-103-0.jnk" has 65576 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1537 utf-check-1537-3-103-1.jnk \
{File "%TEMP%/utf-check-1537-3-103-1.jnk" has 16401 bytes.
{File "%TEMP%/utf-check-1537-3-103-1.jnk" has 65577 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1538 utf-check-1538-3-104-0.jnk \
{File "%TEMP%/utf-check-1538-3-104-0.jnk" has 16392 bytes.
{File "%TEMP%/utf-check-1538-3-104-0.jnk" has 65544 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1539 utf-check-1539-3-104-1.jnk \
{File "%TEMP%/utf-check-1539-3-104-1.jnk" has 16393 bytes.
{File "%TEMP%/utf-check-1539-3-104-1.jnk" has 65545 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1540 utf-check-1540-3-105-0.jnk \
{File "%TEMP%/utf-check-1540-3-105-0.jnk" has 16394 bytes.
{File "%TEMP%/utf-check-1540-3-105-0.jnk" has 65546 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1541 utf-check-1541-3-105-1.jnk \
{File "%TEMP%/utf-check-1541-3-105-1.jnk" has 16395 bytes.
{File "%TEMP%/utf-check-1541-3-105-1.jnk" has 65547 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1542 utf-check-1542-3-106-0.jnk \
{File "%TEMP%/utf-check-1542-3-106-0.jnk" has 16394 bytes.
{File "%TEMP%/utf-check-1542-3-106-0.jnk" has 65546 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1543 utf-check-1543-3-106-1.jnk \
{File "%TEMP%/utf-check-1543-3-106-1.jnk" has 16395 bytes.
{File "%TEMP%/utf-check-1543-3-106-1.jnk" has 65547 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1544 utf-check-1544-3-107-0.jnk \
{File "%TEMP%/utf-check-1544-3-107-0.jnk" has 16396 bytes.
{File "%TEMP%/utf-check-1544-3-107-0.jnk" has 65548 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1545 utf-check-1545-3-107-1.jnk \
{File "%TEMP%/utf-check-1545-3-107-1.jnk" has 16397 bytes.
{File "%TEMP%/utf-check-1545-3-107-1.jnk" has 65549 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1546 utf-check-1546-3-108-0.jnk \
{File "%TEMP%/utf-check-1546-3-108-0.jnk" has 16398 bytes.
{File "%TEMP%/utf-check-1546-3-108-0.jnk" has 65574 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1547 utf-check-1547-3-108-1.jnk \
{File "%TEMP%/utf-check-1547-3-108-1.jnk" has 16399 bytes.
{File "%TEMP%/utf-check-1547-3-108-1.jnk" has 65575 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1548 utf-check-1548-3-109-0.jnk \
{File "%TEMP%/utf-check-1548-3-109-0.jnk" has 16400 bytes.
{File "%TEMP%/utf-check-1548-3-109-0.jnk" has 65576 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1549 utf-check-1549-3-109-1.jnk \
{File "%TEMP%/utf-check-1549-3-109-1.jnk" has 16401 bytes.
{File "%TEMP%/utf-check-1549-3-109-1.jnk" has 65577 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1550 utf-check-1550-3-110-0.jnk \
{File "%TEMP%/utf-check-1550-3-110-0.jnk" has 16400 bytes.
{File "%TEMP%/utf-check-1550-3-110-0.jnk" has 65576 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1551 utf-check-1551-3-110-1.jnk \
{File "%TEMP%/utf-check-1551-3-110-1.jnk" has 16401 bytes.
{File "%TEMP%/utf-check-1551-3-110-1.jnk" has 65577 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1552 utf-check-1552-3-111-0.jnk \
{File "%TEMP%/utf-check-1552-3-111-0.jnk" has 16402 bytes.
{File "%TEMP%/utf-check-1552-3-111-0.jnk" has 65578 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: yes
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1553 utf-check-1553-3-111-1.jnk \
{File "%TEMP%/utf-check-1553-3-111-1.jnk" has 16403 bytes.
{File "%TEMP%/utf-check-1553-3-111-1.jnk" has 65579 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
24122
24123
24124
24125
24126
24127
24128
24129
24130


24131
24132
24133
24134
24135
24136
24137
24138

24139
24140
24141
24142
24143
24144
24145

24146
24147
24148
24149
24150
24151
24152
24122
24123
24124
24125
24126
24127
24128


24129
24130
24131
24132
24133
24134
24135
24136
24137

24138
24139
24140
24141
24142
24143
24144

24145
24146
24147
24148
24149
24150
24151
24152







-
-
+
+







-
+






-
+







Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1586 utf-check-1586-3-128-0.jnk \
{File "%TEMP%/utf-check-1586-3-128-0.jnk" has 6 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Starts with UTF-16 BOM: reversed
Looks like UTF-16: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: no
Has flag LOOK_INVALID: yes
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}

utf-check 1587 utf-check-1587-3-128-1.jnk \
{File "%TEMP%/utf-check-1587-3-128-1.jnk" has 7 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Starts with UTF-16 BOM: reversed
Looks like UTF-8: no
Has flag LOOK_NUL: yes
Has flag LOOK_CR: no
Has flag LOOK_LONE_CR: no
Has flag LOOK_LF: no
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no

Changes to test/wiki.test.

15
16
17
18
19
20
21






22
23
24
25
26
27
28
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34







+
+
+
+
+
+







#
############################################################################
#
# 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]
43
44
45
46
47
48
49
50

51
52
53
54



55
56
57
58



59
60
61
62



63
64

65
66
67
68
69
70
71
49
50
51
52
53
54
55

56
57



58
59
60
61



62
63
64
65



66
67
68
69

70
71
72
73
74
75
76
77







-
+

-
-
-
+
+
+

-
-
-
+
+
+

-
-
-
+
+
+

-
+







# "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"
    return "error: /wiki?name=$name $CODE $RESULT"
  }
  set CODE [regexp {href="/info/([0-9a-f]+)"} $RESULT match info]
  if {$CODE == 0} {
    return "error: No info link found for wiki page $name"
  fossil whatis --type w $name
  if {$CODE != 0} {
    return "error: fossil whatis --type w $name $CODE $RESULT"
  }
  fossil http << "GET /artifact/$info"
  if {$CODE != 0} {
    return "error: /artifact/$info $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"
  }
  set CODE [regexp {<pre>(.*)</pre>} $RESULT match pre]
  if {$CODE == 0} {
    return "error: No pre block in /artifact/$info"
  fossil artifact $info
  if {$CODE != 0} {
    return "error: fossil artifact $info $CODE $RESULT"
  }
  set CODE [regexp -line {^N (.*)$} $pre match mimetype]
  set CODE [regexp -line {^N (.*)$} $RESULT match mimetype]
  if {$CODE == 0} {
    return "text/x-fossil-wiki"
  }
  return $mimetype
}


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/email-sender.tcl.

19
20
21
22
23
24
25




26

27
28
29
30
31
32
33
19
20
21
22
23
24
25
26
27
28
29

30
31
32
33
34
35
36
37







+
+
+
+
-
+







    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]
      set out [open |$pipe w]
      puts -nonewline $out $msg
      flush $out
      close $out
      incr n
    }
    if {$n>0} {
      db eval {DELETE FROM email}

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 "$@"

Changes to tools/fossil-autocomplete.bash.

1
2
3
4
5
6
7
8

9
10
11
12


13
14
15
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.
        # 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) )
        # 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;
	}
}

Changes to tools/fossil-stress.tcl.

13
14
15
16
17
18
19
20

21
22
23
24
25
26
27
13
14
15
16
17
18
19

20
21
22
23
24
25
26
27







-
+







  }
  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 argment \"$x\""
    error "unknown argument \"$x\""
  } else {
    set url $x
  }
}
if {![info exists url]} {
  error "Usage: $argv0 [-threads N] URL"
}

Deleted tools/fossil_chat.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

































































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
#!/home/drh/bin/tobe
#
# Simple chat client for Tcl/Tk.
#
package require Tk

set SERVERHOST fossil-scm.hwaci.com
# set SERVERHOST 127.0.0.1
#set SERVERHOST 64.5.53.192
set SERVERPORT 8615

# set to correct values if you have to use a proxy
set PROXYHOST {}
set PROXYPORT {}

# Setup the user interface
wm title . Fossil-Chat
wm iconname . [wm title .]

menu .mb -type menubar
if {$tcl_platform(platform)=="unix" && $tcl_platform(os)!="Darwin"} {
  pack .mb -side top -fill x
} else {
  . config -menu .mb
}
.mb add cascade -label File -underline 0 -menu .mb.file
menu .mb.file -tearoff 0
.mb.file add command -label Send -command send_message
.mb.file add command -label {Remove older messages} -command cleanup_record
.mb.file add separator
.mb.file add command -label {Exit} -command exit

frame .who
pack .who -side right -anchor n -fill y
label .who.title -text {Users:      }
pack .who.title -side top -anchor nw
label .who.list -anchor w -justify left -text {}
pack .who.list -side top -anchor nw -expand 1 -padx 5
label .who.time -text {} -justify right
proc update_time {} {
  after 1000 update_time
  set now [clock seconds]
  set time [clock format $now -format %H:%M -gmt 1]
  .who.time config -text "UTC: $time"
}
update_time
pack .who.time -side bottom -anchor sw

frame .input
pack .input -side bottom -fill x
text .input.t -bd 1 -relief sunken -bg white -fg black -width 60 -height 3 \
   -wrap word -yscrollcommand [list .input.sb set] -takefocus 1
bind .input.t <Key-Return> {send_message; break}
pack .input.t -side left -fill both -expand 1
scrollbar .input.sb -orient vertical -command [list .input.t yview]
pack .input.sb -side left -fill y

frame .msg
pack .msg -side top -fill both -expand 1
text .msg.t -bd 1 -relief sunken -bg white -fg black -width 60 -height 20 \
   -wrap word -yscrollcommand [list .msg.sb set] -takefocus 0
bindtags .msg.t [list .msg.t . all]
.msg.t tag config error -foreground red
.msg.t tag config meta -foreground forestgreen
.msg.t tag config norm -foreground black
pack .msg.t -side left -fill both -expand 1
scrollbar .msg.sb -orient vertical -command [list .msg.t yview]
pack .msg.sb -side left -fill y

update

# Send periodic messages to keep the TCP/IP link up
#
proc keep_alive {} {
  global TIMER SOCKET
  catch {after cancel $TIMER}
  set TIMER [after 300000 keep_alive]
  catch {puts $SOCKET noop; flush $SOCKET}
}

# Connect to the server
proc connect {} {
  global SOCKET tcl_platform
  catch {close $SOCKET}
  if {[catch {
      if {$::PROXYHOST ne {}} {
          set SOCKET [socket $::PROXYHOST $::PROXYPORT]
          puts $SOCKET "CONNECT $::SERVERHOST:$::SERVERPORT HTTP/1.1"
          puts $SOCKET "Host:  $::SERVERHOST:$::SERVERPORT"
          puts $SOCKET ""
      } else {
          set SOCKET [socket $::SERVERHOST $::SERVERPORT]
      }
    fconfigure $SOCKET -translation binary -blocking 0
    puts $SOCKET [list login $tcl_platform(user) fact,fuzz]
    flush $SOCKET
    fileevent $SOCKET readable handle_input
    keep_alive
  } errmsg]} {
    if {[tk_messageBox -icon error -type yesno -parent . -message \
           "Unable to connect to server.  $errmsg.\n\nTry again?"]=="yes"} {
      after 100 connect
    }
  }
}
connect

# Send the message text contained in the .input.t widget to the server.
#
proc send_message {} {
  set txt [.input.t get 1.0 end]
  .input.t delete 1.0 end
  regsub -all "\[ \t\n\f\r\]+" [string trim $txt] { } txt
  if {$txt==""} return
  global SOCKET
  puts $SOCKET [list message $txt]
  flush $SOCKET
}

.mb add cascade -label "Transfer" -underline 0 -menu .mb.files
menu .mb.files -tearoff 0
.mb.files add command -label "Send file..." -command send_file
.mb.files add command -label "Delete files" -command delete_files \
    -state disabled
.mb.files add separator

# Encode a string (possibly containing binary and \000 characters) into
# single line of text.
#
proc encode {txt} {
  return [string map [list % %25 + %2b " " + \n %0a \t %09 \000 %00] $txt]
}

# Undo the work of encode.  Convert an encoded string back into its original
# form.
#
proc decode {txt} {
  return [string map [list %00 \000 %09 \t %0a \n + " " %2b + %25 %] $txt]
}

# Delete all of the downloaded files we are currently holding.
#
proc delete_files {} {
  global FILES
  .mb.files delete 3 end
  array unset FILES
  .mb.files entryconfigure 1 -state disabled
}

# Prompt the user to select a file from the disk.  Then send that
# file to all chat participants.
#
proc send_file {} {
  global SOCKET
  set openfile [tk_getOpenFile]
  if {$openfile==""} return
  set f [open $openfile]
  fconfigure $f -translation binary
  set data [read $f]
  close $f
  puts $SOCKET [list file [file tail $openfile] [encode $data]]
  flush $SOCKET
  set time [clock format [clock seconds] -format {%H:%M} -gmt 1]
  .msg.t insert end "\[$time\] sent file [file tail $openfile]\
        - [string length $data] bytes\n" meta
  .msg.t see end
}

# Save the named file to the disk.
#
 proc save_file {filename} {
  global FILES
  set savefile [tk_getSaveFile -initialfile $filename]
  if {$savefile==""} return
  set f [open $savefile w]
  fconfigure $f -translation binary
  puts -nonewline $f [decode $FILES($filename)]
  close $f
}

# Handle a "file" message from the chat server.
#
proc handle_file {from filename data} {
  global FILES
  foreach prior [array names FILES] {
    if {$filename==$prior} break
  }
  if {![info exists prior] || $filename!=$prior} {
    .mb.files add command -label "Save \"$filename\"" \
        -command [list save_file $filename]
  }
  set FILES($filename) $data
  .mb.files entryconfigure 1 -state active
  set time [clock format [clock seconds] -format {%H:%M} -gmt 1]
  .msg.t insert end "\[$time $from\] " meta "File: \"$filename\"\n" norm
  .msg.t see end
}

# Handle input from the server
#
proc handle_input {} {
  global SOCKET
  if {[eof $SOCKET]} {
    disconnect
    return
  }
  set line [gets $SOCKET]
  if {$line==""} return
  set cmd [lindex $line 0]
  if {$cmd=="userlist"} {
    set ulist {}
    foreach u [lrange $line 1 end] {
      append ulist $u\n
    }
    .who.list config -text [string trim $ulist]
  } elseif {$cmd=="message"} {
    set time [clock format [clock seconds] -format {%H:%M} -gmt 1]
    set from [lindex $line 1]
    .msg.t insert end "\[$time $from\] " meta [lindex $line 2]\n norm
    .msg.t see end
    bell
    wm deiconify .
    update
    raise .
  } elseif {$cmd=="noop"} {
    # do nothing
  } elseif {$cmd=="meta"} {
    set now [clock seconds]
    set time [clock format $now -format {%H:%M} -gmt 1]
    .msg.t insert end "\[$time\] [lindex $line 1]\n" meta
    .msg.t see end
  } elseif {$cmd=="file"} {
    if {[info commands handle_file]=="handle_file"} {
      handle_file [lindex $line 1] [lindex $line 2] [lindex $line 3]
    }
  }
}

# Handle a broken socket connection
#
proc disconnect {} {
  global SOCKET
  close $SOCKET
  set q [tk_messageBox -icon error -type yesno -parent . -message \
           "TCP/IP link lost.  Try to reconnet?"]
  if {$q=="yes"} {
    connect
  } else {
    exit
  }
}

# Remove all but the most recent 100 message from the message log
#
proc cleanup_record {} {
  .msg.t delete 1.0 {end -100 lines}
}

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 &#8220;<code>.h</code>&#8221;
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> &#8220;<code>extern</code>&#8221; 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 &#8220;<code>.h</code>&#8221; suffix and definitions are
placed in &#8220;<code>.c</code>&#8221; files.
The .c files contain &#8220;<code>#include</code>&#8221; 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>&lt;stdio.h&gt</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 &#8220;exportable&#8221;.
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
&#8220;<code>alpha.c</code>&#8221;,
&#8220;<code>beta.c</code>&#8221; and
&#8220;<code>gamma.c</code>&#8221;
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 &#8220;<code>-f</code>&#8221; 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 &#8220;<code>mkhdr.dat</code>&#8221;
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 &#8220;<code>-local</code>&#8221; option causes makeheaders to
generate of prototypes for &#8220;<code>static</code>&#8221; functions and
procedures.
Such prototypes are normally omitted.
</p>

<p>
Finally, makeheaders also includes a &#8220;<code>-doc</code>&#8221; 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 &#8220;<code>--help</code>&#8221; or
&#8220;<code>-?</code>&#8221;)
and it will print a summary of the available options on standard
error.
If you need to process a file whose name begins with
&#8220;<code>-</code>&#8221;,
you can prepend a &#8220;<code>./</code>&#8221; to its name in order to get it
accepted by the command line parser.
Or, you can insert the special option &#8220;<code>--</code>&#8221; on the
command line to cause all subsequent command line arguments to be treated as
filenames even if their names begin with &#8220;<code>-</code>&#8221;.
</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)
&#8220;<code>alpha.c</code>&#8221;
makeheaders will attempt to generate a corresponding header file
named &#8220;<code>alpha.h</code>&#8221;.
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 &#8220;<code>#include</code>&#8221; to the
top of the file that sources the header file that makeheaders will generate.
Hence, the beginning of a source file named &#8220;<code>alpha.c</code>&#8221;
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
&#8220;<code>#if</code>&#8221; 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 &#8220;<code>beta.h</code>&#8221;,
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 &#8220;<code>#if</code>&#8221; 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,
&#8220;<code>*.[ch]</code>&#8221;,
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 &#8220;<code>static</code>&#8221; keyword of C appears at the
beginning of the function definition, the prototype is suppressed.
If you use the &#8220;<code>LOCAL</code>&#8221; keyword where you would normally
say &#8220;<code>static</code>&#8221;, 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 &#8220;<code>LOCAL</code>&#8221; 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 &#8220;<code>-local</code>&#8221;
command-line option, then it treats the &#8220;<code>static</code>&#8221;
keyword like &#8220;<code>LOCAL</code>&#8221; 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
&#8220;<code>extern</code>&#8221;
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 &#8220;<code>struct</code>&#8221;, &#8220;<code>union</code>&#8221; or
&#8220;<code>enum</code>&#8221; 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 &#8220;<code>X</code>&#8221; requires a
prior declaration of another structure &#8220;<code>Y</code>&#8221;, 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
&#8220;<code>#if INTERFACE</code>&#8221; but before the corresponding
&#8220;<code>#endif</code>&#8221; are eligible to be copied into the
automatically generated
.h files of other .c files.
</p>

<p>
If you use the &#8220;<code>#if INTERFACE</code>&#8221; 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
&#8220;<code>#if INTERFACE</code>&#8221; 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
&#8220;<code>#if INTERFACE</code>&#8221; 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 &#8220;<code>export</code>&#8221;.
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 &#8220;<code>static</code>&#8221; 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: &#8220;<code>EXPORT</code>&#8221;
</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 &#8220;<code>#if EXPORT_INTERFACE</code>&#8221; mechanism can be used in
either .c or .h files.
(The &#8220;<code>#if INTERFACE</code>&#8221; 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 &#8220;<code>private</code>&#8221;.
</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 &#8220;<code>LOCAL_INTERFACE</code>&#8221; block works very much like the
&#8220;<code>INTERFACE</code>&#8221; and
&#8220;<code>EXPORT_INTERFACE</code>&#8221;
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 &#8220;<code>class</code>&#8221;
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 &#8220;<code>alpha.cpp</code>&#8221; will induce makeheaders to
generate a header file named &#8220;<code>alpha.hpp</code>&#8221;.
</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&amp;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&amp;R function.
Therefore you should take care to avoid putting K&amp;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. &#8220;<code>enum {X, Y, Z};</code>&#8221;.
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 &#8220;<code>-doc</code>&#8221; 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 &#8220;<code>-doc</code>&#8221; 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: &#8220;<code>#define</code>&#8221;)
</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
     (&#8220;<code>#if</code>&#8221;) 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/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;
}

Deleted win/Makefile.PellesCGMake.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197





































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
#
##############################################################################
# WARNING: DO NOT EDIT, AUTOMATICALLY GENERATED FILE (SEE "src/makemake.tcl")
##############################################################################
#
# This file is automatically generated.  Instead of editing this
# file, edit "makemake.tcl" then run "tclsh makemake.tcl"
# to regenerate this file.
#
# HowTo
# -----
#
# This is a Makefile to compile fossil with PellesC from
#  http://www.smorgasbordet.com/pellesc/index.htm
# In addition to the Compiler envrionment, you need
#  gmake from http://sourceforge.net/projects/unxutils/, Pelles make version
#        couldn't handle the complex dependencies in this build
#  zlib sources
# Then you do
# 1. create a directory PellesC in the project root directory
# 2. Change the variables PellesCDir/ZLIBSRCDIR to the path of your installation
# 3. open a dos prompt window and change working directory into PellesC (step 1)
# 4. run gmake -f ..\win\Makefile.PellesCGMake
#
# this file is tested with
#   PellesC         5.00.13
#   gmake           3.80
#   zlib sources    1.2.5
#   Windows XP SP 2
# and
#   PellesC         6.00.4
#   gmake           3.80
#   zlib sources    1.2.5
#   Windows 7 Home Premium
#

#
PellesCDir=c:\Programme\PellesC

# Select between 32/64 bit code, default is 32 bit
#TARGETVERSION=64

ifeq ($(TARGETVERSION),64)
# 64 bit version
TARGETMACHINE_CC=amd64
TARGETMACHINE_LN=amd64
TARGETEXTEND=64
else
# 32 bit version
TARGETMACHINE_CC=x86
TARGETMACHINE_LN=ix86
TARGETEXTEND=
endif

# define the project directories
B=..
SRCDIR=$(B)/src/
WINDIR=$(B)/win/
ZLIBSRCDIR=../../zlib/

# define linker command and options
LINK=$(PellesCDir)/bin/polink.exe
LINKFLAGS=-subsystem:console -machine:$(TARGETMACHINE_LN) /LIBPATH:$(PellesCDir)\lib\win$(TARGETEXTEND) /LIBPATH:$(PellesCDir)\lib kernel32.lib advapi32.lib delayimp$(TARGETEXTEND).lib Wsock32.lib dnsapi.lib Crtmt$(TARGETEXTEND).lib

# define standard C-compiler and flags, used to compile
# the fossil binary. Some special definitions follow for
# special files follow
CC=$(PellesCDir)\bin\pocc.exe
DEFINES=-D_pgmptr=g.argv[0]
CCFLAGS=-T$(TARGETMACHINE_CC)-coff -Ot -W2 -Gd -Go -Ze -MT $(DEFINES)
INCLUDE=/I $(PellesCDir)\Include\Win /I $(PellesCDir)\Include /I $(ZLIBSRCDIR) /I $(SRCDIR)

# define commands for building the windows resource files
RESOURCE=fossil.res
RC=$(PellesCDir)\bin\porc.exe
RCFLAGS=$(INCLUDE) -D__POCC__=1 -D_M_X$(TARGETVERSION)

# define the special utilities files, needed to generate
# the automatically generated source files
UTILS=translate.exe mkindex.exe makeheaders.exe mkbuiltin.exe
UTILS_OBJ=$(UTILS:.exe=.obj)
UTILS_SRC=$(foreach uf,$(UTILS),$(SRCDIR)$(uf:.exe=.c))

# define the SQLite files, which need special flags on compile
SQLITESRC=sqlite3.c
ORIGSQLITESRC=$(foreach sf,$(SQLITESRC),$(SRCDIR)$(sf))
SQLITEOBJ=$(foreach sf,$(SQLITESRC),$(sf:.c=.obj))
SQLITEDEFINES=-DNDEBUG=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_GET_TABLE -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_WIN32_NO_ANSI

# define the SQLite shell files, which need special flags on compile
SQLITESHELLSRC=shell.c
ORIGSQLITESHELLSRC=$(foreach sf,$(SQLITESHELLSRC),$(SRCDIR)$(sf))
SQLITESHELLOBJ=$(foreach sf,$(SQLITESHELLSRC),$(sf:.c=.obj))
SQLITESHELLDEFINES=-DNDEBUG=1 -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_GET_TABLE -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -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

# define the th scripting files, which need special flags on compile
THSRC=th.c th_lang.c
ORIGTHSRC=$(foreach sf,$(THSRC),$(SRCDIR)$(sf))
THOBJ=$(foreach sf,$(THSRC),$(sf:.c=.obj))

# define the zlib files, needed by this compile
ZLIBSRC=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
ORIGZLIBSRC=$(foreach sf,$(ZLIBSRC),$(ZLIBSRCDIR)$(sf))
ZLIBOBJ=$(foreach sf,$(ZLIBSRC),$(sf:.c=.obj))

# define all fossil sources, using the standard compile and
# source generation. These are all files in SRCDIR, which are not
# mentioned as special files above:
ORIGSRC=$(filter-out $(UTILS_SRC) $(ORIGTHSRC) $(ORIGSQLITESRC) $(ORIGSQLITESHELLSRC),$(wildcard $(SRCDIR)*.c))
SRC=$(subst $(SRCDIR),,$(ORIGSRC))
TRANSLATEDSRC=$(SRC:.c=_.c)
TRANSLATEDOBJ=$(TRANSLATEDSRC:.c=.obj)

# main target file is the application
APPLICATION=fossil.exe

# define the standard make target
.PHONY:	default
default:	page_index.h builtin_data.h headers $(APPLICATION)

# symbolic target to generate the source generate utils
.PHONY:	utils
utils:	$(UTILS)

# link utils
$(UTILS) version.exe:	%.exe:	%.obj
	$(LINK) $(LINKFLAGS) -out:"$@" $<

# compiling standard fossil utils
$(UTILS_OBJ):	%.obj:	$(SRCDIR)%.c
	$(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@"

# compile special windows utils
version.obj:	$(SRCDIR)mkversion.c
	$(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@"

# generate the translated c-source files
$(TRANSLATEDSRC):	%_.c:	$(SRCDIR)%.c translate.exe
	translate.exe $< >$@

# generate the index source, containing all web references,..
page_index.h:	$(TRANSLATEDSRC) mkindex.exe
	mkindex.exe $(TRANSLATEDSRC) >$@

builtin_data.h:	$(EXTRA_FILES) mkbuiltin.exe
	mkbuiltin.exe --prefix $(SRCDIR)/ $(EXTRA_FILES) >$@

# extracting version info from manifest
VERSION.h:	version.exe ..\manifest.uuid ..\manifest ..\VERSION
	version.exe ..\manifest.uuid ..\manifest ..\VERSION  >$@

default_css.h:	mkcss.exe default_css.txt
	mkcss.exe default_css.txt $@

# generate the simplified headers
headers: makeheaders.exe page_index.h builtin_data.h default_css.h VERSION.h ../src/sqlite3.h ../src/th.h
	makeheaders.exe $(foreach ts,$(TRANSLATEDSRC),$(ts):$(ts:_.c=.h)) ../src/sqlite3.h ../src/th.h VERSION.h
	echo Done >$@

# compile C sources with relevant options

$(TRANSLATEDOBJ):	%_.obj:	%_.c %.h
	$(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@"

$(SQLITEOBJ):	%.obj:	$(SRCDIR)%.c $(SRCDIR)%.h
	$(CC) $(CCFLAGS) $(SQLITEDEFINES) $(INCLUDE) "$<" -Fo"$@"

$(SQLITESHELLOBJ):	%.obj:	$(SRCDIR)%.c
	$(CC) $(CCFLAGS) $(SQLITESHELLDEFINES) $(INCLUDE) "$<" -Fo"$@"

$(THOBJ):	%.obj:	$(SRCDIR)%.c $(SRCDIR)th.h
	$(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@"

$(ZLIBOBJ):	%.obj:	$(ZLIBSRCDIR)%.c
	$(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@"

# create the windows resource with icon and version info
$(RESOURCE):	%.res:	../win/%.rc ../win/*.ico
	$(RC) $(RCFLAGS) $< -Fo"$@"

# link the application
$(APPLICATION):	$(TRANSLATEDOBJ) $(SQLITEOBJ) $(SQLITESHELLOBJ) $(THOBJ) $(ZLIBOBJ) headers $(RESOURCE)
	$(LINK) $(LINKFLAGS) -out:"$@" $(TRANSLATEDOBJ) $(SQLITEOBJ) $(SQLITESHELLOBJ) $(THOBJ) $(ZLIBOBJ) $(RESOURCE)

# cleanup

.PHONY: clean
clean:
	-del /F $(TRANSLATEDOBJ) $(SQLITEOBJ) $(THOBJ) $(ZLIBOBJ) $(UTILS_OBJ) version.obj
	-del /F $(TRANSLATEDSRC)
	-del /F *.h headers
	-del /F $(RESOURCE)

.PHONY: clobber
clobber: clean
	-del /F *.exe

Changes to 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
1
2

3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

21
22
23
24
25
26
27
28
29
30

31
32

33
34
35
36

37
38

39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57

58
59
60
61
62
63
64

65
66
67

68
69
70

71
72
73

74
75
76

77
78
79




80
81
82

83
84
85

86
87
88
89
90
91
92
93
94

95
96
97
98
99
100



101
102
103
104
105
106
107
108
109
110
111

112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130

131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172


-
+








+
+







-
+









-
+

-
+

+
+
-
+

-
+


















-
+






-
+


-
+


-
+


-
+


-
+


-
-
-
-
+


-
+


-
+








-
+





-
-
-











-
+


















-





+
+
+
+
+
+


















+
+
+
+
+
+







#
##############################################################################
# WARNING: DO NOT EDIT, AUTOMATICALLY GENERATED FILE (SEE "src/makemake.tcl")
# 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$B\win\include -I$(DMDIR)\extra\include
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_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_GET_TABLE -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB
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_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_GET_TABLE -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -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
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 alerts_.c allrepo_.c attach_.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 checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c file_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c glob_.c graph_.c gzip_.c hname_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c piechart_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c report_.c rss_.c schema_.c search_.c 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 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 webmail_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c
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)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$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)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$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)\file$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\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)\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)\webmail$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\wysiwyg$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O
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 alerts allrepo attach backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi checkin checkout clearsign clone comformat configure content cookies db delta deltacmd descendants diff diffcmd dispatch doc encode etag event export file finfo foci forum fshell fusefs glob graph gzip hname http http_socket http_ssl http_transport import info json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name path piechart pivot popen pqueue printf publish purge rebuild regexp report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile webmail wiki wikiformat winfile winhttp wysiwyg xfer xfersetup zip shell sqlite3 th th_lang > $@
	+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)\translate.c
translate$E: $(SRCDIR_tools)\translate.c
	$(BCC) -o$@ $**

makeheaders$E: $(SRCDIR)\makeheaders.c
makeheaders$E: $(SRCDIR_tools)\makeheaders.c
	$(BCC) -o$@ $**

mkindex$E: $(SRCDIR)\mkindex.c
mkindex$E: $(SRCDIR_tools)\mkindex.c
	$(BCC) -o$@ $**

mkbuiltin$E: $(SRCDIR)\mkbuiltin.c
mkbuiltin$E: $(SRCDIR_tools)\mkbuiltin.c
	$(BCC) -o$@ $**

mkversion$E: $(SRCDIR)\mkversion.c
mkversion$E: $(SRCDIR_tools)\mkversion.c
	$(BCC) -o$@ $**

mkcss$E: $(SRCDIR)\mkcss.c
	$(BCC) -o$@ $**

codecheck1$E: $(SRCDIR)\codecheck1.c
codecheck1$E: $(SRCDIR_tools)\codecheck1.c
	$(BCC) -o$@ $**

$(OBJDIR)\shell$O : $(SRCDIR)\shell.c
$(OBJDIR)\shell$O : $(SRCDIR_extsrc)\shell.c
	$(TCC) -o$@ -c $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) $**

$(OBJDIR)\sqlite3$O : $(SRCDIR)\sqlite3.c
$(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)\cson_amalgamation.h
$(OBJDIR)\cson_amalgamation.h : $(SRCDIR_extsrc)\cson_amalgamation.h
	cp $@ $@

VERSION.h : mkversion$E $B\manifest.uuid $B\manifest $B\VERSION
	+$** > $@

default_css.h : mkcss$E $B\src\default_css.txt
	+$** $B\src\default_css.txt $@

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 mkcss$E
	-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 $** > $@

222
223
224
225
226
227
228






229
230
231
232
233
234
235
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250







+
+
+
+
+
+







	+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 $** > $@

246
247
248
249
250
251
252






253
254
255
256
257
258
259
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280







+
+
+
+
+
+







	+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 $** > $@

288
289
290
291
292
293
294






295
296
297
298
299
300
301
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328







+
+
+
+
+
+







	+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 $** > $@

342
343
344
345
346
347
348






349
350
351
352
353
354






355
356
357
358
359
360
361
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400







+
+
+
+
+
+






+
+
+
+
+
+







	+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 $** > $@

378
379
380
381
382
383
384






385
386
387
388
389
390
391
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436







+
+
+
+
+
+







	+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 $** > $@

402
403
404
405
406
407
408






409
410
411
412
413
414
415
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466







+
+
+
+
+
+







	+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 $** > $@

438
439
440
441
442
443
444






445
446
447
448
449
450
451
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508







+
+
+
+
+
+







	+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 $** > $@

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
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







+
+
+
+
+
+












+
+
+
+
+
+







	+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 $** > $@

666
667
668
669
670
671
672






673
674
675
676
677
678
679
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754







+
+
+
+
+
+







	+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 $** > $@

798
799
800
801
802
803
804






805
806
807
808
809
810
811
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892







+
+
+
+
+
+







	+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 $** > $@

883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
964
965
966
967
968
969
970






971
972
973
974
975
976
977







-
-
-
-
-
-








$(OBJDIR)\vfile$O : vfile_.c vfile.h
	$(TCC) -o$@ -c vfile_.c

vfile_.c : $(SRCDIR)\vfile.c
	+translate$E $** > $@

$(OBJDIR)\webmail$O : webmail_.c webmail.h
	$(TCC) -o$@ -c webmail_.c

webmail_.c : $(SRCDIR)\webmail.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
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
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







-
-
-
-
-
-


















-
-
+
+


$(OBJDIR)\winhttp$O : winhttp_.c winhttp.h
	$(TCC) -o$@ -c winhttp_.c

winhttp_.c : $(SRCDIR)\winhttp.c
	+translate$E $** > $@

$(OBJDIR)\wysiwyg$O : wysiwyg_.c wysiwyg.h
	$(TCC) -o$@ -c wysiwyg_.c

wysiwyg_.c : $(SRCDIR)\wysiwyg.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 default_css.h VERSION.h
	 +makeheaders$E add_.c:add.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.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 checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h cookies_.c:cookies.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h dispatch_.c:dispatch.h doc_.c:doc.h encode_.c:encode.h etag_.c:etag.h event_.c:event.h export_.c:export.h file_.c:file.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.h piechart_.c:piechart.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h 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 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 webmail_.c:webmail.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h wysiwyg_.c:wysiwyg.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h
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

Changes to win/Makefile.mingw.

1
2
3
4

5
6
7
8
9
10
11
1
2
3

4
5
6
7
8
9
10
11



-
+







#!/usr/bin/make
#
##############################################################################
# WARNING: DO NOT EDIT, AUTOMATICALLY GENERATED FILE (SEE "src/makemake.tcl")
# 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
26
27
28
29
30
31
32


33
34
35
36
37
38
39
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41







+
+







# 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
50
51
52
53
54
55
56
57

58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
52
53
54
55
56
57
58

59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75




76
77
78
79
80
81
82







-
+
















-
-
-
-







#
BCC = $(BCCEXE)

#### Enable compiling with debug symbols (much larger binary)
#
# FOSSIL_ENABLE_SYMBOLS = 1

#### Enable JSON (http://www.json.org) support using "cson"
#### 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 legacy treatment of mv/rm (skip checkout files)
#
FOSSIL_ENABLE_LEGACY_MV_RM = 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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
101
102
103
104
105
106
107




108
109
110
111
112
113
114







-
-
-
-







#
# USE_MMAN_H = 1

#### Use the SQLite Encryption Extension
#
# USE_SEE = 1

#### Use the miniz compression library
#
# FOSSIL_ENABLE_MINIZ = 1

#### Use the Tcl source directory instead of the install directory?
#    This is useful when Tcl has been compiled statically with MinGW.
#
FOSSIL_TCL_SOURCE = 1

#### Check if the workaround for the MinGW command line handling needs to
#    be enabled by default.  This check may be somewhat fragile due to the
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163

164
165

166
167
168
169
170
171
172
173
174
175
176
177
178
179

180
181
182
183
184
185
186
136
137
138
139
140
141
142




143
144

145
146
147
148
149
150
151

152
153

154
155
156
157
158

159

160
161



162

163
164
165
166
167
168
169
170







-
-
-
-


-







-
+

-
+




-

-


-
-
-

-
+








#### Determine if the optimized assembly routines provided with zlib should be
#    used, taking into account whether zlib is actually enabled and the target
#    processor architecture.
#
ifndef X64
SSLCONFIG = mingw
ifndef FOSSIL_ENABLE_MINIZ
ZLIBCONFIG = LOC="-DASMV -DASMINF" OBJA="inffas86.o match.o"
ZLIBTARGETS = $(ZLIBDIR)/inffas86.o $(ZLIBDIR)/match.o
else
ZLIBCONFIG =
ZLIBTARGETS =
endif
else
SSLCONFIG = mingw64
ZLIBCONFIG =
ZLIBTARGETS =
endif

#### Disable creation of the OpenSSL shared libraries.  Also, disable support
#    for both SSLv2 and SSLv3 (i.e. thereby forcing the use of TLS).
#    for SSLv3 (i.e. thereby forcing the use of TLS).
#
SSLCONFIG += no-ssl2 no-ssl3 no-weak-ssl-ciphers no-shared
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).
#
ifndef FOSSIL_ENABLE_MINIZ
SSLCONFIG +=  --with-zlib-lib=$(PWD)/$(ZLIBDIR) --with-zlib-include=$(PWD)/$(ZLIBDIR) zlib
endif

#### The directories where the OpenSSL include and library files are located.
#    The recommended usage here is to use the Sysinternals junction tool
#    to create a hard link between an "openssl-1.x" sub-directory of the
#    Fossil source code directory and the target OpenSSL source directory.
#
OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.0.2p
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
228
229
230
231
232
233
234

235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254

255
256
257

258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229



230

231
232
233
234

235



236

237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254






255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272






273
274
275
276
277
278
279







+










-
-
-

-




-
+
-
-
-
+
-


















-
-
-
-
-
-


















-
-
-
-
-
-







#### 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

#### When not using the miniz compression library, zlib is required.
#
ifndef FOSSIL_ENABLE_MINIZ
TCC += -L$(ZLIBDIR) -I$(ZINCDIR)
endif

#### Compile resources for use in building executables that will run
#    on the target platform.
#
RCC = $(PREFIX)windres -I$(SRCDIR)
RCC = $(PREFIX)windres -I$(SRCDIR) -I$(ZINCDIR)

ifndef FOSSIL_ENABLE_MINIZ
RCC += -I$(ZINCDIR)
RCC += -I$(SRCDIR_extsrc)
endif

# With HTTPS support
ifdef FOSSIL_ENABLE_SSL
TCC += -L$(OPENSSLLIBDIR) -I$(OPENSSLINCDIR)
RCC += -I$(OPENSSLINCDIR)
endif

# With Tcl support
ifdef FOSSIL_ENABLE_TCL
ifdef FOSSIL_TCL_SOURCE
TCC += -L$(TCLSRCDIR)/win -I$(TCLSRCDIR)/generic -I$(TCLSRCDIR)/win
RCC += -I$(TCLSRCDIR)/generic -I$(TCLSRCDIR)/win
else
TCC += -L$(TCLLIBDIR) -I$(TCLINCDIR)
RCC += -I$(TCLINCDIR)
endif
endif

# With miniz (i.e. instead of zlib)
ifdef FOSSIL_ENABLE_MINIZ
TCC += -DFOSSIL_ENABLE_MINIZ=1
RCC += -DFOSSIL_ENABLE_MINIZ=1
endif

# With MinGW command line handling workaround
ifdef MINGW_IS_32BIT_ONLY
TCC += -DBROKEN_MINGW_CMDLINE=1
RCC += -DBROKEN_MINGW_CMDLINE=1
endif

# With HTTPS support
ifdef FOSSIL_ENABLE_SSL
TCC += -DFOSSIL_ENABLE_SSL=1
RCC += -DFOSSIL_ENABLE_SSL=1
endif

# With 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 legacy treatment of mv/rm
ifdef FOSSIL_ENABLE_LEGACY_MV_RM
TCC += -DFOSSIL_ENABLE_LEGACY_MV_RM=1
RCC += -DFOSSIL_ENABLE_LEGACY_MV_RM=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
387
388
389
390
391
392
393
394

395
396
397
398
399
400
401
402
403
404
405
353
354
355
356
357
358
359

360
361

362

363
364
365
366
367
368
369







-
+

-

-








#### Extra arguments for linking the finished binary.  Fossil needs
#    to link against the Z-Lib compression library.  There are no
#    other mandatory dependencies.
#
LIB += -lmingwex

#### When not using the miniz compression library, zlib is required.
#### zlib is required.
#
ifndef FOSSIL_ENABLE_MINIZ
LIB += -lz
endif

#### These libraries MUST appear in the same order as they do for Tcl
#    or linking with it will not work (exact reason unknown).
#
ifdef FOSSIL_ENABLE_TCL
ifdef FOSSIL_ENABLE_TCL_STUBS
LIB += -lkernel32 -lws2_32
435
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
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
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







+



+












+




+







+









+

+





+




+






+







# 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 \
514
515
516
517
518
519
520

521
522

523
524
525
526
527
528
529
530

531
532
533
534
535
536
537
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







+


+








+







  $(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 \
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
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







+














-




-





+
-
+
+













-
-
-
-
-
-
-
-
-
+
+
+
+




-




-
-
-
-
+
+
+
+












-
-
-
-




+
+
+
+
+

+
+
+


+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+




-



+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+






+



+












+




+







+









+

+





+




+






+







  $(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)/webmail.c \
  $(SRCDIR)/wiki.c \
  $(SRCDIR)/wikiformat.c \
  $(SRCDIR)/winfile.c \
  $(SRCDIR)/winhttp.c \
  $(SRCDIR)/wysiwyg.c \
  $(SRCDIR)/xfer.c \
  $(SRCDIR)/xfersetup.c \
  $(SRCDIR)/zip.c

EXTRA_FILES = \
  $(SRCDIR)/../extsrc/pikchr-worker.js \
  $(SRCDIR)/../skins/aht/details.txt \
  $(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/blitz_no_logo/css.txt \
  $(SRCDIR)/../skins/blitz_no_logo/details.txt \
  $(SRCDIR)/../skins/blitz_no_logo/footer.txt \
  $(SRCDIR)/../skins/blitz_no_logo/header.txt \
  $(SRCDIR)/../skins/blitz_no_logo/ticket.txt \
  $(SRCDIR)/../skins/bootstrap/css.txt \
  $(SRCDIR)/../skins/bootstrap/details.txt \
  $(SRCDIR)/../skins/bootstrap/footer.txt \
  $(SRCDIR)/../skins/bootstrap/header.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/default/js.txt \
  $(SRCDIR)/../skins/eagle/css.txt \
  $(SRCDIR)/../skins/eagle/details.txt \
  $(SRCDIR)/../skins/eagle/footer.txt \
  $(SRCDIR)/../skins/eagle/header.txt \
  $(SRCDIR)/../skins/enhanced1/css.txt \
  $(SRCDIR)/../skins/enhanced1/details.txt \
  $(SRCDIR)/../skins/enhanced1/footer.txt \
  $(SRCDIR)/../skins/enhanced1/header.txt \
  $(SRCDIR)/../skins/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/rounded1/css.txt \
  $(SRCDIR)/../skins/rounded1/details.txt \
  $(SRCDIR)/../skins/rounded1/footer.txt \
  $(SRCDIR)/../skins/rounded1/header.txt \
  $(SRCDIR)/../skins/xekri/css.txt \
  $(SRCDIR)/../skins/xekri/details.txt \
  $(SRCDIR)/../skins/xekri/footer.txt \
  $(SRCDIR)/../skins/xekri/header.txt \
  $(SRCDIR)/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)/sbsdiff.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 \
724
725
726
727
728
729
730

731
732

733
734
735
736
737
738
739
740

741
742
743
744
745
746
747
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







+


+








+







  $(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 \
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
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







+














-




-






+



+












+




+







+









+

+





+




+






+







  $(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)/webmail_.c \
  $(OBJDIR)/wiki_.c \
  $(OBJDIR)/wikiformat_.c \
  $(OBJDIR)/winfile_.c \
  $(OBJDIR)/winhttp_.c \
  $(OBJDIR)/wysiwyg_.c \
  $(OBJDIR)/xfer_.c \
  $(OBJDIR)/xfersetup_.c \
  $(OBJDIR)/zip_.c

OBJ = \
 $(OBJDIR)/add.o \
 $(OBJDIR)/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 \
861
862
863
864
865
866
867

868
869

870
871
872
873
874
875
876
877

878
879
880
881
882
883
884
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







+


+








+







 $(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 \
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
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







+














-




-




















-














-












-
+

-




-







 $(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)/webmail.o \
 $(OBJDIR)/wiki.o \
 $(OBJDIR)/wikiformat.o \
 $(OBJDIR)/winfile.o \
 $(OBJDIR)/winhttp.o \
 $(OBJDIR)/wysiwyg.o \
 $(OBJDIR)/xfer.o \
 $(OBJDIR)/xfersetup.o \
 $(OBJDIR)/zip.o

APPNAME    = fossil.exe
APPTARGETS =

#### If the USE_WINDOWS variable exists, it is assumed that we are building
#    inside of a Windows-style shell; otherwise, it is assumed that we are
#    building inside of a Unix-style shell.  Note that the "move" command is
#    broken when attempting to use it from the Windows shell via MinGW make
#    because the SHELL variable is only used for certain commands that are
#    recognized internally by make.
#
ifdef USE_WINDOWS
TRANSLATE   = $(subst /,\,$(OBJDIR)/translate.exe)
MAKEHEADERS = $(subst /,\,$(OBJDIR)/makeheaders.exe)
MKINDEX     = $(subst /,\,$(OBJDIR)/mkindex.exe)
MKBUILTIN   = $(subst /,\,$(OBJDIR)/mkbuiltin.exe)
MKVERSION   = $(subst /,\,$(OBJDIR)/mkversion.exe)
MKCSS       = $(subst /,\,$(OBJDIR)/mkcss.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
MKCSS       = $(OBJDIR)/mkcss.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 $(OBJDIR)/default_css.h
$(OBJDIR)/fossil.o:	$(SRCDIR)/../win/fossil.rc $(OBJDIR)/VERSION.h
ifdef USE_WINDOWS
	$(CAT) $(subst /,\,$(SRCDIR)\miniz.c) | $(GREP) "define MZ_VERSION" > $(subst /,\,$(OBJDIR)\minizver.h)
	$(CP) $(subst /,\,$(SRCDIR)\..\win\fossil.rc) $(subst /,\,$(OBJDIR))
	$(CP) $(subst /,\,$(SRCDIR)\..\win\fossil.ico) $(subst /,\,$(OBJDIR))
	$(CP) $(subst /,\,$(SRCDIR)\..\win\fossil.exe.manifest) $(subst /,\,$(OBJDIR))
else
	$(CAT) $(SRCDIR)/miniz.c | $(GREP) "define MZ_VERSION" > $(OBJDIR)/minizver.h
	$(CP) $(SRCDIR)/../win/fossil.rc $(OBJDIR)
	$(CP) $(SRCDIR)/../win/fossil.ico $(OBJDIR)
	$(CP) $(SRCDIR)/../win/fossil.exe.manifest $(OBJDIR)
endif
	$(RCC) $(OBJDIR)/fossil.rc -o $(OBJDIR)/fossil.o

install:	$(OBJDIR) $(APPNAME)
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
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







-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
+
-
-
-
-
+

-
-
+
+







-
+


-
-
+
+

-
-
+
+


+
+
+
+

-
+
+

-
+
-
-
-
-
-
-

-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+







-
-
+
+







-
-
-
-
-
-






-
-
-

-



+


















-
+

-
+







$(OBJDIR):
ifdef USE_WINDOWS
	$(MKDIR) $(subst /,\,$(OBJDIR))
else
	$(MKDIR) $(OBJDIR)
endif

$(TRANSLATE):	$(SRCDIR)/translate.c
	$(XBCC) -o $@ $(SRCDIR)/translate.c
$(TRANSLATE):	$(SRCDIR_tools)/translate.c
	$(XBCC) -o $@ $(SRCDIR_tools)/translate.c

$(MAKEHEADERS):	$(SRCDIR)/makeheaders.c
	$(XBCC) -o $@ $(SRCDIR)/makeheaders.c
$(MAKEHEADERS):	$(SRCDIR_tools)/makeheaders.c
	$(XBCC) -o $@ $(SRCDIR_tools)/makeheaders.c

$(MKINDEX):	$(SRCDIR)/mkindex.c
	$(XBCC) -o $@ $(SRCDIR)/mkindex.c
$(MKINDEX):	$(SRCDIR_tools)/mkindex.c
	$(XBCC) -o $@ $(SRCDIR_tools)/mkindex.c

$(MKBUILTIN):	$(SRCDIR)/mkbuiltin.c
	$(XBCC) -o $@ $(SRCDIR)/mkbuiltin.c
$(MKBUILTIN):	$(SRCDIR_tools)/mkbuiltin.c
	$(XBCC) -o $@ $(SRCDIR_tools)/mkbuiltin.c

$(MKVERSION): $(SRCDIR)/mkversion.c
$(MKVERSION): $(SRCDIR_tools)/mkversion.c
	$(XBCC) -o $@ $(SRCDIR)/mkversion.c

$(MKCSS): $(SRCDIR)/mkcss.c
	$(XBCC) -o $@ $(SRCDIR)/mkcss.c
	$(XBCC) -o $@ $(SRCDIR_tools)/mkversion.c

$(CODECHECK1):	$(SRCDIR)/codecheck1.c
	$(XBCC) -o $@ $(SRCDIR)/codecheck1.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)/VERSION.h:	$(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(MKVERSION) $(OBJDIR)/phony.h
	$(MKVERSION) $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION >$@

$(OBJDIR)/default_css.h:	$(SRCDIR)/default_css.txt $(MKCSS)
	$(MKCSS) $(SRCDIR)/default_css.txt $@
$(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 set
# to 1. If it is set to 1, then there is no need to build or link
# 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 =
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 FOSSIL_ENABLE_MINIZ variable may be undefined, set to 0, or
# set to 1.  If it is set to 1, the miniz library included in the
# source tree should be used; otherwise, it should not.
MINIZ_OBJ.0 =
MINIZ_OBJ.1 = $(OBJDIR)/miniz.o
MINIZ_OBJ.  = $(MINIZ_OBJ.0)

# The USE_SEE variable may be undefined, 0 or 1.  If undefined or
# 0, ordinary 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 = sqlite3.c
SQLITE3_SRC.1 = sqlite3-see.c
SQLITE3_SRC. = sqlite3.c
SQLITE3_SRC = $(SRCDIR)/$(SQLITE3_SRC.$(USE_SEE))
SQLITE3_SHELL_SRC.0 = shell.c
SQLITE3_SHELL_SRC.1 = shell-see.c
SQLITE3_SHELL_SRC. = shell.c
SQLITE3_SHELL_SRC = $(SRCDIR)/$(SQLITE3_SHELL_SRC.$(USE_SEE))
# 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.$(USE_SYSTEM_SQLITE)) \
 $(MINIZ_OBJ.$(FOSSIL_ENABLE_MINIZ)) \
 $(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


$(ZLIBDIR)/inffas86.o:
	$(TCC) -c -o $@ -DASMINF -I$(ZLIBDIR) -O3 $(ZLIBDIR)/contrib/inflate86/inffas86.c

$(ZLIBDIR)/match.o:
	$(TCC) -c -o $@ -DASMV $(ZLIBDIR)/contrib/asm686/match.S

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

ifdef FOSSIL_ENABLE_MINIZ
BLDTARGETS =
else
BLDTARGETS = zlib
endif

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) $(OBJ) $(EXTRAOBJ) $(OBJDIR)/fossil.o
$(APPNAME):	$(APPTARGETS) $(OBJDIR)/headers $(CODECHECK1) $(EXTRAOBJ) $(OBJ) $(OBJDIR)/fossil.o
	$(CODECHECK1) $(TRANS_SRC)
	$(TCC) -o $@ $(OBJ) $(EXTRAOBJ) $(OBJDIR)/fossil.o $(LIB)
	$(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

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
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







-
+

+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+













+
+
+
+
+
+
+
+








$(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 $(OBJDIR)/default_css.h $(MAKEHEADERS) $(OBJDIR)/VERSION.h
$(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)/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)/checkin_.c:$(OBJDIR)/checkin.h \
		$(OBJDIR)/checkout_.c:$(OBJDIR)/checkout.h \
		$(OBJDIR)/clearsign_.c:$(OBJDIR)/clearsign.h \
		$(OBJDIR)/clone_.c:$(OBJDIR)/clone.h \
		$(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h \
		$(OBJDIR)/configure_.c:$(OBJDIR)/configure.h \
		$(OBJDIR)/content_.c:$(OBJDIR)/content.h \
		$(OBJDIR)/cookies_.c:$(OBJDIR)/cookies.h \
		$(OBJDIR)/db_.c:$(OBJDIR)/db.h \
		$(OBJDIR)/delta_.c:$(OBJDIR)/delta.h \
		$(OBJDIR)/deltacmd_.c:$(OBJDIR)/deltacmd.h \
		$(OBJDIR)/descendants_.c:$(OBJDIR)/descendants.h \
		$(OBJDIR)/diff_.c:$(OBJDIR)/diff.h \
		$(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h \
		$(OBJDIR)/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)/file_.c:$(OBJDIR)/file.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)/glob_.c:$(OBJDIR)/glob.h \
		$(OBJDIR)/graph_.c:$(OBJDIR)/graph.h \
		$(OBJDIR)/gzip_.c:$(OBJDIR)/gzip.h \
		$(OBJDIR)/hname_.c:$(OBJDIR)/hname.h \
		$(OBJDIR)/http_.c:$(OBJDIR)/http.h \
		$(OBJDIR)/http_socket_.c:$(OBJDIR)/http_socket.h \
		$(OBJDIR)/http_ssl_.c:$(OBJDIR)/http_ssl.h \
		$(OBJDIR)/http_transport_.c:$(OBJDIR)/http_transport.h \
		$(OBJDIR)/import_.c:$(OBJDIR)/import.h \
		$(OBJDIR)/info_.c:$(OBJDIR)/info.h \
		$(OBJDIR)/json_.c:$(OBJDIR)/json.h \
		$(OBJDIR)/json_artifact_.c:$(OBJDIR)/json_artifact.h \
		$(OBJDIR)/json_branch_.c:$(OBJDIR)/json_branch.h \
		$(OBJDIR)/json_config_.c:$(OBJDIR)/json_config.h \
		$(OBJDIR)/json_diff_.c:$(OBJDIR)/json_diff.h \
		$(OBJDIR)/json_dir_.c:$(OBJDIR)/json_dir.h \
		$(OBJDIR)/json_finfo_.c:$(OBJDIR)/json_finfo.h \
		$(OBJDIR)/json_login_.c:$(OBJDIR)/json_login.h \
		$(OBJDIR)/json_query_.c:$(OBJDIR)/json_query.h \
		$(OBJDIR)/json_report_.c:$(OBJDIR)/json_report.h \
		$(OBJDIR)/json_status_.c:$(OBJDIR)/json_status.h \
		$(OBJDIR)/json_tag_.c:$(OBJDIR)/json_tag.h \
		$(OBJDIR)/json_timeline_.c:$(OBJDIR)/json_timeline.h \
		$(OBJDIR)/json_user_.c:$(OBJDIR)/json_user.h \
		$(OBJDIR)/json_wiki_.c:$(OBJDIR)/json_wiki.h \
		$(OBJDIR)/leaf_.c:$(OBJDIR)/leaf.h \
		$(OBJDIR)/loadctrl_.c:$(OBJDIR)/loadctrl.h \
		$(OBJDIR)/login_.c:$(OBJDIR)/login.h \
		$(OBJDIR)/lookslike_.c:$(OBJDIR)/lookslike.h \
		$(OBJDIR)/main_.c:$(OBJDIR)/main.h \
		$(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h \
		$(OBJDIR)/markdown_.c:$(OBJDIR)/markdown.h \
		$(OBJDIR)/markdown_html_.c:$(OBJDIR)/markdown_html.h \
		$(OBJDIR)/md5_.c:$(OBJDIR)/md5.h \
		$(OBJDIR)/merge_.c:$(OBJDIR)/merge.h \
		$(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h \
		$(OBJDIR)/moderate_.c:$(OBJDIR)/moderate.h \
		$(OBJDIR)/name_.c:$(OBJDIR)/name.h \
		$(OBJDIR)/path_.c:$(OBJDIR)/path.h \
		$(OBJDIR)/piechart_.c:$(OBJDIR)/piechart.h \
		$(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h \
		$(OBJDIR)/popen_.c:$(OBJDIR)/popen.h \
		$(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h \
		$(OBJDIR)/printf_.c:$(OBJDIR)/printf.h \
		$(OBJDIR)/publish_.c:$(OBJDIR)/publish.h \
		$(OBJDIR)/purge_.c:$(OBJDIR)/purge.h \
		$(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h \
		$(OBJDIR)/regexp_.c:$(OBJDIR)/regexp.h \
		$(OBJDIR)/report_.c:$(OBJDIR)/report.h \
		$(OBJDIR)/rss_.c:$(OBJDIR)/rss.h \
		$(OBJDIR)/schema_.c:$(OBJDIR)/schema.h \
		$(OBJDIR)/search_.c:$(OBJDIR)/search.h \
		$(OBJDIR)/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)/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)/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)/webmail_.c:$(OBJDIR)/webmail.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)/wiki_.c:$(OBJDIR)/wiki.h \
	$(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h \
	$(OBJDIR)/winfile_.c:$(OBJDIR)/winfile.h \
	$(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h \
		$(OBJDIR)/wysiwyg_.c:$(OBJDIR)/wysiwyg.h \
		$(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h \
		$(OBJDIR)/xfersetup_.c:$(OBJDIR)/xfersetup.h \
		$(OBJDIR)/zip_.c:$(OBJDIR)/zip.h \
		$(SRCDIR)/sqlite3.h \
		$(SRCDIR)/th.h \
		$(OBJDIR)/VERSION.h
	$(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

1312
1313
1314
1315
1316
1317
1318








1319
1320
1321
1322
1323
1324
1325
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380







+
+
+
+
+
+
+
+







$(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

1408
1409
1410
1411
1412
1413
1414








1415
1416
1417
1418
1419
1420
1421
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484







+
+
+
+
+
+
+
+







$(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

1440
1441
1442
1443
1444
1445
1446








1447
1448
1449
1450
1451
1452
1453
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524







+
+
+
+
+
+
+
+







$(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

1496
1497
1498
1499
1500
1501
1502








1503
1504
1505
1506
1507
1508
1509
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588







+
+
+
+
+
+
+
+







$(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

1568
1569
1570
1571
1572
1573
1574








1575
1576
1577
1578
1579
1580
1581
1582








1583
1584
1585
1586
1587
1588
1589
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







+
+
+
+
+
+
+
+








+
+
+
+
+
+
+
+







$(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

1616
1617
1618
1619
1620
1621
1622








1623
1624
1625
1626
1627
1628
1629
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732







+
+
+
+
+
+
+
+







$(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

1648
1649
1650
1651
1652
1653
1654








1655
1656
1657
1658
1659
1660
1661
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772







+
+
+
+
+
+
+
+







$(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

1696
1697
1698
1699
1700
1701
1702








1703
1704
1705
1706
1707
1708
1709
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828







+
+
+
+
+
+
+
+







$(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

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
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







+
+
+
+
+
+
+
+
















+
+
+
+
+
+
+
+







$(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

2000
2001
2002
2003
2004
2005
2006








2007
2008
2009
2010
2011
2012
2013
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156







+
+
+
+
+
+
+
+







$(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

2148
2149
2150
2151
2152
2153
2154
2155

2156
2157
2158
2159
2160
2161
2162
2291
2292
2293
2294
2295
2296
2297

2298
2299
2300
2301
2302
2303
2304
2305







-
+







	$(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 $(OBJDIR)/default_css.h $(SRCDIR)/config.h
$(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 >$@

2176
2177
2178
2179
2180
2181
2182








2183
2184
2185
2186
2187
2188
2189
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340







+
+
+
+
+
+
+
+







$(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

2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2440
2441
2442
2443
2444
2445
2446








2447
2448
2449
2450
2451
2452
2453







-
-
-
-
-
-
-
-







	$(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)/webmail_.c:	$(SRCDIR)/webmail.c $(TRANSLATE)
	$(TRANSLATE) $(SRCDIR)/webmail.c >$@

$(OBJDIR)/webmail.o:	$(OBJDIR)/webmail_.c $(OBJDIR)/webmail.h $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/webmail.o -c $(OBJDIR)/webmail_.c

$(OBJDIR)/webmail.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
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2472
2473
2474
2475
2476
2477
2478








2479
2480
2481
2482
2483
2484
2485







-
-
-
-
-
-
-
-







	$(TRANSLATE) $(SRCDIR)/winhttp.c >$@

$(OBJDIR)/winhttp.o:	$(OBJDIR)/winhttp_.c $(OBJDIR)/winhttp.h $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/winhttp.o -c $(OBJDIR)/winhttp_.c

$(OBJDIR)/winhttp.h:	$(OBJDIR)/headers

$(OBJDIR)/wysiwyg_.c:	$(SRCDIR)/wysiwyg.c $(TRANSLATE)
	$(TRANSLATE) $(SRCDIR)/wysiwyg.c >$@

$(OBJDIR)/wysiwyg.o:	$(OBJDIR)/wysiwyg_.c $(OBJDIR)/wysiwyg.h $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/wysiwyg.o -c $(OBJDIR)/wysiwyg_.c

$(OBJDIR)/wysiwyg.h:	$(OBJDIR)/headers

$(OBJDIR)/xfer_.c:	$(SRCDIR)/xfer.c $(TRANSLATE)
	$(TRANSLATE) $(SRCDIR)/xfer.c >$@

$(OBJDIR)/xfer.o:	$(OBJDIR)/xfer_.c $(OBJDIR)/xfer.h $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/xfer.o -c $(OBJDIR)/xfer_.c

$(OBJDIR)/xfer.h:	$(OBJDIR)/headers
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
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







+






-




-





-



-

+
+






+
-
-
-
-
-
-
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
-
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+
-
-

-
+



-
-
+
+



-
+











-
-
+
+

	$(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_GET_TABLE \
                 -DSQLITE_OMIT_PROGRESS_CALLBACK \
                 -DSQLITE_OMIT_SHARED_CACHE \
                 -DSQLITE_OMIT_LOAD_EXTENSION \
                 -DSQLITE_MAX_EXPR_DEPTH=0 \
                 -DSQLITE_USE_ALLOCA \
                 -DSQLITE_ENABLE_LOCKING_STYLE=0 \
                 -DSQLITE_DEFAULT_FILE_FORMAT=4 \
                 -DSQLITE_ENABLE_EXPLAIN_COMMENTS \
                 -DSQLITE_ENABLE_FTS4 \
                 -DSQLITE_ENABLE_DBSTAT_VTAB \
                 -DSQLITE_ENABLE_JSON1 \
                 -DSQLITE_ENABLE_FTS5 \
                 -DSQLITE_ENABLE_STMTVTAB \
                 -DSQLITE_HAVE_ZLIB \
                 -DSQLITE_INTROSPECTION_PRAGMAS \
                 -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_THREADSAFE=0 \
                 -DSQLITE_DEFAULT_MEMSTATUS=0 \
                 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 \
                 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS \
                 -DSQLITE_OMIT_DECLTYPE \
                 -DSQLITE_OMIT_DEPRECATED \
                -DSQLITE_OMIT_GET_TABLE \
                -DSQLITE_OMIT_PROGRESS_CALLBACK \
                -DSQLITE_OMIT_SHARED_CACHE \
                -DSQLITE_OMIT_LOAD_EXTENSION \
                -DSQLITE_MAX_EXPR_DEPTH=0 \
                 -DSQLITE_OMIT_PROGRESS_CALLBACK \
                 -DSQLITE_OMIT_SHARED_CACHE \
                 -DSQLITE_OMIT_LOAD_EXTENSION \
                 -DSQLITE_MAX_EXPR_DEPTH=0 \
                -DSQLITE_USE_ALLOCA \
                -DSQLITE_ENABLE_LOCKING_STYLE=0 \
                -DSQLITE_DEFAULT_FILE_FORMAT=4 \
                -DSQLITE_ENABLE_EXPLAIN_COMMENTS \
                -DSQLITE_ENABLE_FTS4 \
                -DSQLITE_ENABLE_DBSTAT_VTAB \
                 -DSQLITE_ENABLE_LOCKING_STYLE=0 \
                 -DSQLITE_DEFAULT_FILE_FORMAT=4 \
                 -DSQLITE_ENABLE_EXPLAIN_COMMENTS \
                 -DSQLITE_ENABLE_FTS4 \
                 -DSQLITE_ENABLE_DBSTAT_VTAB \
                -DSQLITE_ENABLE_JSON1 \
                -DSQLITE_ENABLE_FTS5 \
                -DSQLITE_ENABLE_STMTVTAB \
                -DSQLITE_HAVE_ZLIB \
                 -DSQLITE_ENABLE_FTS5 \
                 -DSQLITE_ENABLE_STMTVTAB \
                 -DSQLITE_HAVE_ZLIB \
                -DSQLITE_INTROSPECTION_PRAGMAS \
                -DSQLITE_ENABLE_DBPAGE_VTAB \
                -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
                 -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

MINIZ_OPTIONS = -DMINIZ_NO_STDIO \
PIKCHR_OPTIONS = -DPIKCHR_TOKEN_LIMIT=10000
                -DMINIZ_NO_TIME \
                -DMINIZ_NO_ARCHIVE_APIS

$(OBJDIR)/sqlite3.o:	$(SQLITE3_SRC) $(SRCDIR)/../win/Makefile.mingw
$(SQLITE3_OBJ):	$(SQLITE3_SRC) $(SRCDIR)/../win/Makefile.mingw
	$(XTCC) $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SEE_FLAGS) \
		-c $(SQLITE3_SRC) -o $@

$(OBJDIR)/cson_amalgamation.o:	$(SRCDIR)/cson_amalgamation.c
	$(XTCC) -c $(SRCDIR)/cson_amalgamation.c -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)/sqlite3.h $(SRCDIR)/../win/Makefile.mingw
$(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)/miniz.o:	$(SRCDIR)/miniz.c
	$(XTCC) $(MINIZ_OPTIONS) -c $(SRCDIR)/miniz.c -o $@
$(OBJDIR)/pikchr.o:	$(SRCDIR_extsrc)/pikchr.c
	$(XTCC) $(PIKCHR_OPTIONS) -c $(SRCDIR_extsrc)/pikchr.c -o $@

Changes to win/Makefile.mingw.mistachkin.

1
2
3
4

5
6
7
8
9
10
11
1
2
3

4
5
6
7
8
9
10
11



-
+







#!/usr/bin/make
#
##############################################################################
# WARNING: DO NOT EDIT, AUTOMATICALLY GENERATED FILE (SEE "src/makemake.tcl")
# 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
26
27
28
29
30
31
32


33
34
35
36
37
38
39
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41







+
+







# 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
50
51
52
53
54
55
56
57

58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
52
53
54
55
56
57
58

59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75




76
77
78
79
80
81
82







-
+
















-
-
-
-







#
BCC = $(BCCEXE)

#### Enable compiling with debug symbols (much larger binary)
#
# FOSSIL_ENABLE_SYMBOLS = 1

#### Enable JSON (http://www.json.org) support using "cson"
#### 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 legacy treatment of mv/rm (skip checkout files)
#
FOSSIL_ENABLE_LEGACY_MV_RM = 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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
101
102
103
104
105
106
107




108
109
110
111
112
113
114







-
-
-
-







#
# USE_MMAN_H = 1

#### Use the SQLite Encryption Extension
#
# USE_SEE = 1

#### Use the miniz compression library
#
# FOSSIL_ENABLE_MINIZ = 1

#### Use the Tcl source directory instead of the install directory?
#    This is useful when Tcl has been compiled statically with MinGW.
#
FOSSIL_TCL_SOURCE = 1

#### Check if the workaround for the MinGW command line handling needs to
#    be enabled by default.  This check may be somewhat fragile due to the
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163

164
165

166
167
168
169
170
171
172
173
174
175
176
177
178
179

180
181
182
183
184
185
186
136
137
138
139
140
141
142




143
144

145
146
147
148
149
150
151

152
153

154
155
156
157
158

159

160
161



162

163
164
165
166
167
168
169
170







-
-
-
-


-







-
+

-
+




-

-


-
-
-

-
+








#### Determine if the optimized assembly routines provided with zlib should be
#    used, taking into account whether zlib is actually enabled and the target
#    processor architecture.
#
ifndef X64
SSLCONFIG = mingw
ifndef FOSSIL_ENABLE_MINIZ
ZLIBCONFIG = LOC="-DASMV -DASMINF" OBJA="inffas86.o match.o"
ZLIBTARGETS = $(ZLIBDIR)/inffas86.o $(ZLIBDIR)/match.o
else
ZLIBCONFIG =
ZLIBTARGETS =
endif
else
SSLCONFIG = mingw64
ZLIBCONFIG =
ZLIBTARGETS =
endif

#### Disable creation of the OpenSSL shared libraries.  Also, disable support
#    for both SSLv2 and SSLv3 (i.e. thereby forcing the use of TLS).
#    for SSLv3 (i.e. thereby forcing the use of TLS).
#
SSLCONFIG += no-ssl2 no-ssl3 no-weak-ssl-ciphers no-shared
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).
#
ifndef FOSSIL_ENABLE_MINIZ
SSLCONFIG +=  --with-zlib-lib=$(PWD)/$(ZLIBDIR) --with-zlib-include=$(PWD)/$(ZLIBDIR) zlib
endif

#### The directories where the OpenSSL include and library files are located.
#    The recommended usage here is to use the Sysinternals junction tool
#    to create a hard link between an "openssl-1.x" sub-directory of the
#    Fossil source code directory and the target OpenSSL source directory.
#
OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.0.2p
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
228
229
230
231
232
233
234

235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254

255
256
257

258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229



230

231
232
233
234

235



236

237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254






255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272






273
274
275
276
277
278
279







+










-
-
-

-




-
+
-
-
-
+
-


















-
-
-
-
-
-


















-
-
-
-
-
-







#### 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

#### When not using the miniz compression library, zlib is required.
#
ifndef FOSSIL_ENABLE_MINIZ
TCC += -L$(ZLIBDIR) -I$(ZINCDIR)
endif

#### Compile resources for use in building executables that will run
#    on the target platform.
#
RCC = $(PREFIX)windres -I$(SRCDIR)
RCC = $(PREFIX)windres -I$(SRCDIR) -I$(ZINCDIR)

ifndef FOSSIL_ENABLE_MINIZ
RCC += -I$(ZINCDIR)
RCC += -I$(SRCDIR_extsrc)
endif

# With HTTPS support
ifdef FOSSIL_ENABLE_SSL
TCC += -L$(OPENSSLLIBDIR) -I$(OPENSSLINCDIR)
RCC += -I$(OPENSSLINCDIR)
endif

# With Tcl support
ifdef FOSSIL_ENABLE_TCL
ifdef FOSSIL_TCL_SOURCE
TCC += -L$(TCLSRCDIR)/win -I$(TCLSRCDIR)/generic -I$(TCLSRCDIR)/win
RCC += -I$(TCLSRCDIR)/generic -I$(TCLSRCDIR)/win
else
TCC += -L$(TCLLIBDIR) -I$(TCLINCDIR)
RCC += -I$(TCLINCDIR)
endif
endif

# With miniz (i.e. instead of zlib)
ifdef FOSSIL_ENABLE_MINIZ
TCC += -DFOSSIL_ENABLE_MINIZ=1
RCC += -DFOSSIL_ENABLE_MINIZ=1
endif

# With MinGW command line handling workaround
ifdef MINGW_IS_32BIT_ONLY
TCC += -DBROKEN_MINGW_CMDLINE=1
RCC += -DBROKEN_MINGW_CMDLINE=1
endif

# With HTTPS support
ifdef FOSSIL_ENABLE_SSL
TCC += -DFOSSIL_ENABLE_SSL=1
RCC += -DFOSSIL_ENABLE_SSL=1
endif

# With 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 legacy treatment of mv/rm
ifdef FOSSIL_ENABLE_LEGACY_MV_RM
TCC += -DFOSSIL_ENABLE_LEGACY_MV_RM=1
RCC += -DFOSSIL_ENABLE_LEGACY_MV_RM=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
387
388
389
390
391
392
393
394

395
396
397
398
399
400
401
402
403
404
405
353
354
355
356
357
358
359

360
361

362

363
364
365
366
367
368
369







-
+

-

-








#### Extra arguments for linking the finished binary.  Fossil needs
#    to link against the Z-Lib compression library.  There are no
#    other mandatory dependencies.
#
LIB += -lmingwex

#### When not using the miniz compression library, zlib is required.
#### zlib is required.
#
ifndef FOSSIL_ENABLE_MINIZ
LIB += -lz
endif

#### These libraries MUST appear in the same order as they do for Tcl
#    or linking with it will not work (exact reason unknown).
#
ifdef FOSSIL_ENABLE_TCL
ifdef FOSSIL_ENABLE_TCL_STUBS
LIB += -lkernel32 -lws2_32
435
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
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
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







+



+












+




+







+









+

+





+




+






+







# 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 \
514
515
516
517
518
519
520

521
522

523
524
525
526
527
528
529
530

531
532
533
534
535
536
537
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







+


+








+







  $(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 \
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
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







+














-




-





+
-
+
+













-
-
-
-
-
-
-
-
-
+
+
+
+




-




-
-
-
-












-
-
-
-




+
+
+
+
+

+
+
+


+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+




-



+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+






+



+












+




+







+









+

+





+




+






+







  $(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)/webmail.c \
  $(SRCDIR)/wiki.c \
  $(SRCDIR)/wikiformat.c \
  $(SRCDIR)/winfile.c \
  $(SRCDIR)/winhttp.c \
  $(SRCDIR)/wysiwyg.c \
  $(SRCDIR)/xfer.c \
  $(SRCDIR)/xfersetup.c \
  $(SRCDIR)/zip.c

EXTRA_FILES = \
  $(SRCDIR)/../extsrc/pikchr-worker.js \
  $(SRCDIR)/../skins/aht/details.txt \
  $(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/blitz_no_logo/css.txt \
  $(SRCDIR)/../skins/blitz_no_logo/details.txt \
  $(SRCDIR)/../skins/blitz_no_logo/footer.txt \
  $(SRCDIR)/../skins/blitz_no_logo/header.txt \
  $(SRCDIR)/../skins/blitz_no_logo/ticket.txt \
  $(SRCDIR)/../skins/bootstrap/css.txt \
  $(SRCDIR)/../skins/bootstrap/details.txt \
  $(SRCDIR)/../skins/bootstrap/footer.txt \
  $(SRCDIR)/../skins/bootstrap/header.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/default/js.txt \
  $(SRCDIR)/../skins/eagle/css.txt \
  $(SRCDIR)/../skins/eagle/details.txt \
  $(SRCDIR)/../skins/eagle/footer.txt \
  $(SRCDIR)/../skins/eagle/header.txt \
  $(SRCDIR)/../skins/enhanced1/css.txt \
  $(SRCDIR)/../skins/enhanced1/details.txt \
  $(SRCDIR)/../skins/enhanced1/footer.txt \
  $(SRCDIR)/../skins/enhanced1/header.txt \
  $(SRCDIR)/../skins/khaki/css.txt \
  $(SRCDIR)/../skins/khaki/details.txt \
  $(SRCDIR)/../skins/khaki/footer.txt \
  $(SRCDIR)/../skins/khaki/header.txt \
  $(SRCDIR)/../skins/original/css.txt \
  $(SRCDIR)/../skins/original/details.txt \
  $(SRCDIR)/../skins/original/footer.txt \
  $(SRCDIR)/../skins/original/header.txt \
  $(SRCDIR)/../skins/plain_gray/css.txt \
  $(SRCDIR)/../skins/plain_gray/details.txt \
  $(SRCDIR)/../skins/plain_gray/footer.txt \
  $(SRCDIR)/../skins/plain_gray/header.txt \
  $(SRCDIR)/../skins/rounded1/css.txt \
  $(SRCDIR)/../skins/rounded1/details.txt \
  $(SRCDIR)/../skins/rounded1/footer.txt \
  $(SRCDIR)/../skins/rounded1/header.txt \
  $(SRCDIR)/../skins/xekri/css.txt \
  $(SRCDIR)/../skins/xekri/details.txt \
  $(SRCDIR)/../skins/xekri/footer.txt \
  $(SRCDIR)/../skins/xekri/header.txt \
  $(SRCDIR)/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)/sbsdiff.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 \
724
725
726
727
728
729
730

731
732

733
734
735
736
737
738
739
740

741
742
743
744
745
746
747
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







+


+








+







  $(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 \
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
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







+














-




-






+



+












+




+







+









+

+





+




+






+







  $(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)/webmail_.c \
  $(OBJDIR)/wiki_.c \
  $(OBJDIR)/wikiformat_.c \
  $(OBJDIR)/winfile_.c \
  $(OBJDIR)/winhttp_.c \
  $(OBJDIR)/wysiwyg_.c \
  $(OBJDIR)/xfer_.c \
  $(OBJDIR)/xfersetup_.c \
  $(OBJDIR)/zip_.c

OBJ = \
 $(OBJDIR)/add.o \
 $(OBJDIR)/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 \
861
862
863
864
865
866
867

868
869

870
871
872
873
874
875
876
877

878
879
880
881
882
883
884
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







+


+








+







 $(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 \
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
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







+














-




-




















-














-












-
+

-




-







 $(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)/webmail.o \
 $(OBJDIR)/wiki.o \
 $(OBJDIR)/wikiformat.o \
 $(OBJDIR)/winfile.o \
 $(OBJDIR)/winhttp.o \
 $(OBJDIR)/wysiwyg.o \
 $(OBJDIR)/xfer.o \
 $(OBJDIR)/xfersetup.o \
 $(OBJDIR)/zip.o

APPNAME    = fossil.exe
APPTARGETS =

#### If the USE_WINDOWS variable exists, it is assumed that we are building
#    inside of a Windows-style shell; otherwise, it is assumed that we are
#    building inside of a Unix-style shell.  Note that the "move" command is
#    broken when attempting to use it from the Windows shell via MinGW make
#    because the SHELL variable is only used for certain commands that are
#    recognized internally by make.
#
ifdef USE_WINDOWS
TRANSLATE   = $(subst /,\,$(OBJDIR)/translate.exe)
MAKEHEADERS = $(subst /,\,$(OBJDIR)/makeheaders.exe)
MKINDEX     = $(subst /,\,$(OBJDIR)/mkindex.exe)
MKBUILTIN   = $(subst /,\,$(OBJDIR)/mkbuiltin.exe)
MKVERSION   = $(subst /,\,$(OBJDIR)/mkversion.exe)
MKCSS       = $(subst /,\,$(OBJDIR)/mkcss.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
MKCSS       = $(OBJDIR)/mkcss.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 $(OBJDIR)/default_css.h
$(OBJDIR)/fossil.o:	$(SRCDIR)/../win/fossil.rc $(OBJDIR)/VERSION.h
ifdef USE_WINDOWS
	$(CAT) $(subst /,\,$(SRCDIR)\miniz.c) | $(GREP) "define MZ_VERSION" > $(subst /,\,$(OBJDIR)\minizver.h)
	$(CP) $(subst /,\,$(SRCDIR)\..\win\fossil.rc) $(subst /,\,$(OBJDIR))
	$(CP) $(subst /,\,$(SRCDIR)\..\win\fossil.ico) $(subst /,\,$(OBJDIR))
	$(CP) $(subst /,\,$(SRCDIR)\..\win\fossil.exe.manifest) $(subst /,\,$(OBJDIR))
else
	$(CAT) $(SRCDIR)/miniz.c | $(GREP) "define MZ_VERSION" > $(OBJDIR)/minizver.h
	$(CP) $(SRCDIR)/../win/fossil.rc $(OBJDIR)
	$(CP) $(SRCDIR)/../win/fossil.ico $(OBJDIR)
	$(CP) $(SRCDIR)/../win/fossil.exe.manifest $(OBJDIR)
endif
	$(RCC) $(OBJDIR)/fossil.rc -o $(OBJDIR)/fossil.o

install:	$(OBJDIR) $(APPNAME)
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
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







-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
+
-
-
-
-
+

-
-
+
+







-
+


-
-
+
+

-
-
+
+


+
+
+
+

-
+
+

-
+
-
-
-
-
-
-

-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+







-
-
+
+







-
-
-
-
-
-






-
-
-

-



+


















-
+

-
+







$(OBJDIR):
ifdef USE_WINDOWS
	$(MKDIR) $(subst /,\,$(OBJDIR))
else
	$(MKDIR) $(OBJDIR)
endif

$(TRANSLATE):	$(SRCDIR)/translate.c
	$(XBCC) -o $@ $(SRCDIR)/translate.c
$(TRANSLATE):	$(SRCDIR_tools)/translate.c
	$(XBCC) -o $@ $(SRCDIR_tools)/translate.c

$(MAKEHEADERS):	$(SRCDIR)/makeheaders.c
	$(XBCC) -o $@ $(SRCDIR)/makeheaders.c
$(MAKEHEADERS):	$(SRCDIR_tools)/makeheaders.c
	$(XBCC) -o $@ $(SRCDIR_tools)/makeheaders.c

$(MKINDEX):	$(SRCDIR)/mkindex.c
	$(XBCC) -o $@ $(SRCDIR)/mkindex.c
$(MKINDEX):	$(SRCDIR_tools)/mkindex.c
	$(XBCC) -o $@ $(SRCDIR_tools)/mkindex.c

$(MKBUILTIN):	$(SRCDIR)/mkbuiltin.c
	$(XBCC) -o $@ $(SRCDIR)/mkbuiltin.c
$(MKBUILTIN):	$(SRCDIR_tools)/mkbuiltin.c
	$(XBCC) -o $@ $(SRCDIR_tools)/mkbuiltin.c

$(MKVERSION): $(SRCDIR)/mkversion.c
$(MKVERSION): $(SRCDIR_tools)/mkversion.c
	$(XBCC) -o $@ $(SRCDIR)/mkversion.c

$(MKCSS): $(SRCDIR)/mkcss.c
	$(XBCC) -o $@ $(SRCDIR)/mkcss.c
	$(XBCC) -o $@ $(SRCDIR_tools)/mkversion.c

$(CODECHECK1):	$(SRCDIR)/codecheck1.c
	$(XBCC) -o $@ $(SRCDIR)/codecheck1.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)/VERSION.h:	$(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(MKVERSION) $(OBJDIR)/phony.h
	$(MKVERSION) $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION >$@

$(OBJDIR)/default_css.h:	$(SRCDIR)/default_css.txt $(MKCSS)
	$(MKCSS) $(SRCDIR)/default_css.txt $@
$(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 set
# to 1. If it is set to 1, then there is no need to build or link
# 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 =
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 FOSSIL_ENABLE_MINIZ variable may be undefined, set to 0, or
# set to 1.  If it is set to 1, the miniz library included in the
# source tree should be used; otherwise, it should not.
MINIZ_OBJ.0 =
MINIZ_OBJ.1 = $(OBJDIR)/miniz.o
MINIZ_OBJ.  = $(MINIZ_OBJ.0)

# The USE_SEE variable may be undefined, 0 or 1.  If undefined or
# 0, ordinary 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 = sqlite3.c
SQLITE3_SRC.1 = sqlite3-see.c
SQLITE3_SRC. = sqlite3.c
SQLITE3_SRC = $(SRCDIR)/$(SQLITE3_SRC.$(USE_SEE))
SQLITE3_SHELL_SRC.0 = shell.c
SQLITE3_SHELL_SRC.1 = shell-see.c
SQLITE3_SHELL_SRC. = shell.c
SQLITE3_SHELL_SRC = $(SRCDIR)/$(SQLITE3_SHELL_SRC.$(USE_SEE))
# 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.$(USE_SYSTEM_SQLITE)) \
 $(MINIZ_OBJ.$(FOSSIL_ENABLE_MINIZ)) \
 $(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


$(ZLIBDIR)/inffas86.o:
	$(TCC) -c -o $@ -DASMINF -I$(ZLIBDIR) -O3 $(ZLIBDIR)/contrib/inflate86/inffas86.c

$(ZLIBDIR)/match.o:
	$(TCC) -c -o $@ -DASMV $(ZLIBDIR)/contrib/asm686/match.S

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

ifdef FOSSIL_ENABLE_MINIZ
BLDTARGETS =
else
BLDTARGETS = zlib
endif

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) $(OBJ) $(EXTRAOBJ) $(OBJDIR)/fossil.o
$(APPNAME):	$(APPTARGETS) $(OBJDIR)/headers $(CODECHECK1) $(EXTRAOBJ) $(OBJ) $(OBJDIR)/fossil.o
	$(CODECHECK1) $(TRANS_SRC)
	$(TCC) -o $@ $(OBJ) $(EXTRAOBJ) $(OBJDIR)/fossil.o $(LIB)
	$(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

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
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







-
+

+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+













+
+
+
+
+
+
+
+








$(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 $(OBJDIR)/default_css.h $(MAKEHEADERS) $(OBJDIR)/VERSION.h
$(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)/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)/checkin_.c:$(OBJDIR)/checkin.h \
		$(OBJDIR)/checkout_.c:$(OBJDIR)/checkout.h \
		$(OBJDIR)/clearsign_.c:$(OBJDIR)/clearsign.h \
		$(OBJDIR)/clone_.c:$(OBJDIR)/clone.h \
		$(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h \
		$(OBJDIR)/configure_.c:$(OBJDIR)/configure.h \
		$(OBJDIR)/content_.c:$(OBJDIR)/content.h \
		$(OBJDIR)/cookies_.c:$(OBJDIR)/cookies.h \
		$(OBJDIR)/db_.c:$(OBJDIR)/db.h \
		$(OBJDIR)/delta_.c:$(OBJDIR)/delta.h \
		$(OBJDIR)/deltacmd_.c:$(OBJDIR)/deltacmd.h \
		$(OBJDIR)/descendants_.c:$(OBJDIR)/descendants.h \
		$(OBJDIR)/diff_.c:$(OBJDIR)/diff.h \
		$(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h \
		$(OBJDIR)/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)/file_.c:$(OBJDIR)/file.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)/glob_.c:$(OBJDIR)/glob.h \
		$(OBJDIR)/graph_.c:$(OBJDIR)/graph.h \
		$(OBJDIR)/gzip_.c:$(OBJDIR)/gzip.h \
		$(OBJDIR)/hname_.c:$(OBJDIR)/hname.h \
		$(OBJDIR)/http_.c:$(OBJDIR)/http.h \
		$(OBJDIR)/http_socket_.c:$(OBJDIR)/http_socket.h \
		$(OBJDIR)/http_ssl_.c:$(OBJDIR)/http_ssl.h \
		$(OBJDIR)/http_transport_.c:$(OBJDIR)/http_transport.h \
		$(OBJDIR)/import_.c:$(OBJDIR)/import.h \
		$(OBJDIR)/info_.c:$(OBJDIR)/info.h \
		$(OBJDIR)/json_.c:$(OBJDIR)/json.h \
		$(OBJDIR)/json_artifact_.c:$(OBJDIR)/json_artifact.h \
		$(OBJDIR)/json_branch_.c:$(OBJDIR)/json_branch.h \
		$(OBJDIR)/json_config_.c:$(OBJDIR)/json_config.h \
		$(OBJDIR)/json_diff_.c:$(OBJDIR)/json_diff.h \
		$(OBJDIR)/json_dir_.c:$(OBJDIR)/json_dir.h \
		$(OBJDIR)/json_finfo_.c:$(OBJDIR)/json_finfo.h \
		$(OBJDIR)/json_login_.c:$(OBJDIR)/json_login.h \
		$(OBJDIR)/json_query_.c:$(OBJDIR)/json_query.h \
		$(OBJDIR)/json_report_.c:$(OBJDIR)/json_report.h \
		$(OBJDIR)/json_status_.c:$(OBJDIR)/json_status.h \
		$(OBJDIR)/json_tag_.c:$(OBJDIR)/json_tag.h \
		$(OBJDIR)/json_timeline_.c:$(OBJDIR)/json_timeline.h \
		$(OBJDIR)/json_user_.c:$(OBJDIR)/json_user.h \
		$(OBJDIR)/json_wiki_.c:$(OBJDIR)/json_wiki.h \
		$(OBJDIR)/leaf_.c:$(OBJDIR)/leaf.h \
		$(OBJDIR)/loadctrl_.c:$(OBJDIR)/loadctrl.h \
		$(OBJDIR)/login_.c:$(OBJDIR)/login.h \
		$(OBJDIR)/lookslike_.c:$(OBJDIR)/lookslike.h \
		$(OBJDIR)/main_.c:$(OBJDIR)/main.h \
		$(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h \
		$(OBJDIR)/markdown_.c:$(OBJDIR)/markdown.h \
		$(OBJDIR)/markdown_html_.c:$(OBJDIR)/markdown_html.h \
		$(OBJDIR)/md5_.c:$(OBJDIR)/md5.h \
		$(OBJDIR)/merge_.c:$(OBJDIR)/merge.h \
		$(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h \
		$(OBJDIR)/moderate_.c:$(OBJDIR)/moderate.h \
		$(OBJDIR)/name_.c:$(OBJDIR)/name.h \
		$(OBJDIR)/path_.c:$(OBJDIR)/path.h \
		$(OBJDIR)/piechart_.c:$(OBJDIR)/piechart.h \
		$(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h \
		$(OBJDIR)/popen_.c:$(OBJDIR)/popen.h \
		$(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h \
		$(OBJDIR)/printf_.c:$(OBJDIR)/printf.h \
		$(OBJDIR)/publish_.c:$(OBJDIR)/publish.h \
		$(OBJDIR)/purge_.c:$(OBJDIR)/purge.h \
		$(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h \
		$(OBJDIR)/regexp_.c:$(OBJDIR)/regexp.h \
		$(OBJDIR)/report_.c:$(OBJDIR)/report.h \
		$(OBJDIR)/rss_.c:$(OBJDIR)/rss.h \
		$(OBJDIR)/schema_.c:$(OBJDIR)/schema.h \
		$(OBJDIR)/search_.c:$(OBJDIR)/search.h \
		$(OBJDIR)/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)/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)/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)/webmail_.c:$(OBJDIR)/webmail.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)/wiki_.c:$(OBJDIR)/wiki.h \
	$(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h \
	$(OBJDIR)/winfile_.c:$(OBJDIR)/winfile.h \
	$(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h \
		$(OBJDIR)/wysiwyg_.c:$(OBJDIR)/wysiwyg.h \
		$(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h \
		$(OBJDIR)/xfersetup_.c:$(OBJDIR)/xfersetup.h \
		$(OBJDIR)/zip_.c:$(OBJDIR)/zip.h \
		$(SRCDIR)/sqlite3.h \
		$(SRCDIR)/th.h \
		$(OBJDIR)/VERSION.h
	$(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

1312
1313
1314
1315
1316
1317
1318








1319
1320
1321
1322
1323
1324
1325
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376







+
+
+
+
+
+
+
+







$(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

1408
1409
1410
1411
1412
1413
1414








1415
1416
1417
1418
1419
1420
1421
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480







+
+
+
+
+
+
+
+







$(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

1440
1441
1442
1443
1444
1445
1446








1447
1448
1449
1450
1451
1452
1453
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520







+
+
+
+
+
+
+
+







$(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

1496
1497
1498
1499
1500
1501
1502








1503
1504
1505
1506
1507
1508
1509
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584







+
+
+
+
+
+
+
+







$(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

1568
1569
1570
1571
1572
1573
1574








1575
1576
1577
1578
1579
1580
1581
1582








1583
1584
1585
1586
1587
1588
1589
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







+
+
+
+
+
+
+
+








+
+
+
+
+
+
+
+







$(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

1616
1617
1618
1619
1620
1621
1622








1623
1624
1625
1626
1627
1628
1629
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728







+
+
+
+
+
+
+
+







$(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

1648
1649
1650
1651
1652
1653
1654








1655
1656
1657
1658
1659
1660
1661
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768







+
+
+
+
+
+
+
+







$(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

1696
1697
1698
1699
1700
1701
1702








1703
1704
1705
1706
1707
1708
1709
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824







+
+
+
+
+
+
+
+







$(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

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
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







+
+
+
+
+
+
+
+
















+
+
+
+
+
+
+
+







$(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

2000
2001
2002
2003
2004
2005
2006








2007
2008
2009
2010
2011
2012
2013
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152







+
+
+
+
+
+
+
+







$(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

2148
2149
2150
2151
2152
2153
2154
2155

2156
2157
2158
2159
2160
2161
2162
2287
2288
2289
2290
2291
2292
2293

2294
2295
2296
2297
2298
2299
2300
2301







-
+







	$(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 $(OBJDIR)/default_css.h $(SRCDIR)/config.h
$(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 >$@

2176
2177
2178
2179
2180
2181
2182








2183
2184
2185
2186
2187
2188
2189
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336







+
+
+
+
+
+
+
+







$(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

2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2436
2437
2438
2439
2440
2441
2442








2443
2444
2445
2446
2447
2448
2449







-
-
-
-
-
-
-
-







	$(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)/webmail_.c:	$(SRCDIR)/webmail.c $(TRANSLATE)
	$(TRANSLATE) $(SRCDIR)/webmail.c >$@

$(OBJDIR)/webmail.o:	$(OBJDIR)/webmail_.c $(OBJDIR)/webmail.h $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/webmail.o -c $(OBJDIR)/webmail_.c

$(OBJDIR)/webmail.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
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2468
2469
2470
2471
2472
2473
2474








2475
2476
2477
2478
2479
2480
2481







-
-
-
-
-
-
-
-







	$(TRANSLATE) $(SRCDIR)/winhttp.c >$@

$(OBJDIR)/winhttp.o:	$(OBJDIR)/winhttp_.c $(OBJDIR)/winhttp.h $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/winhttp.o -c $(OBJDIR)/winhttp_.c

$(OBJDIR)/winhttp.h:	$(OBJDIR)/headers

$(OBJDIR)/wysiwyg_.c:	$(SRCDIR)/wysiwyg.c $(TRANSLATE)
	$(TRANSLATE) $(SRCDIR)/wysiwyg.c >$@

$(OBJDIR)/wysiwyg.o:	$(OBJDIR)/wysiwyg_.c $(OBJDIR)/wysiwyg.h $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/wysiwyg.o -c $(OBJDIR)/wysiwyg_.c

$(OBJDIR)/wysiwyg.h:	$(OBJDIR)/headers

$(OBJDIR)/xfer_.c:	$(SRCDIR)/xfer.c $(TRANSLATE)
	$(TRANSLATE) $(SRCDIR)/xfer.c >$@

$(OBJDIR)/xfer.o:	$(OBJDIR)/xfer_.c $(OBJDIR)/xfer.h $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/xfer.o -c $(OBJDIR)/xfer_.c

$(OBJDIR)/xfer.h:	$(OBJDIR)/headers
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
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







+






-




-




-

-



-

+
+






+
-
-
-
-
-
-
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
-
-
-
-
-
+
+
+
+
-
-
+
-
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+
-
-

-
+



-
-
+
+



-
+











-
-
+
+

	$(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_GET_TABLE \
                 -DSQLITE_OMIT_PROGRESS_CALLBACK \
                 -DSQLITE_OMIT_SHARED_CACHE \
                 -DSQLITE_OMIT_LOAD_EXTENSION \
                 -DSQLITE_MAX_EXPR_DEPTH=0 \
                 -DSQLITE_USE_ALLOCA \
                 -DSQLITE_ENABLE_LOCKING_STYLE=0 \
                 -DSQLITE_DEFAULT_FILE_FORMAT=4 \
                 -DSQLITE_ENABLE_EXPLAIN_COMMENTS \
                 -DSQLITE_ENABLE_FTS4 \
                 -DSQLITE_ENABLE_FTS3_PARENTHESIS \
                 -DSQLITE_ENABLE_DBSTAT_VTAB \
                 -DSQLITE_ENABLE_JSON1 \
                 -DSQLITE_ENABLE_FTS5 \
                 -DSQLITE_ENABLE_STMTVTAB \
                 -DSQLITE_HAVE_ZLIB \
                 -DSQLITE_INTROSPECTION_PRAGMAS \
                 -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_THREADSAFE=0 \
                 -DSQLITE_DEFAULT_MEMSTATUS=0 \
                 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 \
                 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS \
                 -DSQLITE_OMIT_DECLTYPE \
                 -DSQLITE_OMIT_DEPRECATED \
                -DSQLITE_OMIT_GET_TABLE \
                -DSQLITE_OMIT_PROGRESS_CALLBACK \
                -DSQLITE_OMIT_SHARED_CACHE \
                -DSQLITE_OMIT_LOAD_EXTENSION \
                -DSQLITE_MAX_EXPR_DEPTH=0 \
                 -DSQLITE_OMIT_PROGRESS_CALLBACK \
                 -DSQLITE_OMIT_SHARED_CACHE \
                 -DSQLITE_OMIT_LOAD_EXTENSION \
                 -DSQLITE_MAX_EXPR_DEPTH=0 \
                -DSQLITE_USE_ALLOCA \
                -DSQLITE_ENABLE_LOCKING_STYLE=0 \
                -DSQLITE_DEFAULT_FILE_FORMAT=4 \
                -DSQLITE_ENABLE_EXPLAIN_COMMENTS \
                -DSQLITE_ENABLE_FTS4 \
                 -DSQLITE_ENABLE_LOCKING_STYLE=0 \
                 -DSQLITE_DEFAULT_FILE_FORMAT=4 \
                 -DSQLITE_ENABLE_EXPLAIN_COMMENTS \
                 -DSQLITE_ENABLE_FTS4 \
                -DSQLITE_ENABLE_FTS3_PARENTHESIS \
                -DSQLITE_ENABLE_DBSTAT_VTAB \
                 -DSQLITE_ENABLE_DBSTAT_VTAB \
                -DSQLITE_ENABLE_JSON1 \
                -DSQLITE_ENABLE_FTS5 \
                -DSQLITE_ENABLE_STMTVTAB \
                -DSQLITE_HAVE_ZLIB \
                 -DSQLITE_ENABLE_FTS5 \
                 -DSQLITE_ENABLE_STMTVTAB \
                 -DSQLITE_HAVE_ZLIB \
                -DSQLITE_INTROSPECTION_PRAGMAS \
                -DSQLITE_ENABLE_DBPAGE_VTAB \
                -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
                 -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

MINIZ_OPTIONS = -DMINIZ_NO_STDIO \
PIKCHR_OPTIONS = -DPIKCHR_TOKEN_LIMIT=10000
                -DMINIZ_NO_TIME \
                -DMINIZ_NO_ARCHIVE_APIS

$(OBJDIR)/sqlite3.o:	$(SQLITE3_SRC) $(SRCDIR)/../win/Makefile.mingw.mistachkin
$(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)/cson_amalgamation.c
	$(XTCC) -c $(SRCDIR)/cson_amalgamation.c -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)/sqlite3.h $(SRCDIR)/../win/Makefile.mingw.mistachkin
$(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)/miniz.o:	$(SRCDIR)/miniz.c
	$(XTCC) $(MINIZ_OPTIONS) -c $(SRCDIR)/miniz.c -o $@
$(OBJDIR)/pikchr.o:	$(SRCDIR_extsrc)/pikchr.c
	$(XTCC) $(PIKCHR_OPTIONS) -c $(SRCDIR_extsrc)/pikchr.c -o $@

Changes to 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
1
2

3
4
5







6
7
8
9
10
11



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38




39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62


-
+


-
-
-
-
-
-
-






-
-
-
+
+
+
+
+
+



+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+

+
+
+
+
+




+
+
+







#
##############################################################################
# WARNING: DO NOT EDIT, AUTOMATICALLY GENERATED FILE (SEE "src/makemake.tcl")
# WARNING: DO NOT EDIT, AUTOMATICALLY GENERATED FILE (SEE "tools/makemake.tcl")
##############################################################################
#
# This Makefile will only function correctly if used from a sub-directory
# that is a direct child of the top-level directory for this project.
#
!if !exist("..\.fossil-settings")
!error "Please change the current directory to the one containing this file."
!endif

#
# This file is automatically generated.  Instead of editing this
# file, edit "makemake.tcl" then run "tclsh makemake.tcl"
# to regenerate this file.
#
B       = ..
SRCDIR  = $B\src
OBJDIR  = .
OX      = .
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 must
# be built from source code.  The PERLDIR variable should point to
# the directory containing the main Perl binary (i.e. "perl.exe").
PERLDIR = C:\Perl\bin
# Perl 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

55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
76
77
78
79
80
81
82










83
84
85
86
87
88
89







-
-
-
-
-
-
-
-
-
-







!endif

# Enable the JSON API?
!ifndef FOSSIL_ENABLE_JSON
FOSSIL_ENABLE_JSON = 0
!endif

# Enable legacy treatment of the mv/rm commands?
!ifndef FOSSIL_ENABLE_LEGACY_MV_RM
FOSSIL_ENABLE_LEGACY_MV_RM = 1
!endif

# Enable use of miniz instead of zlib?
!ifndef FOSSIL_ENABLE_MINIZ
FOSSIL_ENABLE_MINIZ = 0
!endif

# Enable OpenSSL support?
!ifndef FOSSIL_ENABLE_SSL
FOSSIL_ENABLE_SSL = 0
!endif

# Enable the Tcl integration subsystem?
!ifndef FOSSIL_ENABLE_TCL
96
97
98
99
100
101
102
103
104


105
106

107
108

109
110
111

112
113
114
115
116

117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136

137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156

157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191

192
193
194

195
196
197
198

199
200
201
202

203
204
205

206
207



208
209
210
211
212
213
214
215
216




217
218

219
220

221
222







223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238












239
240

241
242
243

244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260

261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277

278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
107
108
109
110
111
112
113


114
115
116

117
118

119
120


121
122
123



124
125
126
127
128
129










130
131



132
133
134
135
136
137



138







139



140
141
142
143
144










145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164

165
166


167

168
169

170
171
172
173

174
175
176

177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196

197
198

199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237

238
239
240

241
242
243
244
245
246
247
248
249
250
251
252
253
254
255

256

257






258
259
260
261
262
263
264
265
266
267

268
269
270
271
272
273
274
275





276
277
278
279
280
281
282







-
-
+
+

-
+

-
+

-
-
+


-
-
-
+





-
-
-
-
-
-
-
-
-
-


-
-
-
+





-
-
-

-
-
-
-
-
-
-

-
-
-
+




-
-
-
-
-
-
-
-
-
-




















-
+

-
-
+
-


-
+



-
+


-
+


+
+
+









+
+
+
+

-
+

-
+


+
+
+
+
+
+
+
















+
+
+
+
+
+
+
+
+
+
+
+

-
+


-
+














-

-
+
-
-
-
-
-
-










-
+







-
-
-
-
-








# Enable support for the SQLite Encryption Extension?
!ifndef USE_SEE
USE_SEE = 0
!endif

!if $(FOSSIL_ENABLE_SSL)!=0
SSLDIR    = $(B)\compat\openssl-1.0.2p
SSLINCDIR = $(SSLDIR)\inc32
SSLDIR    = $(B)\compat\openssl
SSLINCDIR = $(SSLDIR)\include
!if $(FOSSIL_DYNAMIC_BUILD)!=0
SSLLIBDIR = $(SSLDIR)\out32dll
SSLLIBDIR = $(SSLDIR)
!else
SSLLIBDIR = $(SSLDIR)\out32
SSLLIBDIR = $(SSLDIR)
!endif
SSLLFLAGS = /nologo /opt:ref /debug
SSLLIB    = ssleay32.lib libeay32.lib user32.lib gdi32.lib crypt32.lib
SSLLIB    = libssl.lib libcrypto.lib user32.lib gdi32.lib crypt32.lib
!if "$(PLATFORM)"=="amd64" || "$(PLATFORM)"=="x64"
!message Using 'x64' platform for OpenSSL...
# BUGBUG (OpenSSL): Using "no-ssl*" here breaks the build.
# SSLCONFIG = VC-WIN64A no-asm no-ssl2 no-ssl3 no-weak-ssl-ciphers
SSLCONFIG = VC-WIN64A no-asm
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
SSLSETUP  = ms\do_win64a.bat
!if $(FOSSIL_DYNAMIC_BUILD)!=0
SSLNMAKE  = ms\ntdll.mak all
!else
SSLNMAKE  = ms\nt.mak all
!endif
# BUGBUG (OpenSSL): Using "OPENSSL_NO_SSL*" here breaks dynamic builds.
!if $(FOSSIL_DYNAMIC_BUILD)==0
SSLCFLAGS = -DOPENSSL_NO_SSL2 -DOPENSSL_NO_SSL3 -DOPENSSL_NO_WEAK_SSL_CIPHERS
!endif
!elseif "$(PLATFORM)"=="ia64"
!message Using 'ia64' platform for OpenSSL...
# BUGBUG (OpenSSL): Using "no-ssl*" here breaks the build.
# SSLCONFIG = VC-WIN64I no-asm no-ssl2 no-ssl3 no-weak-ssl-ciphers
SSLCONFIG = VC-WIN64I no-asm
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
SSLSETUP  = ms\do_win64i.bat
!if $(FOSSIL_DYNAMIC_BUILD)!=0
SSLNMAKE  = ms\ntdll.mak all
!else
SSLNMAKE  = ms\nt.mak all
!endif
# BUGBUG (OpenSSL): Using "OPENSSL_NO_SSL*" here breaks dynamic builds.
!if $(FOSSIL_DYNAMIC_BUILD)==0
SSLCFLAGS = -DOPENSSL_NO_SSL2 -DOPENSSL_NO_SSL3 -DOPENSSL_NO_WEAK_SSL_CIPHERS
!endif
!else
!message Assuming 'x86' platform for OpenSSL...
# BUGBUG (OpenSSL): Using "no-ssl*" here breaks the build.
# SSLCONFIG = VC-WIN32 no-asm no-ssl2 no-ssl3 no-weak-ssl-ciphers
SSLCONFIG = VC-WIN32 no-asm
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
SSLSETUP  = ms\do_ms.bat
!if $(FOSSIL_DYNAMIC_BUILD)!=0
SSLNMAKE  = ms\ntdll.mak all
!else
SSLNMAKE  = ms\nt.mak all
!endif
# BUGBUG (OpenSSL): Using "OPENSSL_NO_SSL*" here breaks dynamic builds.
!if $(FOSSIL_DYNAMIC_BUILD)==0
SSLCFLAGS = -DOPENSSL_NO_SSL2 -DOPENSSL_NO_SSL3 -DOPENSSL_NO_WEAK_SSL_CIPHERS
!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$(SRCDIR) /I$B\win\include
INCL      = /I. /I"$(OX)" /I"$(SRCDIR)" /I"$(SRCDIR_extsrc)" /I"$(B)\win\include"

!if $(FOSSIL_ENABLE_MINIZ)==0
INCL      = $(INCL) /I$(ZINCDIR)
INCL      = $(INCL) /I"$(ZINCDIR)"
!endif

!if $(FOSSIL_ENABLE_SSL)!=0
INCL      = $(INCL) /I$(SSLINCDIR)
INCL      = $(INCL) /I"$(SSLINCDIR)"
!endif

!if $(FOSSIL_ENABLE_TCL)!=0
INCL      = $(INCL) /I$(TCLINCDIR)
INCL      = $(INCL) /I"$(TCLINCDIR)"
!endif

CFLAGS    = /nologo
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) /SUBSYSTEM:CONSOLE,5.02
XPLDFLAGS = $(XPLDFLAGS) /OSVERSION:5.02 /SUBSYSTEM:CONSOLE,5.02
!else
XPLDFLAGS = $(XPLDFLAGS) /SUBSYSTEM:CONSOLE,5.01
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) /Od
CFLAGS    = $(CFLAGS) /Zi $(CRTFLAGS) $(DBGOPTS) /DFOSSIL_DEBUG /DTH_MEMDEBUG
LDFLAGS   = $(LDFLAGS) /DEBUG
!else
CFLAGS    = $(CFLAGS) $(CRTFLAGS) /O2
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

!if $(FOSSIL_ENABLE_MINIZ)==0
LIBS      = $(LIBS) $(ZLIB)
LIBDIR    = $(LIBDIR) /LIBPATH:$(ZLIBDIR)
LIBDIR    = $(LIBDIR) /LIBPATH:"$(ZLIBDIR)"
!endif

!if $(FOSSIL_ENABLE_MINIZ)!=0
TCC       = $(TCC) /DFOSSIL_ENABLE_MINIZ=1
RCC       = $(RCC) /DFOSSIL_ENABLE_MINIZ=1
!endif

!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)
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_LEGACY_MV_RM)!=0
TCC       = $(TCC) /DFOSSIL_ENABLE_LEGACY_MV_RM=1
RCC       = $(RCC) /DFOSSIL_ENABLE_LEGACY_MV_RM=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
310
311
312
313
314
315
316

317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339


340
341
342

343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365


366
367
368
369
370
371
372
373
374
375
376
377

378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
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









296
297
298
299
300
301
302
303
304
305
306
307
308
309

310
311
312
313

314
315
316
317
318

319
320
321

322
323
324
325
326
327
328
329
330
331
332
333
334

335
336
337
338

339
340
341
342
343

344
345
346

347
348
349
350
351
352
353
354
355
356
357
358
359
360

361


362






























































































































363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
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







+






-




-





-



-

+
+



+






-




-





-



-

+
+











-
+
-
-

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
-
-
-
-
+
+
+
+

+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-

-
+
-
-
-
-
+
-









+
+
+



-
+
-
+

+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+


-


-








-
+
-
-
-
-
-
+
+
+
+

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
-
-
-
-
+
+
+
-
-
-


-
-
-
+
-
-
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
-
+
-
-
+




-
+


-
-
+
+


-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

+
-
-
+
+
+
+

-
-
+
+

-
-
+
+

-
-
-
+
+
-
-

-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
-
+
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+
+
+
+

+
+
-
-
+
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
-
+
+
+
+
-
-
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+
+
+
+

+
+
-
-
+
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
-
+
+
+
+
-
-
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+
+
+
+

+
+
-
-
+
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

+
+
-
-
+
+
+
+
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+
+
+
+

+
+
-
-
+
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+
+
+
+

+
+
-
-
+
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+
+
+
+

+
+
-
-
+
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+
+
+
+

+
+
-
-
+
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+
+
+
+

+
+
-
-
+
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+
+
+
+

+
+
-
-
+
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+
+
+
+

+
+
-
-
+
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

+
+
-
-
+
+
+
+
+
+

-
-
+
+
+
+
+

+
+
-
-
+
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+
+
+
+

+
+
-
-
+
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+
+
+
+

+
+
-
-
+
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+
+
+
+

+
+
-
-
+
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+
+
+
+

+
+
-
-
+
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
-
+
+
+
+
-
-
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+
+
+
+

+
+
-
-
+
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
-
+
+
+
+
-
-
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+
+
+
+

+
+
-
-
+
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
-
+
+
+
+
-
-
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+
+
+
+

+
+
-
-
+
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

+
+
-
-
+
+
+
+
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

+
+
-
-
+
+
+
+
+
+

-
-
+
+

-
-
+
+

-
-
+
+

+
+
-
-
+
+
+
+
+
+

-
-
+
+

-
-
+
+

-
-
+
+

+
+
-
-
+
+
+
+
+
+

-
-
+
+

-
-
+
+

-
-
+
+

+
+
-
-
+
+
+
+
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+
+
+
+

+
+
+
-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
+
+

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+

!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_GET_TABLE \
                 /DSQLITE_OMIT_PROGRESS_CALLBACK \
                 /DSQLITE_OMIT_SHARED_CACHE \
                 /DSQLITE_OMIT_LOAD_EXTENSION \
                 /DSQLITE_MAX_EXPR_DEPTH=0 \
                 /DSQLITE_USE_ALLOCA \
                 /DSQLITE_ENABLE_LOCKING_STYLE=0 \
                 /DSQLITE_DEFAULT_FILE_FORMAT=4 \
                 /DSQLITE_ENABLE_EXPLAIN_COMMENTS \
                 /DSQLITE_ENABLE_FTS4 \
                 /DSQLITE_ENABLE_DBSTAT_VTAB \
                 /DSQLITE_ENABLE_JSON1 \
                 /DSQLITE_ENABLE_FTS5 \
                 /DSQLITE_ENABLE_STMTVTAB \
                 /DSQLITE_HAVE_ZLIB \
                 /DSQLITE_INTROSPECTION_PRAGMAS \
                 /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_GET_TABLE \
                /DSQLITE_OMIT_PROGRESS_CALLBACK \
                /DSQLITE_OMIT_SHARED_CACHE \
                /DSQLITE_OMIT_LOAD_EXTENSION \
                /DSQLITE_MAX_EXPR_DEPTH=0 \
                /DSQLITE_USE_ALLOCA \
                /DSQLITE_ENABLE_LOCKING_STYLE=0 \
                /DSQLITE_DEFAULT_FILE_FORMAT=4 \
                /DSQLITE_ENABLE_EXPLAIN_COMMENTS \
                /DSQLITE_ENABLE_FTS4 \
                /DSQLITE_ENABLE_DBSTAT_VTAB \
                /DSQLITE_ENABLE_JSON1 \
                /DSQLITE_ENABLE_FTS5 \
                /DSQLITE_ENABLE_STMTVTAB \
                /DSQLITE_HAVE_ZLIB \
                /DSQLITE_INTROSPECTION_PRAGMAS \
                /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

MINIZ_OPTIONS = /DMINIZ_NO_STDIO \
PIKCHR_OPTIONS = /DPIKCHR_TOKEN_LIMIT=10000
                /DMINIZ_NO_TIME \
                /DMINIZ_NO_ARCHIVE_APIS

SRC   = add_.c \
        alerts_.c \
        allrepo_.c \
        attach_.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 \
        checkin_.c \
        checkout_.c \
        clearsign_.c \
        clone_.c \
        comformat_.c \
        configure_.c \
        content_.c \
        cookies_.c \
        db_.c \
        delta_.c \
        deltacmd_.c \
        descendants_.c \
        diff_.c \
        diffcmd_.c \
        dispatch_.c \
        doc_.c \
        encode_.c \
        etag_.c \
        event_.c \
        export_.c \
        file_.c \
        finfo_.c \
        foci_.c \
        forum_.c \
        fshell_.c \
        fusefs_.c \
        glob_.c \
        graph_.c \
        gzip_.c \
        hname_.c \
        http_.c \
        http_socket_.c \
        http_ssl_.c \
        http_transport_.c \
        import_.c \
        info_.c \
        json_.c \
        json_artifact_.c \
        json_branch_.c \
        json_config_.c \
        json_diff_.c \
        json_dir_.c \
        json_finfo_.c \
        json_login_.c \
        json_query_.c \
        json_report_.c \
        json_status_.c \
        json_tag_.c \
        json_timeline_.c \
        json_user_.c \
        json_wiki_.c \
        leaf_.c \
        loadctrl_.c \
        login_.c \
        lookslike_.c \
        main_.c \
        manifest_.c \
        markdown_.c \
        markdown_html_.c \
        md5_.c \
        merge_.c \
        merge3_.c \
        moderate_.c \
        name_.c \
        path_.c \
        piechart_.c \
        pivot_.c \
        popen_.c \
        pqueue_.c \
        printf_.c \
        publish_.c \
        purge_.c \
        rebuild_.c \
        regexp_.c \
        report_.c \
        rss_.c \
        schema_.c \
        search_.c \
        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 \
        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 \
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" \
        webmail_.c \
        wiki_.c \
        wikiformat_.c \
        winfile_.c \
        winhttp_.c \
        "$(OX)\wiki_.c" \
        "$(OX)\wikiformat_.c" \
        "$(OX)\winfile_.c" \
        "$(OX)\winhttp_.c" \
        wysiwyg_.c \
        xfer_.c \
        xfersetup_.c \
        zip_.c
        "$(OX)\xfer_.c" \
        "$(OX)\xfersetup_.c" \
        "$(OX)\zip_.c" \
        "$(SRCDIR_extsrc)\pikchr.c"

EXTRA_FILES   = "$(SRCDIR)\..\extsrc\pikchr-worker.js" \
EXTRA_FILES   = $(SRCDIR)\..\skins\aht\details.txt \
        $(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\blitz_no_logo\css.txt \
        $(SRCDIR)\..\skins\blitz_no_logo\details.txt \
        $(SRCDIR)\..\skins\blitz_no_logo\footer.txt \
        $(SRCDIR)\..\skins\blitz_no_logo\header.txt \
        $(SRCDIR)\..\skins\blitz_no_logo\ticket.txt \
        $(SRCDIR)\..\skins\bootstrap\css.txt \
        $(SRCDIR)\..\skins\bootstrap\details.txt \
        $(SRCDIR)\..\skins\bootstrap\footer.txt \
        $(SRCDIR)\..\skins\bootstrap\header.txt \
        $(SRCDIR)\..\skins\default\css.txt \
        $(SRCDIR)\..\skins\default\details.txt \
        $(SRCDIR)\..\skins\default\footer.txt \
        $(SRCDIR)\..\skins\default\header.txt \
        $(SRCDIR)\..\skins\default\js.txt \
        $(SRCDIR)\..\skins\eagle\css.txt \
        $(SRCDIR)\..\skins\eagle\details.txt \
        $(SRCDIR)\..\skins\eagle\footer.txt \
        $(SRCDIR)\..\skins\eagle\header.txt \
        $(SRCDIR)\..\skins\enhanced1\css.txt \
        $(SRCDIR)\..\skins\enhanced1\details.txt \
        $(SRCDIR)\..\skins\enhanced1\footer.txt \
        $(SRCDIR)\..\skins\enhanced1\header.txt \
        $(SRCDIR)\..\skins\khaki\css.txt \
        $(SRCDIR)\..\skins\khaki\details.txt \
        $(SRCDIR)\..\skins\khaki\footer.txt \
        $(SRCDIR)\..\skins\khaki\header.txt \
        $(SRCDIR)\..\skins\original\css.txt \
        $(SRCDIR)\..\skins\original\details.txt \
        $(SRCDIR)\..\skins\original\footer.txt \
        $(SRCDIR)\..\skins\original\header.txt \
        $(SRCDIR)\..\skins\plain_gray\css.txt \
        $(SRCDIR)\..\skins\plain_gray\details.txt \
        $(SRCDIR)\..\skins\plain_gray\footer.txt \
        $(SRCDIR)\..\skins\plain_gray\header.txt \
        $(SRCDIR)\..\skins\rounded1\css.txt \
        $(SRCDIR)\..\skins\rounded1\details.txt \
        $(SRCDIR)\..\skins\rounded1\footer.txt \
        $(SRCDIR)\..\skins\rounded1\header.txt \
        $(SRCDIR)\..\skins\xekri\css.txt \
        $(SRCDIR)\..\skins\xekri\details.txt \
        $(SRCDIR)\..\skins\xekri\footer.txt \
        $(SRCDIR)\..\skins\xekri\header.txt \
        $(SRCDIR)\ci_edit.js \
        $(SRCDIR)\diff.tcl \
        $(SRCDIR)\forum.js \
        $(SRCDIR)\graph.js \
        $(SRCDIR)\href.js \
        $(SRCDIR)\login.js \
        $(SRCDIR)\markdown.md \
        $(SRCDIR)\menu.js \
        $(SRCDIR)\sbsdiff.js \
        $(SRCDIR)\scroll.js \
        $(SRCDIR)\skin.js \
        $(SRCDIR)\sorttable.js \
        $(SRCDIR)\tree.js \
        $(SRCDIR)\useredit.js \
        $(SRCDIR)\wiki.wiki
        "$(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)\alerts$O \
        $(OX)\allrepo$O \
        $(OX)\attach$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)\checkin$O \
        $(OX)\checkout$O \
        $(OX)\clearsign$O \
        $(OX)\clone$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)\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)\file$O \
        $(OX)\finfo$O \
        $(OX)\foci$O \
        $(OX)\forum$O \
        $(OX)\fshell$O \
        $(OX)\fusefs$O \
        $(OX)\glob$O \
        $(OX)\graph$O \
        $(OX)\gzip$O \
        $(OX)\hname$O \
        $(OX)\http$O \
        $(OX)\http_socket$O \
        $(OX)\http_ssl$O \
        $(OX)\http_transport$O \
        $(OX)\import$O \
        $(OX)\info$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)\path$O \
        $(OX)\piechart$O \
        $(OX)\pivot$O \
        $(OX)\popen$O \
        $(OX)\pqueue$O \
        $(OX)\printf$O \
        $(OX)\publish$O \
        $(OX)\purge$O \
        $(OX)\rebuild$O \
        $(OX)\regexp$O \
        $(OX)\report$O \
        $(OX)\rss$O \
        $(OX)\schema$O \
        $(OX)\search$O \
        $(OX)\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)\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 \
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)\webmail$O \
        $(OX)\wiki$O \
        $(OX)\wikiformat$O \
        $(OX)\winfile$O \
        $(OX)\winhttp$O \
        "$(OX)\wiki$O" \
        "$(OX)\wikiformat$O" \
        "$(OX)\winfile$O" \
        "$(OX)\winhttp$O" \
        $(OX)\wysiwyg$O \
        $(OX)\xfer$O \
        $(OX)\xfersetup$O \
        $(OX)\zip$O \
!if $(FOSSIL_ENABLE_MINIZ)!=0
        $(OX)\miniz$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)\fossil.res


$(OX):
APPNAME    = $(OX)\fossil$(E)
PDBNAME    = $(OX)\fossil$(P)
APPTARGETS =

	@-mkdir $@
all: $(OX) $(APPNAME)

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 "$(PERLDIR)" != ""
!if $(FOSSIL_ENABLE_WINXP)!=0
	@set PATH=$(PERLDIR);$(PATH)
	@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
	@pushd "$(SSLDIR)" && call $(SSLSETUP) && popd
!if $(FOSSIL_ENABLE_WINXP)!=0
	@pushd "$(SSLDIR)" && $(MAKE) /f $(SSLNMAKE) "CC=cl $(SSLCFLAGS) $(XPCFLAGS)" "LFLAGS=$(SSLLFLAGS) $(XPLDFLAGS)" && popd
!else
	@pushd "$(SSLDIR)" && $(MAKE) /f $(SSLNMAKE) "CC=cl $(SSLCFLAGS)" && popd
	@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_ENABLE_MINIZ)==0
!if $(FOSSIL_BUILD_ZLIB)!=0
APPTARGETS = $(APPTARGETS) zlib
!endif
!endif

!if $(FOSSIL_ENABLE_SSL)!=0
!if $(FOSSIL_BUILD_SSL)!=0
APPTARGETS = $(APPTARGETS) openssl
!endif
!endif

$(APPNAME) : $(APPTARGETS) translate$E mkindex$E codecheck1$E headers $(OBJ) $(OX)\linkopts
"$(APPNAME)" : $(APPTARGETS) "$(OBJDIR)\translate$E" "$(OBJDIR)\mkindex$E" "$(OBJDIR)\codecheck1$E" "$(OX)\headers" $(OBJ) "$(OX)\linkopts"
	cd $(OX)
	codecheck1$E $(SRC)
	link $(LDFLAGS) /OUT:$@ $(LIBDIR) Wsetargv.obj fossil.res @linkopts
	if exist $@.manifest \
		$(MTC) -nologo -manifest $@.manifest -outputresource:$@;1
	"$(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)\alerts.obj >> $@
	echo $(OX)\allrepo.obj >> $@
	echo $(OX)\attach.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)\checkin.obj >> $@
	echo $(OX)\checkout.obj >> $@
	echo $(OX)\clearsign.obj >> $@
	echo $(OX)\clone.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)\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)\file.obj >> $@
	echo $(OX)\finfo.obj >> $@
	echo $(OX)\foci.obj >> $@
	echo $(OX)\forum.obj >> $@
	echo $(OX)\fshell.obj >> $@
	echo $(OX)\fusefs.obj >> $@
	echo $(OX)\glob.obj >> $@
	echo $(OX)\graph.obj >> $@
	echo $(OX)\gzip.obj >> $@
	echo $(OX)\hname.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)\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)\path.obj >> $@
	echo $(OX)\piechart.obj >> $@
	echo $(OX)\pivot.obj >> $@
	echo $(OX)\popen.obj >> $@
	echo $(OX)\pqueue.obj >> $@
	echo $(OX)\printf.obj >> $@
	echo $(OX)\publish.obj >> $@
	echo $(OX)\purge.obj >> $@
	echo $(OX)\rebuild.obj >> $@
	echo $(OX)\regexp.obj >> $@
	echo $(OX)\report.obj >> $@
	echo $(OX)\rss.obj >> $@
	echo $(OX)\schema.obj >> $@
	echo $(OX)\search.obj >> $@
	echo $(OX)\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)\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 >> $@
"$(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)\webmail.obj >> $@
	echo $(OX)\wiki.obj >> $@
	echo $(OX)\wikiformat.obj >> $@
	echo $(OX)\winfile.obj >> $@
	echo $(OX)\winhttp.obj >> $@
	echo "$(OX)\wiki.obj" >> $@
	echo "$(OX)\wikiformat.obj" >> $@
	echo "$(OX)\winfile.obj" >> $@
	echo "$(OX)\winhttp.obj" >> $@
	echo $(OX)\wysiwyg.obj >> $@
	echo $(OX)\xfer.obj >> $@
	echo $(OX)\xfersetup.obj >> $@
	echo $(OX)\zip.obj >> $@
	echo "$(OX)\xfer.obj" >> $@
	echo "$(OX)\xfersetup.obj" >> $@
	echo "$(OX)\zip.obj" >> $@
!if $(FOSSIL_ENABLE_MINIZ)!=0
	echo $(OX)\miniz.obj >> $@
!endif
	echo $(LIBS) >> $@

$(OX):
	@-mkdir $@

"$(OBJDIR)\translate$E": "$(SRCDIR_tools)\translate.c"
translate$E: $(SRCDIR)\translate.c
	$(BCC) $**
	$(BCC) /Fe$@ /Fo$(@D)\ /Fd$(@D)\ $**

makeheaders$E: $(SRCDIR)\makeheaders.c
	$(BCC) $**
"$(OBJDIR)\makeheaders$E": "$(SRCDIR_tools)\makeheaders.c"
	$(BCC) /Fe$@ /Fo$(@D)\ /Fd$(@D)\ $**

mkindex$E: $(SRCDIR)\mkindex.c
	$(BCC) $**
"$(OBJDIR)\mkindex$E": "$(SRCDIR_tools)\mkindex.c"
	$(BCC) /Fe$@ /Fo$(@D)\ /Fd$(@D)\ $**

mkbuiltin$E: $(SRCDIR)\mkbuiltin.c
	$(BCC) $**
"$(OBJDIR)\mkbuiltin$E": "$(SRCDIR_tools)\mkbuiltin.c"
	$(BCC) /Fe$@ /Fo$(@D)\ /Fd$(@D)\ $**

mkversion$E: $(SRCDIR)\mkversion.c
	$(BCC) $**
"$(OBJDIR)\mkversion$E": "$(SRCDIR_tools)\mkversion.c"
	$(BCC) /Fe$@ /Fo$(@D)\ /Fd$(@D)\ $**

mkcss$E: $(SRCDIR)\mkcss.c
	$(BCC) $**

"$(OBJDIR)\codecheck1$E": "$(SRCDIR_tools)\codecheck1.c"
codecheck1$E: $(SRCDIR)\codecheck1.c
	$(BCC) $**
	$(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)\sqlite3-see.c
SQLITE3_SRC = $(SRCDIR_extsrc)\sqlite3-see.c
!else
SEE_FLAGS =
SQLITE3_SHELL_SRC = $(SRCDIR)\shell.c
SQLITE3_SRC = $(SRCDIR)\sqlite3.c
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$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) $(SEE_FLAGS) -c $(SQLITE3_SHELL_SRC)
"$(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$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SEE_FLAGS) $(SQLITE3_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$@ -c $**
"$(OX)\th$O" : "$(SRCDIR)\th.c"
	$(TCC) /Fo$@ /Fd$(@D)\ -c $**

$(OX)\th_lang$O : $(SRCDIR)\th_lang.c
	$(TCC) /Fo$@ -c $**
"$(OX)\th_lang$O" : "$(SRCDIR)\th_lang.c"
	$(TCC) /Fo$@ /Fd$(@D)\ -c $**

$(OX)\th_tcl$O : $(SRCDIR)\th_tcl.c
	$(TCC) /Fo$@ -c $**
"$(OX)\th_tcl$O" : "$(SRCDIR)\th_tcl.c"
	$(TCC) /Fo$@ /Fd$(@D)\ -c $**

$(OX)\miniz$O : $(SRCDIR)\miniz.c
	$(TCC) /Fo$@ -c $(MINIZ_OPTIONS) $(SRCDIR)\miniz.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"
VERSION.h : mkversion$E $B\manifest.uuid $B\manifest $B\VERSION
	$** > $@
	"$(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)\cson_amalgamation.c
	$(TCC) /Fo$@ /c $**
"$(OX)\cson_amalgamation$O" : "$(SRCDIR_extsrc)\cson_amalgamation.c"
	$(TCC) /Fo$@ /Fd$(@D)\ -c $**

default_css.h: mkcss$E $(SRCDIR)\default_css.txt
	$** $@
"$(OX)\page_index.h": "$(OBJDIR)\mkindex$E" $(SRC)
	$** > $@

page_index.h: mkindex$E $(SRC)
	$** > $@

"$(OX)\builtin_data.h":	"$(OBJDIR)\mkbuiltin$E" "$(OX)\builtin_data.reslist"
	"$(OBJDIR)\mkbuiltin$E" --prefix "$(SRCDIR)/" --reslist "$(OX)\builtin_data.reslist" > $@
builtin_data.h:	mkbuiltin$E $(EXTRA_FILES)
	mkbuiltin$E --prefix $(SRCDIR)/ $(EXTRA_FILES) > $@

clean:
	-del $(OX)\*.obj 2>NUL
	-del *.obj 2>NUL
	-del *_.c 2>NUL
	-del *.h 2>NUL
	-del *.ilk 2>NUL
	-del *.map 2>NUL
	-del *.res 2>NUL
	-del headers 2>NUL
	-del linkopts 2>NUL
	-del vc*.pdb 2>NUL
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

realclean: clean
	-del $(APPNAME) 2>NUL
	-del $(PDBNAME) 2>NUL
	-del translate$E 2>NUL
	-del translate$P 2>NUL
	-del mkindex$E 2>NUL
	-del mkindex$P 2>NUL
	-del makeheaders$E 2>NUL
	-del makeheaders$P 2>NUL
	-del mkversion$E 2>NUL
	-del mkversion$P 2>NUL
	-del mkcss$E 2>NUL
	-del mkcss$P 2>NUL
	-del codecheck1$E 2>NUL
	-del codecheck1$P 2>NUL
	-del mkbuiltin$E 2>NUL
	-del mkbuiltin$P 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$O : $(SRCDIR)\json_detail.h
"$(OBJDIR)\json_diff$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_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)\json_finfo$O" : "$(SRCDIR)\json_detail.h"
"$(OBJDIR)\json_login$O" : "$(SRCDIR)\json_detail.h"
"$(OBJDIR)\json_query$O" : "$(SRCDIR)\json_detail.h"
"$(OBJDIR)\json_report$O" : "$(SRCDIR)\json_detail.h"
"$(OBJDIR)\json_status$O" : "$(SRCDIR)\json_detail.h"
"$(OBJDIR)\json_tag$O" : "$(SRCDIR)\json_detail.h"
"$(OBJDIR)\json_timeline$O" : "$(SRCDIR)\json_detail.h"
"$(OBJDIR)\json_user$O" : "$(SRCDIR)\json_detail.h"
"$(OBJDIR)\json_wiki$O" : "$(SRCDIR)\json_detail.h"

$(OX)\add$O : add_.c add.h
	$(TCC) /Fo$@ -c add_.c

add_.c : $(SRCDIR)\add.c
	translate$E $** > $@

$(OX)\alerts$O : alerts_.c alerts.h
	$(TCC) /Fo$@ -c alerts_.c

alerts_.c : $(SRCDIR)\alerts.c
	translate$E $** > $@

$(OX)\allrepo$O : allrepo_.c allrepo.h
	$(TCC) /Fo$@ -c allrepo_.c

allrepo_.c : $(SRCDIR)\allrepo.c
	translate$E $** > $@

$(OX)\attach$O : attach_.c attach.h
	$(TCC) /Fo$@ -c attach_.c

attach_.c : $(SRCDIR)\attach.c
	translate$E $** > $@

$(OX)\backoffice$O : backoffice_.c backoffice.h
	$(TCC) /Fo$@ -c backoffice_.c

backoffice_.c : $(SRCDIR)\backoffice.c
	translate$E $** > $@

$(OX)\bag$O : bag_.c bag.h
	$(TCC) /Fo$@ -c bag_.c

bag_.c : $(SRCDIR)\bag.c
	translate$E $** > $@

$(OX)\bisect$O : bisect_.c bisect.h
	$(TCC) /Fo$@ -c bisect_.c

bisect_.c : $(SRCDIR)\bisect.c
	translate$E $** > $@

$(OX)\blob$O : blob_.c blob.h
	$(TCC) /Fo$@ -c blob_.c

blob_.c : $(SRCDIR)\blob.c
	translate$E $** > $@

$(OX)\branch$O : branch_.c branch.h
	$(TCC) /Fo$@ -c branch_.c

branch_.c : $(SRCDIR)\branch.c
	translate$E $** > $@

$(OX)\browse$O : browse_.c browse.h
	$(TCC) /Fo$@ -c browse_.c

browse_.c : $(SRCDIR)\browse.c
	translate$E $** > $@

$(OX)\builtin$O : builtin_.c builtin.h
	$(TCC) /Fo$@ -c builtin_.c

builtin_.c : $(SRCDIR)\builtin.c
	translate$E $** > $@

$(OX)\bundle$O : bundle_.c bundle.h
	$(TCC) /Fo$@ -c bundle_.c

bundle_.c : $(SRCDIR)\bundle.c
	translate$E $** > $@

$(OX)\cache$O : cache_.c cache.h
	$(TCC) /Fo$@ -c cache_.c

cache_.c : $(SRCDIR)\cache.c
	translate$E $** > $@

$(OX)\capabilities$O : capabilities_.c capabilities.h
	$(TCC) /Fo$@ -c capabilities_.c

capabilities_.c : $(SRCDIR)\capabilities.c
	translate$E $** > $@
"$(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)\captcha$O : captcha_.c captcha.h
	$(TCC) /Fo$@ -c captcha_.c
"$(OX)\add$O" : "$(OX)\add_.c" "$(OX)\add.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\add_.c"

captcha_.c : $(SRCDIR)\captcha.c
	translate$E $** > $@
"$(OX)\add_.c" : "$(SRCDIR)\add.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\cgi$O : cgi_.c cgi.h
	$(TCC) /Fo$@ -c cgi_.c
"$(OX)\ajax$O" : "$(OX)\ajax_.c" "$(OX)\ajax.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\ajax_.c"

cgi_.c : $(SRCDIR)\cgi.c
	translate$E $** > $@
"$(OX)\ajax_.c" : "$(SRCDIR)\ajax.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\checkin$O : checkin_.c checkin.h
	$(TCC) /Fo$@ -c checkin_.c
"$(OX)\alerts$O" : "$(OX)\alerts_.c" "$(OX)\alerts.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\alerts_.c"

checkin_.c : $(SRCDIR)\checkin.c
	translate$E $** > $@
"$(OX)\alerts_.c" : "$(SRCDIR)\alerts.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\checkout$O : checkout_.c checkout.h
	$(TCC) /Fo$@ -c checkout_.c
"$(OX)\allrepo$O" : "$(OX)\allrepo_.c" "$(OX)\allrepo.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\allrepo_.c"

checkout_.c : $(SRCDIR)\checkout.c
	translate$E $** > $@
"$(OX)\allrepo_.c" : "$(SRCDIR)\allrepo.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\clearsign$O : clearsign_.c clearsign.h
	$(TCC) /Fo$@ -c clearsign_.c
"$(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"
clearsign_.c : $(SRCDIR)\clearsign.c
	translate$E $** > $@

"$(OX)\backlink_.c" : "$(SRCDIR)\backlink.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\clone$O : clone_.c clone.h
	$(TCC) /Fo$@ -c clone_.c
"$(OX)\backoffice$O" : "$(OX)\backoffice_.c" "$(OX)\backoffice.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\backoffice_.c"

clone_.c : $(SRCDIR)\clone.c
	translate$E $** > $@
"$(OX)\backoffice_.c" : "$(SRCDIR)\backoffice.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\comformat$O : comformat_.c comformat.h
	$(TCC) /Fo$@ -c comformat_.c
"$(OX)\bag$O" : "$(OX)\bag_.c" "$(OX)\bag.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\bag_.c"

comformat_.c : $(SRCDIR)\comformat.c
	translate$E $** > $@

"$(OX)\bag_.c" : "$(SRCDIR)\bag.c"
	"$(OBJDIR)\translate$E" $** > $@

"$(OX)\bisect$O" : "$(OX)\bisect_.c" "$(OX)\bisect.h"
$(OX)\configure$O : configure_.c configure.h
	$(TCC) /Fo$@ -c configure_.c
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\bisect_.c"

configure_.c : $(SRCDIR)\configure.c
	translate$E $** > $@
"$(OX)\bisect_.c" : "$(SRCDIR)\bisect.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\content$O : content_.c content.h
	$(TCC) /Fo$@ -c content_.c
"$(OX)\blob$O" : "$(OX)\blob_.c" "$(OX)\blob.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\blob_.c"

content_.c : $(SRCDIR)\content.c
	translate$E $** > $@
"$(OX)\blob_.c" : "$(SRCDIR)\blob.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\cookies$O : cookies_.c cookies.h
	$(TCC) /Fo$@ -c cookies_.c
"$(OX)\branch$O" : "$(OX)\branch_.c" "$(OX)\branch.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\branch_.c"

cookies_.c : $(SRCDIR)\cookies.c
	translate$E $** > $@
"$(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)\db$O : db_.c db.h
	$(TCC) /Fo$@ -c db_.c

"$(OX)\builtin$O" : "$(OX)\builtin_.c" "$(OX)\builtin.h" "$(OX)\builtin_data.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\builtin_.c"

db_.c : $(SRCDIR)\db.c
	translate$E $** > $@
"$(OX)\builtin_.c" : "$(SRCDIR)\builtin.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\delta$O : delta_.c delta.h
	$(TCC) /Fo$@ -c delta_.c
"$(OX)\bundle$O" : "$(OX)\bundle_.c" "$(OX)\bundle.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\bundle_.c"

delta_.c : $(SRCDIR)\delta.c
	translate$E $** > $@
"$(OX)\bundle_.c" : "$(SRCDIR)\bundle.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\deltacmd$O : deltacmd_.c deltacmd.h
	$(TCC) /Fo$@ -c deltacmd_.c

"$(OX)\cache$O" : "$(OX)\cache_.c" "$(OX)\cache.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\cache_.c"

"$(OX)\cache_.c" : "$(SRCDIR)\cache.c"
deltacmd_.c : $(SRCDIR)\deltacmd.c
	translate$E $** > $@
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\descendants$O : descendants_.c descendants.h
	$(TCC) /Fo$@ -c descendants_.c
"$(OX)\capabilities$O" : "$(OX)\capabilities_.c" "$(OX)\capabilities.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\capabilities_.c"

descendants_.c : $(SRCDIR)\descendants.c
	translate$E $** > $@
"$(OX)\capabilities_.c" : "$(SRCDIR)\capabilities.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\diff$O : diff_.c diff.h
	$(TCC) /Fo$@ -c diff_.c
"$(OX)\captcha$O" : "$(OX)\captcha_.c" "$(OX)\captcha.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\captcha_.c"

diff_.c : $(SRCDIR)\diff.c
	translate$E $** > $@
"$(OX)\captcha_.c" : "$(SRCDIR)\captcha.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\diffcmd$O : diffcmd_.c diffcmd.h
	$(TCC) /Fo$@ -c diffcmd_.c
"$(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"
diffcmd_.c : $(SRCDIR)\diffcmd.c
	translate$E $** > $@

"$(OX)\chat_.c" : "$(SRCDIR)\chat.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\dispatch$O : dispatch_.c dispatch.h
	$(TCC) /Fo$@ -c dispatch_.c
"$(OX)\checkin$O" : "$(OX)\checkin_.c" "$(OX)\checkin.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\checkin_.c"

dispatch_.c : $(SRCDIR)\dispatch.c
	translate$E $** > $@
"$(OX)\checkin_.c" : "$(SRCDIR)\checkin.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\doc$O : doc_.c doc.h
	$(TCC) /Fo$@ -c doc_.c
"$(OX)\checkout$O" : "$(OX)\checkout_.c" "$(OX)\checkout.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\checkout_.c"

doc_.c : $(SRCDIR)\doc.c
	translate$E $** > $@
"$(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)\encode$O : encode_.c encode.h
	$(TCC) /Fo$@ -c encode_.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"

encode_.c : $(SRCDIR)\encode.c
	translate$E $** > $@
"$(OX)\clone_.c" : "$(SRCDIR)\clone.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\etag$O : etag_.c etag.h
	$(TCC) /Fo$@ -c etag_.c
"$(OX)\color$O" : "$(OX)\color_.c" "$(OX)\color.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\color_.c"

etag_.c : $(SRCDIR)\etag.c
	translate$E $** > $@
"$(OX)\color_.c" : "$(SRCDIR)\color.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\event$O : event_.c event.h
	$(TCC) /Fo$@ -c event_.c
"$(OX)\comformat$O" : "$(OX)\comformat_.c" "$(OX)\comformat.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\comformat_.c"

event_.c : $(SRCDIR)\event.c
	translate$E $** > $@
"$(OX)\comformat_.c" : "$(SRCDIR)\comformat.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\export$O : export_.c export.h
	$(TCC) /Fo$@ -c export_.c
"$(OX)\configure$O" : "$(OX)\configure_.c" "$(OX)\configure.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\configure_.c"

export_.c : $(SRCDIR)\export.c
	translate$E $** > $@
"$(OX)\configure_.c" : "$(SRCDIR)\configure.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\file$O : file_.c file.h
	$(TCC) /Fo$@ -c file_.c
"$(OX)\content$O" : "$(OX)\content_.c" "$(OX)\content.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\content_.c"

file_.c : $(SRCDIR)\file.c
	translate$E $** > $@
"$(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)\finfo$O : finfo_.c finfo.h
	$(TCC) /Fo$@ -c finfo_.c

"$(OX)\db$O" : "$(OX)\db_.c" "$(OX)\db.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\db_.c"

finfo_.c : $(SRCDIR)\finfo.c
	translate$E $** > $@
"$(OX)\db_.c" : "$(SRCDIR)\db.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\foci$O : foci_.c foci.h
	$(TCC) /Fo$@ -c foci_.c
"$(OX)\delta$O" : "$(OX)\delta_.c" "$(OX)\delta.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\delta_.c"

foci_.c : $(SRCDIR)\foci.c
	translate$E $** > $@
"$(OX)\delta_.c" : "$(SRCDIR)\delta.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\forum$O : forum_.c forum.h
	$(TCC) /Fo$@ -c forum_.c
"$(OX)\deltacmd$O" : "$(OX)\deltacmd_.c" "$(OX)\deltacmd.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\deltacmd_.c"

forum_.c : $(SRCDIR)\forum.c
	translate$E $** > $@
"$(OX)\deltacmd_.c" : "$(SRCDIR)\deltacmd.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\fshell$O : fshell_.c fshell.h
	$(TCC) /Fo$@ -c fshell_.c
"$(OX)\deltafunc$O" : "$(OX)\deltafunc_.c" "$(OX)\deltafunc.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\deltafunc_.c"

fshell_.c : $(SRCDIR)\fshell.c
	translate$E $** > $@
"$(OX)\deltafunc_.c" : "$(SRCDIR)\deltafunc.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\fusefs$O : fusefs_.c fusefs.h
	$(TCC) /Fo$@ -c fusefs_.c
"$(OX)\descendants$O" : "$(OX)\descendants_.c" "$(OX)\descendants.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\descendants_.c"

fusefs_.c : $(SRCDIR)\fusefs.c
	translate$E $** > $@
"$(OX)\descendants_.c" : "$(SRCDIR)\descendants.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\glob$O : glob_.c glob.h
	$(TCC) /Fo$@ -c glob_.c
"$(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"
glob_.c : $(SRCDIR)\glob.c
	translate$E $** > $@

"$(OX)\diffcmd_.c" : "$(SRCDIR)\diffcmd.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\graph$O : graph_.c graph.h
	$(TCC) /Fo$@ -c graph_.c
"$(OX)\dispatch$O" : "$(OX)\dispatch_.c" "$(OX)\dispatch.h" "$(OX)\page_index.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\dispatch_.c"

graph_.c : $(SRCDIR)\graph.c
	translate$E $** > $@
"$(OX)\dispatch_.c" : "$(SRCDIR)\dispatch.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\gzip$O : gzip_.c gzip.h
	$(TCC) /Fo$@ -c gzip_.c
"$(OX)\doc$O" : "$(OX)\doc_.c" "$(OX)\doc.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\doc_.c"

gzip_.c : $(SRCDIR)\gzip.c
	translate$E $** > $@
"$(OX)\doc_.c" : "$(SRCDIR)\doc.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\hname$O : hname_.c hname.h
	$(TCC) /Fo$@ -c hname_.c
"$(OX)\encode$O" : "$(OX)\encode_.c" "$(OX)\encode.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\encode_.c"

hname_.c : $(SRCDIR)\hname.c
	translate$E $** > $@
"$(OX)\encode_.c" : "$(SRCDIR)\encode.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\http$O : http_.c http.h
	$(TCC) /Fo$@ -c http_.c
"$(OX)\etag$O" : "$(OX)\etag_.c" "$(OX)\etag.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\etag_.c"

http_.c : $(SRCDIR)\http.c
	translate$E $** > $@
"$(OX)\etag_.c" : "$(SRCDIR)\etag.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\http_socket$O : http_socket_.c http_socket.h
	$(TCC) /Fo$@ -c http_socket_.c
"$(OX)\event$O" : "$(OX)\event_.c" "$(OX)\event.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\event_.c"

http_socket_.c : $(SRCDIR)\http_socket.c
	translate$E $** > $@
"$(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)\http_ssl$O : http_ssl_.c http_ssl.h
	$(TCC) /Fo$@ -c http_ssl_.c

"$(OX)\extcgi$O" : "$(OX)\extcgi_.c" "$(OX)\extcgi.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\extcgi_.c"

http_ssl_.c : $(SRCDIR)\http_ssl.c
	translate$E $** > $@
"$(OX)\extcgi_.c" : "$(SRCDIR)\extcgi.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\http_transport$O : http_transport_.c http_transport.h
	$(TCC) /Fo$@ -c http_transport_.c
"$(OX)\file$O" : "$(OX)\file_.c" "$(OX)\file.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\file_.c"

http_transport_.c : $(SRCDIR)\http_transport.c
	translate$E $** > $@
"$(OX)\file_.c" : "$(SRCDIR)\file.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\import$O : import_.c import.h
	$(TCC) /Fo$@ -c import_.c
"$(OX)\fileedit$O" : "$(OX)\fileedit_.c" "$(OX)\fileedit.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\fileedit_.c"

import_.c : $(SRCDIR)\import.c
	translate$E $** > $@
"$(OX)\fileedit_.c" : "$(SRCDIR)\fileedit.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\info$O : info_.c info.h
	$(TCC) /Fo$@ -c info_.c
"$(OX)\finfo$O" : "$(OX)\finfo_.c" "$(OX)\finfo.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\finfo_.c"

info_.c : $(SRCDIR)\info.c
	translate$E $** > $@
"$(OX)\finfo_.c" : "$(SRCDIR)\finfo.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\json$O : json_.c json.h
	$(TCC) /Fo$@ -c json_.c
"$(OX)\foci$O" : "$(OX)\foci_.c" "$(OX)\foci.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\foci_.c"

json_.c : $(SRCDIR)\json.c
	translate$E $** > $@
"$(OX)\foci_.c" : "$(SRCDIR)\foci.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\json_artifact$O : json_artifact_.c json_artifact.h
	$(TCC) /Fo$@ -c json_artifact_.c
"$(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"
json_artifact_.c : $(SRCDIR)\json_artifact.c
	translate$E $** > $@

"$(OX)\fshell_.c" : "$(SRCDIR)\fshell.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\json_branch$O : json_branch_.c json_branch.h
	$(TCC) /Fo$@ -c json_branch_.c
"$(OX)\fusefs$O" : "$(OX)\fusefs_.c" "$(OX)\fusefs.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\fusefs_.c"

json_branch_.c : $(SRCDIR)\json_branch.c
	translate$E $** > $@
"$(OX)\fusefs_.c" : "$(SRCDIR)\fusefs.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\json_config$O : json_config_.c json_config.h
	$(TCC) /Fo$@ -c json_config_.c
"$(OX)\fuzz$O" : "$(OX)\fuzz_.c" "$(OX)\fuzz.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\fuzz_.c"

json_config_.c : $(SRCDIR)\json_config.c
	translate$E $** > $@
"$(OX)\fuzz_.c" : "$(SRCDIR)\fuzz.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\json_diff$O : json_diff_.c json_diff.h
	$(TCC) /Fo$@ -c json_diff_.c
"$(OX)\glob$O" : "$(OX)\glob_.c" "$(OX)\glob.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\glob_.c"

json_diff_.c : $(SRCDIR)\json_diff.c
	translate$E $** > $@
"$(OX)\glob_.c" : "$(SRCDIR)\glob.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\json_dir$O : json_dir_.c json_dir.h
	$(TCC) /Fo$@ -c json_dir_.c
"$(OX)\graph$O" : "$(OX)\graph_.c" "$(OX)\graph.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\graph_.c"

json_dir_.c : $(SRCDIR)\json_dir.c
	translate$E $** > $@
"$(OX)\graph_.c" : "$(SRCDIR)\graph.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\json_finfo$O : json_finfo_.c json_finfo.h
	$(TCC) /Fo$@ -c json_finfo_.c
"$(OX)\gzip$O" : "$(OX)\gzip_.c" "$(OX)\gzip.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\gzip_.c"

json_finfo_.c : $(SRCDIR)\json_finfo.c
	translate$E $** > $@
"$(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)\json_login$O : json_login_.c json_login.h
	$(TCC) /Fo$@ -c json_login_.c

"$(OX)\hook$O" : "$(OX)\hook_.c" "$(OX)\hook.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\hook_.c"

json_login_.c : $(SRCDIR)\json_login.c
	translate$E $** > $@
"$(OX)\hook_.c" : "$(SRCDIR)\hook.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\json_query$O : json_query_.c json_query.h
	$(TCC) /Fo$@ -c json_query_.c
"$(OX)\http$O" : "$(OX)\http_.c" "$(OX)\http.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\http_.c"

json_query_.c : $(SRCDIR)\json_query.c
	translate$E $** > $@
"$(OX)\http_.c" : "$(SRCDIR)\http.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\json_report$O : json_report_.c json_report.h
	$(TCC) /Fo$@ -c json_report_.c
"$(OX)\http_socket$O" : "$(OX)\http_socket_.c" "$(OX)\http_socket.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\http_socket_.c"

json_report_.c : $(SRCDIR)\json_report.c
	translate$E $** > $@
"$(OX)\http_socket_.c" : "$(SRCDIR)\http_socket.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\json_status$O : json_status_.c json_status.h
	$(TCC) /Fo$@ -c json_status_.c
"$(OX)\http_ssl$O" : "$(OX)\http_ssl_.c" "$(OX)\http_ssl.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\http_ssl_.c"

json_status_.c : $(SRCDIR)\json_status.c
	translate$E $** > $@
"$(OX)\http_ssl_.c" : "$(SRCDIR)\http_ssl.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\json_tag$O : json_tag_.c json_tag.h
	$(TCC) /Fo$@ -c json_tag_.c
"$(OX)\http_transport$O" : "$(OX)\http_transport_.c" "$(OX)\http_transport.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\http_transport_.c"

json_tag_.c : $(SRCDIR)\json_tag.c
	translate$E $** > $@
"$(OX)\http_transport_.c" : "$(SRCDIR)\http_transport.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\json_timeline$O : json_timeline_.c json_timeline.h
	$(TCC) /Fo$@ -c json_timeline_.c
"$(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"
json_timeline_.c : $(SRCDIR)\json_timeline.c
	translate$E $** > $@

"$(OX)\info_.c" : "$(SRCDIR)\info.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\json_user$O : json_user_.c json_user.h
	$(TCC) /Fo$@ -c json_user_.c
"$(OX)\interwiki$O" : "$(OX)\interwiki_.c" "$(OX)\interwiki.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\interwiki_.c"

json_user_.c : $(SRCDIR)\json_user.c
	translate$E $** > $@
"$(OX)\interwiki_.c" : "$(SRCDIR)\interwiki.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\json_wiki$O : json_wiki_.c json_wiki.h
	$(TCC) /Fo$@ -c json_wiki_.c
"$(OX)\json$O" : "$(OX)\json_.c" "$(OX)\json.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\json_.c"

json_wiki_.c : $(SRCDIR)\json_wiki.c
	translate$E $** > $@
"$(OX)\json_.c" : "$(SRCDIR)\json.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\leaf$O : leaf_.c leaf.h
	$(TCC) /Fo$@ -c leaf_.c
"$(OX)\json_artifact$O" : "$(OX)\json_artifact_.c" "$(OX)\json_artifact.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\json_artifact_.c"

leaf_.c : $(SRCDIR)\leaf.c
	translate$E $** > $@
"$(OX)\json_artifact_.c" : "$(SRCDIR)\json_artifact.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\loadctrl$O : loadctrl_.c loadctrl.h
	$(TCC) /Fo$@ -c loadctrl_.c
"$(OX)\json_branch$O" : "$(OX)\json_branch_.c" "$(OX)\json_branch.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\json_branch_.c"

loadctrl_.c : $(SRCDIR)\loadctrl.c
	translate$E $** > $@
"$(OX)\json_branch_.c" : "$(SRCDIR)\json_branch.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\login$O : login_.c login.h
	$(TCC) /Fo$@ -c login_.c
"$(OX)\json_config$O" : "$(OX)\json_config_.c" "$(OX)\json_config.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\json_config_.c"

login_.c : $(SRCDIR)\login.c
	translate$E $** > $@
"$(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)\lookslike$O : lookslike_.c lookslike.h
	$(TCC) /Fo$@ -c lookslike_.c

"$(OX)\json_dir$O" : "$(OX)\json_dir_.c" "$(OX)\json_dir.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\json_dir_.c"

lookslike_.c : $(SRCDIR)\lookslike.c
	translate$E $** > $@
"$(OX)\json_dir_.c" : "$(SRCDIR)\json_dir.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\main$O : main_.c main.h
	$(TCC) /Fo$@ -c main_.c
"$(OX)\json_finfo$O" : "$(OX)\json_finfo_.c" "$(OX)\json_finfo.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\json_finfo_.c"

main_.c : $(SRCDIR)\main.c
	translate$E $** > $@
"$(OX)\json_finfo_.c" : "$(SRCDIR)\json_finfo.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\manifest$O : manifest_.c manifest.h
	$(TCC) /Fo$@ -c manifest_.c
"$(OX)\json_login$O" : "$(OX)\json_login_.c" "$(OX)\json_login.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\json_login_.c"

manifest_.c : $(SRCDIR)\manifest.c
	translate$E $** > $@
"$(OX)\json_login_.c" : "$(SRCDIR)\json_login.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\markdown$O : markdown_.c markdown.h
	$(TCC) /Fo$@ -c markdown_.c
"$(OX)\json_query$O" : "$(OX)\json_query_.c" "$(OX)\json_query.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\json_query_.c"

markdown_.c : $(SRCDIR)\markdown.c
	translate$E $** > $@
"$(OX)\json_query_.c" : "$(SRCDIR)\json_query.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\markdown_html$O : markdown_html_.c markdown_html.h
	$(TCC) /Fo$@ -c markdown_html_.c
"$(OX)\json_report$O" : "$(OX)\json_report_.c" "$(OX)\json_report.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\json_report_.c"

markdown_html_.c : $(SRCDIR)\markdown_html.c
	translate$E $** > $@
"$(OX)\json_report_.c" : "$(SRCDIR)\json_report.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\md5$O : md5_.c md5.h
	$(TCC) /Fo$@ -c md5_.c
"$(OX)\json_status$O" : "$(OX)\json_status_.c" "$(OX)\json_status.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\json_status_.c"

md5_.c : $(SRCDIR)\md5.c
	translate$E $** > $@
"$(OX)\json_status_.c" : "$(SRCDIR)\json_status.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\merge$O : merge_.c merge.h
	$(TCC) /Fo$@ -c merge_.c
"$(OX)\json_tag$O" : "$(OX)\json_tag_.c" "$(OX)\json_tag.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\json_tag_.c"

merge_.c : $(SRCDIR)\merge.c
	translate$E $** > $@
"$(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)\merge3$O : merge3_.c merge3.h
	$(TCC) /Fo$@ -c merge3_.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"

merge3_.c : $(SRCDIR)\merge3.c
	translate$E $** > $@
"$(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)\moderate$O : moderate_.c moderate.h
	$(TCC) /Fo$@ -c moderate_.c

"$(OX)\leaf$O" : "$(OX)\leaf_.c" "$(OX)\leaf.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\leaf_.c"

moderate_.c : $(SRCDIR)\moderate.c
	translate$E $** > $@
"$(OX)\leaf_.c" : "$(SRCDIR)\leaf.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\name$O : name_.c name.h
	$(TCC) /Fo$@ -c name_.c
"$(OX)\loadctrl$O" : "$(OX)\loadctrl_.c" "$(OX)\loadctrl.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\loadctrl_.c"

name_.c : $(SRCDIR)\name.c
	translate$E $** > $@
"$(OX)\loadctrl_.c" : "$(SRCDIR)\loadctrl.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\path$O : path_.c path.h
	$(TCC) /Fo$@ -c path_.c
"$(OX)\login$O" : "$(OX)\login_.c" "$(OX)\login.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\login_.c"

path_.c : $(SRCDIR)\path.c
	translate$E $** > $@
"$(OX)\login_.c" : "$(SRCDIR)\login.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\piechart$O : piechart_.c piechart.h
	$(TCC) /Fo$@ -c piechart_.c
"$(OX)\lookslike$O" : "$(OX)\lookslike_.c" "$(OX)\lookslike.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\lookslike_.c"

piechart_.c : $(SRCDIR)\piechart.c
	translate$E $** > $@
"$(OX)\lookslike_.c" : "$(SRCDIR)\lookslike.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\pivot$O : pivot_.c pivot.h
	$(TCC) /Fo$@ -c pivot_.c
"$(OX)\main$O" : "$(OX)\main_.c" "$(OX)\main.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\main_.c"

pivot_.c : $(SRCDIR)\pivot.c
	translate$E $** > $@
"$(OX)\main_.c" : "$(SRCDIR)\main.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\popen$O : popen_.c popen.h
	$(TCC) /Fo$@ -c popen_.c
"$(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"
popen_.c : $(SRCDIR)\popen.c
	translate$E $** > $@

"$(OX)\markdown_.c" : "$(SRCDIR)\markdown.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\pqueue$O : pqueue_.c pqueue.h
	$(TCC) /Fo$@ -c pqueue_.c
"$(OX)\markdown_html$O" : "$(OX)\markdown_html_.c" "$(OX)\markdown_html.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\markdown_html_.c"

pqueue_.c : $(SRCDIR)\pqueue.c
	translate$E $** > $@
"$(OX)\markdown_html_.c" : "$(SRCDIR)\markdown_html.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\printf$O : printf_.c printf.h
	$(TCC) /Fo$@ -c printf_.c
"$(OX)\md5$O" : "$(OX)\md5_.c" "$(OX)\md5.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\md5_.c"

printf_.c : $(SRCDIR)\printf.c
	translate$E $** > $@
"$(OX)\md5_.c" : "$(SRCDIR)\md5.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\publish$O : publish_.c publish.h
	$(TCC) /Fo$@ -c publish_.c
"$(OX)\merge$O" : "$(OX)\merge_.c" "$(OX)\merge.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\merge_.c"

publish_.c : $(SRCDIR)\publish.c
	translate$E $** > $@
"$(OX)\merge_.c" : "$(SRCDIR)\merge.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\purge$O : purge_.c purge.h
	$(TCC) /Fo$@ -c purge_.c
"$(OX)\merge3$O" : "$(OX)\merge3_.c" "$(OX)\merge3.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\merge3_.c"

purge_.c : $(SRCDIR)\purge.c
	translate$E $** > $@
"$(OX)\merge3_.c" : "$(SRCDIR)\merge3.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\rebuild$O : rebuild_.c rebuild.h
	$(TCC) /Fo$@ -c rebuild_.c
"$(OX)\moderate$O" : "$(OX)\moderate_.c" "$(OX)\moderate.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\moderate_.c"

rebuild_.c : $(SRCDIR)\rebuild.c
	translate$E $** > $@
"$(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)\regexp$O : regexp_.c regexp.h
	$(TCC) /Fo$@ -c regexp_.c

"$(OX)\patch$O" : "$(OX)\patch_.c" "$(OX)\patch.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\patch_.c"

regexp_.c : $(SRCDIR)\regexp.c
	translate$E $** > $@
"$(OX)\patch_.c" : "$(SRCDIR)\patch.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\report$O : report_.c report.h
	$(TCC) /Fo$@ -c report_.c
"$(OX)\path$O" : "$(OX)\path_.c" "$(OX)\path.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\path_.c"

report_.c : $(SRCDIR)\report.c
	translate$E $** > $@
"$(OX)\path_.c" : "$(SRCDIR)\path.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\rss$O : rss_.c rss.h
	$(TCC) /Fo$@ -c rss_.c
"$(OX)\piechart$O" : "$(OX)\piechart_.c" "$(OX)\piechart.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\piechart_.c"

rss_.c : $(SRCDIR)\rss.c
	translate$E $** > $@
"$(OX)\piechart_.c" : "$(SRCDIR)\piechart.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\schema$O : schema_.c schema.h
	$(TCC) /Fo$@ -c schema_.c
"$(OX)\pikchrshow$O" : "$(OX)\pikchrshow_.c" "$(OX)\pikchrshow.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\pikchrshow_.c"

schema_.c : $(SRCDIR)\schema.c
	translate$E $** > $@
"$(OX)\pikchrshow_.c" : "$(SRCDIR)\pikchrshow.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\search$O : search_.c search.h
	$(TCC) /Fo$@ -c search_.c
"$(OX)\pivot$O" : "$(OX)\pivot_.c" "$(OX)\pivot.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\pivot_.c"

search_.c : $(SRCDIR)\search.c
	translate$E $** > $@
"$(OX)\pivot_.c" : "$(SRCDIR)\pivot.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\security_audit$O : security_audit_.c security_audit.h
	$(TCC) /Fo$@ -c security_audit_.c
"$(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"
security_audit_.c : $(SRCDIR)\security_audit.c
	translate$E $** > $@

"$(OX)\pqueue_.c" : "$(SRCDIR)\pqueue.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\setup$O : setup_.c setup.h
	$(TCC) /Fo$@ -c setup_.c
"$(OX)\printf$O" : "$(OX)\printf_.c" "$(OX)\printf.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\printf_.c"

setup_.c : $(SRCDIR)\setup.c
	translate$E $** > $@
"$(OX)\printf_.c" : "$(SRCDIR)\printf.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\setupuser$O : setupuser_.c setupuser.h
	$(TCC) /Fo$@ -c setupuser_.c
"$(OX)\publish$O" : "$(OX)\publish_.c" "$(OX)\publish.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\publish_.c"

setupuser_.c : $(SRCDIR)\setupuser.c
	translate$E $** > $@
"$(OX)\publish_.c" : "$(SRCDIR)\publish.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\sha1$O : sha1_.c sha1.h
	$(TCC) /Fo$@ -c sha1_.c
"$(OX)\purge$O" : "$(OX)\purge_.c" "$(OX)\purge.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\purge_.c"

sha1_.c : $(SRCDIR)\sha1.c
	translate$E $** > $@
"$(OX)\purge_.c" : "$(SRCDIR)\purge.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\sha1hard$O : sha1hard_.c sha1hard.h
	$(TCC) /Fo$@ -c sha1hard_.c
"$(OX)\rebuild$O" : "$(OX)\rebuild_.c" "$(OX)\rebuild.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\rebuild_.c"

sha1hard_.c : $(SRCDIR)\sha1hard.c
	translate$E $** > $@
"$(OX)\rebuild_.c" : "$(SRCDIR)\rebuild.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\sha3$O : sha3_.c sha3.h
	$(TCC) /Fo$@ -c sha3_.c
"$(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"
sha3_.c : $(SRCDIR)\sha3.c
	translate$E $** > $@

"$(OX)\repolist_.c" : "$(SRCDIR)\repolist.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\shun$O : shun_.c shun.h
	$(TCC) /Fo$@ -c shun_.c
"$(OX)\report$O" : "$(OX)\report_.c" "$(OX)\report.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\report_.c"

shun_.c : $(SRCDIR)\shun.c
	translate$E $** > $@
"$(OX)\report_.c" : "$(SRCDIR)\report.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\sitemap$O : sitemap_.c sitemap.h
	$(TCC) /Fo$@ -c sitemap_.c
"$(OX)\rss$O" : "$(OX)\rss_.c" "$(OX)\rss.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\rss_.c"

sitemap_.c : $(SRCDIR)\sitemap.c
	translate$E $** > $@

"$(OX)\rss_.c" : "$(SRCDIR)\rss.c"
	"$(OBJDIR)\translate$E" $** > $@

"$(OX)\schema$O" : "$(OX)\schema_.c" "$(OX)\schema.h"
$(OX)\skins$O : skins_.c skins.h
	$(TCC) /Fo$@ -c skins_.c
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\schema_.c"

skins_.c : $(SRCDIR)\skins.c
	translate$E $** > $@
"$(OX)\schema_.c" : "$(SRCDIR)\schema.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\smtp$O : smtp_.c smtp.h
	$(TCC) /Fo$@ -c smtp_.c
"$(OX)\search$O" : "$(OX)\search_.c" "$(OX)\search.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\search_.c"

smtp_.c : $(SRCDIR)\smtp.c
	translate$E $** > $@
"$(OX)\search_.c" : "$(SRCDIR)\search.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\sqlcmd$O : sqlcmd_.c sqlcmd.h
	$(TCC) /Fo$@ -c sqlcmd_.c
"$(OX)\security_audit$O" : "$(OX)\security_audit_.c" "$(OX)\security_audit.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\security_audit_.c"

sqlcmd_.c : $(SRCDIR)\sqlcmd.c
	translate$E $** > $@
"$(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)\stash$O : stash_.c stash.h
	$(TCC) /Fo$@ -c stash_.c

"$(OX)\setupuser$O" : "$(OX)\setupuser_.c" "$(OX)\setupuser.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\setupuser_.c"

stash_.c : $(SRCDIR)\stash.c
	translate$E $** > $@
"$(OX)\setupuser_.c" : "$(SRCDIR)\setupuser.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\stat$O : stat_.c stat.h
	$(TCC) /Fo$@ -c stat_.c
"$(OX)\sha1$O" : "$(OX)\sha1_.c" "$(OX)\sha1.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\sha1_.c"

stat_.c : $(SRCDIR)\stat.c
	translate$E $** > $@
"$(OX)\sha1_.c" : "$(SRCDIR)\sha1.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\statrep$O : statrep_.c statrep.h
	$(TCC) /Fo$@ -c statrep_.c

"$(OX)\sha1hard$O" : "$(OX)\sha1hard_.c" "$(OX)\sha1hard.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\sha1hard_.c"

"$(OX)\sha1hard_.c" : "$(SRCDIR)\sha1hard.c"
statrep_.c : $(SRCDIR)\statrep.c
	translate$E $** > $@
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\style$O : style_.c style.h
	$(TCC) /Fo$@ -c style_.c
"$(OX)\sha3$O" : "$(OX)\sha3_.c" "$(OX)\sha3.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\sha3_.c"

style_.c : $(SRCDIR)\style.c
	translate$E $** > $@
"$(OX)\sha3_.c" : "$(SRCDIR)\sha3.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\sync$O : sync_.c sync.h
	$(TCC) /Fo$@ -c sync_.c
"$(OX)\shun$O" : "$(OX)\shun_.c" "$(OX)\shun.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\shun_.c"

sync_.c : $(SRCDIR)\sync.c
	translate$E $** > $@
"$(OX)\shun_.c" : "$(SRCDIR)\shun.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\tag$O : tag_.c tag.h
	$(TCC) /Fo$@ -c tag_.c
"$(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"
tag_.c : $(SRCDIR)\tag.c
	translate$E $** > $@

"$(OX)\skins_.c" : "$(SRCDIR)\skins.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\tar$O : tar_.c tar.h
	$(TCC) /Fo$@ -c tar_.c
"$(OX)\smtp$O" : "$(OX)\smtp_.c" "$(OX)\smtp.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\smtp_.c"

tar_.c : $(SRCDIR)\tar.c
	translate$E $** > $@
"$(OX)\smtp_.c" : "$(SRCDIR)\smtp.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\th_main$O : th_main_.c th_main.h
	$(TCC) /Fo$@ -c th_main_.c
"$(OX)\sqlcmd$O" : "$(OX)\sqlcmd_.c" "$(OX)\sqlcmd.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\sqlcmd_.c"

th_main_.c : $(SRCDIR)\th_main.c
	translate$E $** > $@

"$(OX)\sqlcmd_.c" : "$(SRCDIR)\sqlcmd.c"
	"$(OBJDIR)\translate$E" $** > $@

"$(OX)\stash$O" : "$(OX)\stash_.c" "$(OX)\stash.h"
$(OX)\timeline$O : timeline_.c timeline.h
	$(TCC) /Fo$@ -c timeline_.c
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\stash_.c"

timeline_.c : $(SRCDIR)\timeline.c
	translate$E $** > $@
"$(OX)\stash_.c" : "$(SRCDIR)\stash.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\tkt$O : tkt_.c tkt.h
	$(TCC) /Fo$@ -c tkt_.c
"$(OX)\stat$O" : "$(OX)\stat_.c" "$(OX)\stat.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\stat_.c"

tkt_.c : $(SRCDIR)\tkt.c
	translate$E $** > $@
"$(OX)\stat_.c" : "$(SRCDIR)\stat.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\tktsetup$O : tktsetup_.c tktsetup.h
	$(TCC) /Fo$@ -c tktsetup_.c
"$(OX)\statrep$O" : "$(OX)\statrep_.c" "$(OX)\statrep.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\statrep_.c"

tktsetup_.c : $(SRCDIR)\tktsetup.c
	translate$E $** > $@
"$(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)\undo$O : undo_.c undo.h
	$(TCC) /Fo$@ -c undo_.c

"$(OX)\sync$O" : "$(OX)\sync_.c" "$(OX)\sync.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\sync_.c"

undo_.c : $(SRCDIR)\undo.c
	translate$E $** > $@
"$(OX)\sync_.c" : "$(SRCDIR)\sync.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\unicode$O : unicode_.c unicode.h
	$(TCC) /Fo$@ -c unicode_.c
"$(OX)\tag$O" : "$(OX)\tag_.c" "$(OX)\tag.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\tag_.c"

unicode_.c : $(SRCDIR)\unicode.c
	translate$E $** > $@
"$(OX)\tag_.c" : "$(SRCDIR)\tag.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\unversioned$O : unversioned_.c unversioned.h
	$(TCC) /Fo$@ -c unversioned_.c
"$(OX)\tar$O" : "$(OX)\tar_.c" "$(OX)\tar.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\tar_.c"

unversioned_.c : $(SRCDIR)\unversioned.c
	translate$E $** > $@
"$(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)\update$O : update_.c update.h
	$(TCC) /Fo$@ -c update_.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"

update_.c : $(SRCDIR)\update.c
	translate$E $** > $@
"$(OX)\th_main_.c" : "$(SRCDIR)\th_main.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\url$O : url_.c url.h
	$(TCC) /Fo$@ -c url_.c
"$(OX)\timeline$O" : "$(OX)\timeline_.c" "$(OX)\timeline.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\timeline_.c"

url_.c : $(SRCDIR)\url.c
	translate$E $** > $@
"$(OX)\timeline_.c" : "$(SRCDIR)\timeline.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\user$O : user_.c user.h
	$(TCC) /Fo$@ -c user_.c
"$(OX)\tkt$O" : "$(OX)\tkt_.c" "$(OX)\tkt.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\tkt_.c"

user_.c : $(SRCDIR)\user.c
	translate$E $** > $@
"$(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)\utf8$O : utf8_.c utf8.h
	$(TCC) /Fo$@ -c utf8_.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"

utf8_.c : $(SRCDIR)\utf8.c
	translate$E $** > $@
"$(OX)\undo_.c" : "$(SRCDIR)\undo.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\util$O : util_.c util.h
	$(TCC) /Fo$@ -c util_.c
"$(OX)\unicode$O" : "$(OX)\unicode_.c" "$(OX)\unicode.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\unicode_.c"

util_.c : $(SRCDIR)\util.c
	translate$E $** > $@
"$(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)\verify$O : verify_.c verify.h
	$(TCC) /Fo$@ -c verify_.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"

verify_.c : $(SRCDIR)\verify.c
	translate$E $** > $@
"$(OX)\update_.c" : "$(SRCDIR)\update.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\vfile$O : vfile_.c vfile.h
	$(TCC) /Fo$@ -c vfile_.c
"$(OX)\url$O" : "$(OX)\url_.c" "$(OX)\url.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\url_.c"

vfile_.c : $(SRCDIR)\vfile.c
	translate$E $** > $@
"$(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)\webmail$O : webmail_.c webmail.h
	$(TCC) /Fo$@ -c webmail_.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"

webmail_.c : $(SRCDIR)\webmail.c
	translate$E $** > $@
"$(OX)\utf8_.c" : "$(SRCDIR)\utf8.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\wiki$O : wiki_.c wiki.h
	$(TCC) /Fo$@ -c wiki_.c
"$(OX)\util$O" : "$(OX)\util_.c" "$(OX)\util.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\util_.c"

wiki_.c : $(SRCDIR)\wiki.c
	translate$E $** > $@
"$(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)\wikiformat$O : wikiformat_.c wikiformat.h
	$(TCC) /Fo$@ -c wikiformat_.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"

wikiformat_.c : $(SRCDIR)\wikiformat.c
	translate$E $** > $@
"$(OX)\vfile_.c" : "$(SRCDIR)\vfile.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\winfile$O : winfile_.c winfile.h
	$(TCC) /Fo$@ -c winfile_.c
"$(OX)\wiki$O" : "$(OX)\wiki_.c" "$(OX)\wiki.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\wiki_.c"

winfile_.c : $(SRCDIR)\winfile.c
	translate$E $** > $@
"$(OX)\wiki_.c" : "$(SRCDIR)\wiki.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\winhttp$O : winhttp_.c winhttp.h
	$(TCC) /Fo$@ -c winhttp_.c
"$(OX)\wikiformat$O" : "$(OX)\wikiformat_.c" "$(OX)\wikiformat.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\wikiformat_.c"

winhttp_.c : $(SRCDIR)\winhttp.c
	translate$E $** > $@
"$(OX)\wikiformat_.c" : "$(SRCDIR)\wikiformat.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\wysiwyg$O : wysiwyg_.c wysiwyg.h
	$(TCC) /Fo$@ -c wysiwyg_.c
"$(OX)\winfile$O" : "$(OX)\winfile_.c" "$(OX)\winfile.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\winfile_.c"

wysiwyg_.c : $(SRCDIR)\wysiwyg.c
	translate$E $** > $@
"$(OX)\winfile_.c" : "$(SRCDIR)\winfile.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\xfer$O : xfer_.c xfer.h
	$(TCC) /Fo$@ -c xfer_.c
"$(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"

xfer_.c : $(SRCDIR)\xfer.c
	translate$E $** > $@
"$(OX)\xfer_.c" : "$(SRCDIR)\xfer.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\xfersetup$O : xfersetup_.c xfersetup.h
	$(TCC) /Fo$@ -c xfersetup_.c
"$(OX)\xfersetup$O" : "$(OX)\xfersetup_.c" "$(OX)\xfersetup.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\xfersetup_.c"

xfersetup_.c : $(SRCDIR)\xfersetup.c
	translate$E $** > $@
"$(OX)\xfersetup_.c" : "$(SRCDIR)\xfersetup.c"
	"$(OBJDIR)\translate$E" $** > $@

$(OX)\zip$O : zip_.c zip.h
	$(TCC) /Fo$@ -c zip_.c
"$(OX)\zip$O" : "$(OX)\zip_.c" "$(OX)\zip.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\zip_.c"

zip_.c : $(SRCDIR)\zip.c
	translate$E $** > $@
"$(OX)\zip_.c" : "$(SRCDIR)\zip.c"
	"$(OBJDIR)\translate$E" $** > $@

fossil.res : $B\win\fossil.rc
	$(RCC)  /fo $@ $**
"$(OX)\fossil.res" : "$(B)\win\fossil.rc"
	$(RCC) /fo $@ $**

headers: makeheaders$E page_index.h builtin_data.h default_css.h VERSION.h
	makeheaders$E add_.c:add.h \
			alerts_.c:alerts.h \
			allrepo_.c:allrepo.h \
			attach_.c:attach.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 \
			checkin_.c:checkin.h \
			checkout_.c:checkout.h \
			clearsign_.c:clearsign.h \
			clone_.c:clone.h \
			comformat_.c:comformat.h \
			configure_.c:configure.h \
			content_.c:content.h \
			cookies_.c:cookies.h \
			db_.c:db.h \
			delta_.c:delta.h \
			deltacmd_.c:deltacmd.h \
			descendants_.c:descendants.h \
			diff_.c:diff.h \
			diffcmd_.c:diffcmd.h \
			dispatch_.c:dispatch.h \
			doc_.c:doc.h \
			encode_.c:encode.h \
			etag_.c:etag.h \
			event_.c:event.h \
			export_.c:export.h \
			file_.c:file.h \
			finfo_.c:finfo.h \
			foci_.c:foci.h \
			forum_.c:forum.h \
			fshell_.c:fshell.h \
			fusefs_.c:fusefs.h \
			glob_.c:glob.h \
			graph_.c:graph.h \
			gzip_.c:gzip.h \
			hname_.c:hname.h \
			http_.c:http.h \
			http_socket_.c:http_socket.h \
			http_ssl_.c:http_ssl.h \
			http_transport_.c:http_transport.h \
			import_.c:import.h \
			info_.c:info.h \
			json_.c:json.h \
			json_artifact_.c:json_artifact.h \
			json_branch_.c:json_branch.h \
			json_config_.c:json_config.h \
			json_diff_.c:json_diff.h \
			json_dir_.c:json_dir.h \
			json_finfo_.c:json_finfo.h \
			json_login_.c:json_login.h \
			json_query_.c:json_query.h \
			json_report_.c:json_report.h \
			json_status_.c:json_status.h \
			json_tag_.c:json_tag.h \
			json_timeline_.c:json_timeline.h \
			json_user_.c:json_user.h \
			json_wiki_.c:json_wiki.h \
			leaf_.c:leaf.h \
			loadctrl_.c:loadctrl.h \
			login_.c:login.h \
			lookslike_.c:lookslike.h \
			main_.c:main.h \
			manifest_.c:manifest.h \
			markdown_.c:markdown.h \
			markdown_html_.c:markdown_html.h \
			md5_.c:md5.h \
			merge_.c:merge.h \
			merge3_.c:merge3.h \
			moderate_.c:moderate.h \
			name_.c:name.h \
			path_.c:path.h \
			piechart_.c:piechart.h \
			pivot_.c:pivot.h \
			popen_.c:popen.h \
			pqueue_.c:pqueue.h \
			printf_.c:printf.h \
			publish_.c:publish.h \
			purge_.c:purge.h \
			rebuild_.c:rebuild.h \
			regexp_.c:regexp.h \
			report_.c:report.h \
			rss_.c:rss.h \
			schema_.c:schema.h \
			search_.c:search.h \
			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 \
			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 \
"$(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" \
			webmail_.c:webmail.h \
			wiki_.c:wiki.h \
			wikiformat_.c:wikiformat.h \
			winfile_.c:winfile.h \
			winhttp_.c:winhttp.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" \
			wysiwyg_.c:wysiwyg.h \
			xfer_.c:xfer.h \
			xfersetup_.c:xfersetup.h \
			zip_.c:zip.h \
			$(SRCDIR)\sqlite3.h \
			$(SRCDIR)\th.h \
			VERSION.h \
			$(SRCDIR)\cson_amalgamation.h
	@copy /Y nul: headers
			"$(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: $@

Changes to win/buildmsvc.bat.

1
2
3
4
5
6

7
8


9
10
11
12
13
14
15
1
2
3
4
5

6
7
8
9
10
11
12
13
14
15
16
17





-
+


+
+







@ECHO OFF

::
:: buildmsvc.bat --
::
:: This batch file attempts to build Fossil using the latest version
:: 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)
31
32
33
34
35
36
37
38

39
40
41
42
43
44
45
46
47
48
49















50
51
52
53
54
55
56
33
34
35
36
37
38
39

40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73







-
+











+
+
+
+
+
+
+
+
+
+
+
+
+
+
+








%_VECHO% Tools = '%TOOLS%'

REM
REM Visual C++ ????
REM
IF DEFINED VCINSTALLDIR IF EXIST "%VCINSTALLDIR%" (
  %_AECHO% Build environment appears to be setup.
  %_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...
180
181
182
183
184
185
186



187
































188
189


190
191
192

193
194
195
196
197
198
199

200
201

202
203
204

205
206


207
208
209
210
211
212
213
214
215
216
217
218
219
220


221
222
223
224
225


226
227

228
229
230
231
232
233
234

235

236

237
238
239
240






241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260

























261
262
263
264
265
266
267
268
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239


240
241
242
243

244
245
246
247
248
249
250

251
252

253
254
255

256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284

285
286
287
288
289
290
291

292
293
294

295
296



297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315







316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348







+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+


-
+






-
+

-
+


-
+


+
+














+
+





+
+

-
+






-
+

+
-
+

-
-
-
+
+
+
+
+
+













-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+








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 "%ROOT%\msvcbld" (
  %__ECHO% MKDIR "%ROOT%\msvcbld"
IF NOT EXIST "%BUILDDIR%" (
  %__ECHO% MKDIR "%BUILDDIR%"

  IF ERRORLEVEL 1 (
    ECHO Could not make directory "%ROOT%\msvcbld".
    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.
REM       the generated files will be placed there, if needed.
REM
%__ECHO2% PUSHD "%ROOT%\msvcbld"
%__ECHO2% PUSHD "%BUILDDIR%"

IF ERRORLEVEL 1 (
  ECHO Could not change to directory "%ROOT%\msvcbld".
  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" %NMAKE_ARGS% %*
%__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.
REM NOTE: Attempt to restore the previously saved directory, if needed.
REM
IF DEFINED NEED_POPD (
%__ECHO2% POPD
  %__ECHO2% POPD

IF ERRORLEVEL 1 (
  ECHO Could not restore directory.
  GOTO errors
  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" (
    SET LIB=%PFILES_SDK71A%\Microsoft SDKs\Windows\7.1A\Lib\x64;%LIB%
  ) ELSE (
    SET LIB=%PFILES_SDK71A%\Microsoft SDKs\Windows\7.1A\Lib;%LIB%
  )
  CALL :fn_UnsetVariable PFILES_SDK71A
  SET NMAKE_ARGS=%NMAKE_ARGS% FOSSIL_ENABLE_WINXP=1
  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
276
277
278
279
280
281
282




























283
284
285
286
287
288
289
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







: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

Changes to win/fossil.exe.manifest.

1
2
3
4

5
6
7
8
9
10
11
1
2
3

4
5
6
7
8
9
10
11



-
+







<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"
          xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
  <assemblyIdentity version="1.0.0.0" processorArchitecture="X86" name="fossil"
  <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>
32
33
34
35
36
37
38
39

40
41
42
43
32
33
34
35
36
37
38

39
40
41
42
43







-
+




           xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
      <dpiAware>true</dpiAware>
    </asmv3:windowsSettings>
  </asmv3:application>
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls"
                        version="6.0.0.0" processorArchitecture="X86"
                        version="6.0.0.0" processorArchitecture="*"
                        publicKeyToken="6595b64144ccf1df" language="*" />
    </dependentAssembly>
  </dependency>
</assembly>

Changes to win/fossil.rc.

28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
28
29
30
31
32
33
34









35

36
37
38
39
40
41
42







-
-
-
-
-
-
-
-
-

-







#  define VS_FF_NONE            0x00000000L
#endif /* !defined(VS_FF_NONE) */

#include "VERSION.h"
#define _RC_COMPILE_
#include "config.h"
#include "sqlite3.h"

#if defined(FOSSIL_ENABLE_MINIZ)
#if defined(__MINGW32__)
#include "minizver.h"
#else
#define MINIZ_HEADER_FILE_ONLY
#include "miniz.c"
#endif /* defined(__MINGW32__) */
#else
#include "zlib.h"
#endif /* defined(FOSSIL_ENABLE_MINIZ) */

#if defined(FOSSIL_ENABLE_SSL)
#include "openssl/opensslv.h"
#endif /* defined(FOSSIL_ENABLE_SSL) */

#if defined(FOSSIL_ENABLE_TCL)
#include "tcl.h"
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
95
96
97
98
99
100
101



102

103
104
105
106
107
108
109
110

111



112
113
114
115
116
117
118







-
-
-

-








-

-
-
-







      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
#if defined(FOSSIL_ENABLE_MINIZ)
      VALUE "MinizVersion", "miniz " MZ_VERSION "\0"
#else
      VALUE "ZlibVersion", "zlib " ZLIB_VERSION "\0"
#endif /* defined(FOSSIL_ENABLE_MINIZ) */
#if defined(BROKEN_MINGW_CMDLINE)
      VALUE "CommandLineIsUnicode", "No\0"
#else
      VALUE "CommandLineIsUnicode", "Yes\0"
#endif /* defined(BROKEN_MINGW_CMDLINE) */
#if defined(FOSSIL_ENABLE_SSL)
      VALUE "SslEnabled", "Yes, " OPENSSL_VERSION_TEXT "\0"
#endif /* defined(FOSSIL_ENABLE_SSL) */
#if defined(FOSSIL_ENABLE_LEGACY_MV_RM)
      VALUE "LegacyMvRm", "Yes\0"
#else
      VALUE "LegacyMvRm", "No\0"
#endif /* defined(FOSSIL_ENABLE_LEGACY_MV_RM) */
#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"

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

Changes to 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
1
2


3
4
5
6
7
8
9
10





11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

26
27

28
29
30
31
32
33



34
35
36
37
38
39
40
41
42
43

44
45
46
47

48
49
50
51

52
53
54


55
56
57
58
59
60
61
62
63
64
65
66
67
68

69
70
71

72
73
74

75
76
77
78

79
80
81
82
83
84
85
86
87
88
89

90
91
92
93

94
95

96
97
98
99

100
101
102

103
104
105
106
107
108
109
110
111
112
113
114

115
116
117
118
119
120
121
122

123
124
125
126
127
128
129
130

131
132
133
134
135
136

137
138
139
140
141


142
143
144
145
146
147
148
149
150
151
152

153
154

155
156
157
158
159
160
161

162
163
164
165
166
167

168
169
170

171
172
173
174
175
176
177



178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201

202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270

271
272
273
274

275
276
277
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><blockquote>
<p>CGI or "Common Gateway Interface" is a venerable yet reliable technique for
<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
<p>This is a "how it works" guide.  If you just want to set up Fossil
as a CGI server, see the [./server.wiki | Fossil Server Setup] page.
</blockquote>
<h2>A Quick Review Of CGI</h2><blockquote>
<p>
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 except a compressed reply, POST
not the requesting application can accept a compressed reply, POST
parameters from forms, and so forth.
<p>

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.)
<p>
Suppose the URL in the HTTP request looks like this:
<blockquote><b>/one/two/timeline/four</b></blockquote>

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.
<p>

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.
<p>

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.
<p>

Before executing the CGI script, the web server will set up various
environment variables with information useful to the CGI script:
<table border=1 cellpadding=5>
<tr><th>Environment<br>Variable<th>Meaning
<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>
<p>

There are other CGI environment variables beyond those listed above.
Many Fossil servers implement the
[https://www.fossil-scm.org/fossil/test_env/two/three?abc=xyz|test_env]
[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.
<p>

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.
<p>

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.
<p>

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.
</blockquote>

<h2>How Fossil Acts As A CGI Program</h2>
<blockquote>

An appropriate CGI script for running Fossil will look something
like the following:

<blockquote><pre>
<pre>
#!/usr/bin/fossil
repository: /home/www/repos/project.fossil
</pre></blockquote>
</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).
<p>

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.
<p>

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&#91;1&#93;</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.
<p>

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.
<p>

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://www.fossil-scm.org/fossil/info/c14ecc43]
<li> [https://www.fossil-scm.org/fossil/info?name=c14ecc43]
<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.
</blockquote>

<h2>Serving Multiple Fossil Repositories From One CGI Script</h2>
<blockquote>

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.
<p>

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:

<blockquote><pre>
<pre>
#!/usr/bin/fossil
directory: /home/www/repos
</pre></blockquote>
</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:
<blockquote>
<b>http://example.com/cgis/example2/subdir/three/timeline</b>
</blockquote>

<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>
</blockquote>

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>
<blockquote><ol type="I">
<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.
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.
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".
<p>
<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.
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>
</blockquote>

Changes to 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
1

2
3
4
5
6
7
8

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

-







-
+





+
+
+
+







<title>How The Fossil Download Page Works</title>
<h1 align="center">How The Download Page Works</h1>

<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 AJAX to retrieve the [/help?cmd=/juvlist|/juvlist] webpage
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
37
38
39
40
41
42
43
44

45
46
47
48
49

50
51
52
53
54
55
56

57
58
59
60
61
62
63
64

65
66
67
68
69
70
71
40
41
42
43
44
45
46

47
48
49
50
51

52
53
54
55
56
57
58

59
60
61
62
63
64
65
66

67
68
69
70
71
72
73
74







-
+




-
+






-
+







-
+







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

    <b>&lt;div class='fossil-doc' data-title='Download Page'&gt;...&lt;/div&gt;</b>
<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 &lt;div class='fossil-doc'&gt; this works.
how this &lt;div class='fossil-doc'&gt; 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
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 Windows10, and so forth.  After a new release is tagged,
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.

86
87
88
89
90
91
92
93
94


95
96
97
98




89
90
91
92
93
94
95


96
97




98
99
100
101







-
-
+
+
-
-
-
-
+
+
+
+
<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 67 people (as of 2017-03-24) who have
check-in privileges.  But only 3 core developers
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
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.
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.

Changes to www/adding_code.wiki.

1
2
3
4
5
6








7
8
9
10
11
12
13
1
2
3
4


5
6
7
8
9
10
11
12
13
14
15
16
17
18
19




-
-
+
+
+
+
+
+
+
+







<title>Adding Features To Fossil</title>

<h2>1.0 Introduction</h2>

This article provides a brief overview of how to write new code that extends
or enhances Fossil.
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.
37
38
39
40
41
42
43
44

45
46
47
48

49


50
51
52
53
54
55
56
57
58
59
60
61

62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77

78
79
80
81
82
83
84
85
86
87
88
89
90

91
92
93
94
95
96
97
98
99
100
101

102
103
104
105

106


107
108
109
110
111
112
113

114
115
116
117

118
119
120
121
122
123

124
125
126
127
128
129
130

131
132

133
134
135
136
137
138
139
140
141
142

143
144
145




146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166

167
168
169
170
171
172

173
174
175
176
177
178
179
180
181

182
183
184
185
186
187
188
43
44
45
46
47
48
49

50
51
52
53
54
55

56
57
58
59
60
61
62
63
64
65
66
67
68

69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84

85
86
87
88
89
90
91
92
93
94
95
96
97

98
99
100
101
102
103
104
105
106
107
108

109
110
111
112
113
114

115
116
117
118
119
120
121
122

123
124
125
126

127
128
129
130
131
132

133
134
135
136
137
138
139

140
141

142
143
144
145
146
147
148
149
150
151
152
153



154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177

178
179
180
181
182
183

184
185
186
187
188
189
190
191
192

193
194
195
196
197
198
199
200







-
+




+
-
+
+











-
+















-
+












-
+










-
+




+
-
+
+






-
+



-
+





-
+






-
+

-
+










+
-
-
-
+
+
+
+




















-
+





-
+








-
+







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 src/makemake.tcl and adding "xyzzy" (without
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>
    <b>tclsh makemake.tcl</b>
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:

<blockquote><verbatim>
<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></blockquote>
</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 [../src/makeheaders.html | makeheaders.html] for additional
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>4.0 Creating A New Command</h2>
<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>
    <b>fossil diff xyzzy.c</b>
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:

<blockquote><verbatim>
<verbatim>
/*
** COMMAND: xyzzy
**
** Help text goes here.
** Help text goes here.  Backslashes must be escaped.
*/
void xyzzy_cmd(void){
  /* Implement the command here */
  fossil_print("Hello, World!\n");
}
</verbatim></blockquote>
</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:

<blockquote><verbatim>
<verbatim>
** COMMAND: xyzzy*
</verbatim></blockquote>
</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>
    <b>./fossil xyzzy<br>
    ./fossil help xyzzy<br>
    ./fossil xyzzy --help</b>
./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>5.0 Creating A New Web Page</h2>
<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:

<blockquote><verbatim>
<verbatim>
/*
** WEBPAGE: helloworld
*/
void helloworld_page(void){
  style_header("Hello World!");
  @ <p>Hello, World!</p>
  style_footer();
}
</verbatim></blockquote>
</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.

209
210
211
212
213
214
215

221
222
223
224
225
226
227
228







+
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]

Changes to www/alerts.md.

10
11
12
13
14
15
16
17

18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35


36
37
38
39
40
41
42
10
11
12
13
14
15
16

17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33


34
35
36
37
38
39
40
41
42







-
+
















-
-
+
+







  *  [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.wiki), which must be
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 the Fossil server that will be sending these email alerts, logged
in as a user with Admin capability. It is not possible to work on a
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.
62
63
64
65
66
67
68
69

70
71
72
73
74
75
76
62
63
64
65
66
67
68

69
70
71
72
73
74
75
76







-
+







`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 trival, because there are many other reasons to have such
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:
89
90
91
92
93
94
95
96

97
98
99
100

101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120



121
122
123
124
125
126
127
89
90
91
92
93
94
95

96
97
98
99

100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117



118
119
120
121
122
123
124
125
126
127







-
+



-
+

















-
-
-
+
+
+







    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
    $ fossil set email-send-command

If that gives a blank value instead of `sendmail -ti`, say

      $ fossil set email-send-command "sendmail -ti"
    $ 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
    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>
153
154
155
156
157
158
159
160
161

162
163
164
165


166
167
168
169
170
171
172
153
154
155
156
157
158
159


160


161

162
163
164
165
166
167
168
169
170







-
-
+
-
-

-
+
+







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:

<blockquote>
  Use a different login with greater privilege than FOO to access
> Use a different login with greater privilege than FOO to access /subscribe
  /subscribe
</blockquote>

...then the repository's administrator forgot to [give the Alerts capability](#cap7)
...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.

187
188
189
190
191
192
193


194
195
196
197
198
199

200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216

217
218
219
220
221
222
223
224
225
226
227
228

229
230
231
232
233



234
235
236



237
238
239
240
241



242
243
244
245
246
247
248
185
186
187
188
189
190
191
192
193
194
195
196
197
198

199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215

216
217
218
219
220
221
222
223
224
225
226
227

228
229
230



231
232
233



234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251







+
+





-
+
















-
+











-
+


-
-
-
+
+
+
-
-
-
+
+
+





+
+
+







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 (&#9776;).

[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 "Unsbuscribe" checkbox to
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
    $ 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"></a>
<a id="cap7" name="ucap"></a>
### User Capabilities

Once email alerts are working, one must also adjust user permissions to
allow users to subscribe to email alerts.  In the capability list for
each user on the Admin → Users page is a new capability called "Email
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
Alerts".  The corresponding capability letter is "7", which you must
give to any user that needs to use the subscription setup pages,
`/subscribe` and `/alerts`.
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
256
257
258
259
260
261
262
263

264
265
266
267
268
269
270
271

272
273
274
275
276
277
278
279

280
281
282
283
284

285
286
287
288
289
290


291
292
293
294
295
296
297
298
299
300
301

302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322

323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342

343
344
345
346
347
348
349
259
260
261
262
263
264
265

266
267
268
269
270
271
272
273

274
275
276
277
278
279
280
281

282
283
284
285
286

287
288
289
290
291


292
293
294
295
296
297
298
299
300
301
302
303

304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324

325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344

345
346
347
348
349
350
351
352







-
+







-
+







-
+




-
+




-
-
+
+










-
+




















-
+



















-
+







### 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
    $ 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
    $ 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
    $ fossil alerts send

This should also kick off the backoffice processing, if there are any
pending alerts to send out.

      $ fossil alert pending
    $ 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
    $ 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
    $ 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][mta] so that Fossil does not need to implement the [roughly two
[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
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
358
359
360
361
362
363
364
365
366


367
368
369



370
371
372
373
374
375
376
377

378
379
380
381
382
383
384
385

386
387
388
389
390
391
392
361
362
363
364
365
366
367


368
369

370

371
372
373
374
375
376
377
378
379
380

381
382
383
384
385
386
387
388

389
390
391
392
393
394
395
396







-
-
+
+
-

-
+
+
+







-
+







-
+







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>
We believe the [`msmtp`][msmtp] SMTP client is compatible with this
protocol if you give it the `-t` option. To our knowledge, this remains
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
untested, but if it works, this would be a useful option on a server
hosting a Fossil repository which doesn't otherwise require a separate
SMTP server for other purposes.
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
[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://www.fossil-scm.org/>
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.
415
416
417
418
419
420
421
422

423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442

443
444
445
446
447
448
449
419
420
421
422
423
424
425

426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445

446
447
448
449
450
451
452
453







-
+



















-
+







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 &
    /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][mta].
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:
472
473
474
475
476
477
478
479

480
481
482
483
484
485
486
476
477
478
479
480
481
482

483
484
485
486
487
488
489
490







-
+









<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 Fossi repository.  A subscriber is someone who receives email
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.

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
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







-
-
-
-
-
-
-
+
-
-
-
-
-




















+












-
+










-
+



-




















-
-
-
-
-
-







on behalf of a subscriber which they could do themselves, such as to
[unsubscribe](#unsub) them.


<a id="backup"></a>
## Cloning, Syncing, and Backups

The Admin → Notification settings are not replicated using clone or
sync, and it is not possible to push such settings from one repository
to another.  In a network of peer repositories, you only want one
repository sending email alerts.  If you were to replicate the email
alert settings to a separate repository, then subscribers would get
multiple alerts for each event, which would be bad.

That’s [covered elsewhere](./backup.md#alerts).
However, the subscriber list can be synced for backup purposes.  Use the
[`fossil config pull subscriber`](/help?cmd=configuration) command to
pull the latest subscriber list from a server into a backup repository.

The `push`, `export`, and `import` commands all work similarly.


<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 administion focused material
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 three new tables in the repository database, starting with
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.

  *  <b>EMAIL\_BOUNCE</b> →
     This table is intended to record email bounce history so that
     subscribers with excessive bounces can be turned off.  That
     logic has not yet been implemented so the EMAIL\_BOUNCE table
     is currently unused.

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
679
680
681
682
683
684
685
686

687
688
689
690
691
692

693
694
695
696
697
698
699
666
667
668
669
670
671
672

673
674
675
676
677
678

679
680
681
682
683
684
685
686







-
+





-
+







*   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 a email flood or other annoyance attack, as
    `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);
    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
710
711
712
713
714
715
716
717
718


719
720
721
722
723
724
725
697
698
699
700
701
702
703


704
705
706
707
708
709
710
711
712







-
-
+
+







### 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
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

Changes to 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
148

149
150
151



1
2






3
4
5
6
7
8


9
10
11

12
13

14
15
16

17
18
19
20
21
22
23


24
25





26
27
28
29
30


31
32
33



34
35
36




37
38
39
40

41
42
43

44
45
46
47
48

49
50
51
52
53
54
55
56
57
58
59
60

61
62


63
64
65




66
67
68
69
70

71
72







73
74
75
76
77
78
79
80
81
82
83

84
85








86
87
88
89
90
91
92
93
94
95
96

97
98
99
100


101
102
103
104
105
106
107
108
109
110

111
112



113
114
115
116
117








118
119



120
121
122
123



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 Spiders</title>
<title>Defense Against Robots</title>

The website presented by a Fossil server has many hyperlinks.
Even a modest project can have millions of pages in its
tree, and many of those pages (for example diffs and annotations
and ZIP archive of older check-ins) can be expensive to compute.
If a spider or bot tries to walk a website implemented by
Fossil, it can present a crippling bandwidth and CPU load.
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.

The website presented by a Fossil server is intended to be used
interactively by humans, not walked by spiders.  This article
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 spiders.
users while keeping out robots.

<h2>The "hyperlink" user capability</h2>
<h2>The Hyperlink User Capability</h2>

Every Fossil web session has a "user".  For random passers-by on the internet
(and for spiders) that user is "nobody".  The "anonymous" user is also
(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.

The "h" or "hyperlink" capability is a permission that can be granted
to users that enables the display of hyperlinks.  Most of the hyperlinks
Users without the <b>[./caps/ref.html#h | Hyperlink]</b> capability
do not see most Fossil-generated hyperlinks. This is
generated by Fossil are suppressed if this capability is missing.  So
one simple defense against spiders is to disable the "h" permission for
the "nobody" user.  This means that users must log in (perhaps as
"anonymous") before they can see any of the hyperlinks.  Spiders do not
normally attempt to log into websites and will therefore
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
not see most of the hyperlinks and will not try to walk the millions of
historical check-ins and diffs available on a Fossil-generated website.
its historical check-ins, create diffs between versions, pull zip
archives, etc. by visiting links, because there are no links.

If the "h" capability is missing from user "nobody" but is present for
user "anonymous", then a message automatically appears at the top of each
page inviting the user to log in as anonymous in order to activate hyperlinks.
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.

Removing the "h" capability from user "nobody" is an effective means
of preventing spiders from walking a Fossil-generated website.  But
it can also be annoying to humans, since it requires them to log in.
Hence, Fossil provides other techniques for blocking spiders which
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>
<h2>Automatic Hyperlinks Based on UserAgent</h2>

Fossil has the ability to selectively enable hyperlinks for users
that lack the "h" capability based on their UserAgent string in the
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 spider) that generated the request.  Typical UserAgent
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 spider used by Google to index the internet.
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 requestor
as human whereas the second two identify the requestor as a spider.
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 requestor and so a malicious spider can forge a UserAgent
string that makes it look like a human.  But most spiders truly
seem to desire to "play nicely" on the internet and are quite open
about the fact that they are a spider.  And so the UserAgent string
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 spider.
from a human or a robot.

In Fossil, under the Admin/Access menu, there is a setting entitled
"<b>Enable hyperlinks for "nobody" based on User-Agent and Javascript</b>".
If this setting is enabled, and if the UserAgent string looks like a
human and not a spider, then Fossil will enable hyperlinks even if
the "h" capability is omitted from the user permissions.  This setting
gives humans easy access to the hyperlinks while preventing spiders
from walking the millions of pages on a typical Fossil site.
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",
But the hyperlinks are not enabled directly with the setting above.
then the hyperlinks are not enabled directly .
Instead, the HTML code that is generated contains anchor tags ("&lt;a&gt;")
without "href=" attributes.  Then, javascript code is added to the
end of the page that goes back and fills in the "href=" attributes of
the anchor tags with the hyperlink targets, thus enabling the hyperlinks.
This extra step of using javascript to enable the hyperlink targets
is a security measure against spiders that forge a human-looking
UserAgent string.  Most spiders do not bother to run javascript and
so to the spider the empty anchor tag will be useless.  But all modern
web browsers implement javascript, so hyperlinks will show up
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>
<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 spiders that use forged UserAgent strings to make them look
like normal web browsers and which interpret javascript.  We do not
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
spiders have been put in place.
robots have been put in place.

On the Admin/Access page of Fossil, just below the
"<b>Enable hyperlinks for "nobody" based on User-Agent and Javascript</b>"
setting, there are now two additional subsettings that can be optionally
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 subsetting waits to run the
javascript that sets the "href=" attributes on anchor tags until after
at least one "mouseover" event has been detected on the &lt;body&gt;
element of the page.  The thinking here is that spiders will not be
simulating mouse motion and so no mouseover events will ever occur and
hence the hyperlinks will never become enabled for spiders.

The second new subsetting is a delay (in milliseconds) before setting
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 spider will try to
render the page immediately, and will not wait for delayed scripts
to be run, thus will never enable the hyperlinks.
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.

These two subsettings can be used separately or together.  If used together,
then the delay timer does not start until after the first mouse movement
is detected.
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
&lt;body&gt; 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 [./server.wiki#loadmgmt|Managing Server Load] for a description
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>
<h2>The Ongoing Struggle</h2>

Fossil currently does a very good job of providing easy access to humans
while keeping out troublesome robots and spiders.  However, spiders and
bots continue to grow more sophisticated, requiring ever more advanced
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 spider defenses of Fossil so
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 spider
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
mailing list:
[mailto:fossil-users@lists.fossil-scm.org | fossil-users@lists.fossil-scm.org].
forum:
[https://fossil-scm.org/forum].

Changes to www/backoffice.md.

1
2
3
4
5
6
7

8
9
10
11
12
13
14


15
16
17
18
19
20
21
1
2
3
4
5
6

7
8
9
10
11
12


13
14
15
16
17
18
19
20
21






-
+





-
-
+
+







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 
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](/doc/trunk/www/server.wiki) to do low-priority
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
37
38
39
40
41
42
43
44
45


46
47
48
49
50
51
52
53
54
55


56
57
58
59
60



61
62
63

64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79

80
81
82

83
84
85
86
87
88
89
90
91

































92
93
94
95


96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116

117
118
119
120
121
122
123

124
125

126
127
128
129
130
131
132
37
38
39
40
41
42
43


44
45
46
47
48
49
50
51
52
53


54
55
56
57
58
59

60
61
62
63
64

65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80

81
82
83

84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129

130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151

152
153
154
155
156
157
158

159
160

161
162
163
164
165
166
167
168







-
-
+
+








-
-
+
+




-
+
+
+


-
+















-
+


-
+









+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



-
+
+




















-
+






-
+

-
+







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](/doc/trunk/www/server.wiki) or
[SCGI](/doc/trunk/www/scgi.wiki) or via SSH.
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 
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.
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 
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 adminstrator can set up a cron job to
If this is a problem, an administrator can set up a cron job to
periodically run:

>   fossil backoffice -R _REPOSITORY_
    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" source file.
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_
    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.  
process to run backoffice.

The new backoffice process becomes the "current" process.  It sets a 
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
159
160
161
162
163
164
165
166

167
168
169
170
171

172
173
174
175
176
177
178
179
180
181





182


183
195
196
197
198
199
200
201

202
203
204
205
206

207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222

223
224
225







-
+




-
+










+
+
+
+
+
-
+
+

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_
    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 two settings that control backoffice behavior.  The
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.
doubt.  Most installations should leave "backoffice-nodelay" off and
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

Changes to 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



1
2

3
4
5
6
7
8
9
10
11

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65




66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133





134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191












192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
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
-
+

-
+
+
+
+
+

+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
# Fossil As Blockchain
# Is Fossil A Blockchain?

Fossil is a version control system built around 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

Wikipedia defines "blockchain" as
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>
>
  "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..." [(1)][]

    *  **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.
By that definition, Fossil is clearly an implementation of blockchain.
The blocks are ["manifests" artifacts](./fileformat.wiki#manifest).
Each manifest has a SHA1 or SHA3 hash of its parent or parents,
a timestamp, and other tranactional data.  The repository grows by
add new manifests onto the list.

    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.
Some people have come to associate blockchain with cryptocurrency, however,
and since Fossil has nothing to do with cryptocurrency, the claim that
Fossil is build around blockchain is met with skepticism.  The key thing
to note here is that cryptocurrency implementations like BitCoin are
built around blockchain, but they are not synonymous with blockchain.
Blockchain is a much broader concept.  Blockchain is a mechanism for
constructed a distributed ledger of transactions.  
Yes, you can use a distributed
ledger to implement a cryptocurrency, but you can also use a distributed
ledger to implement a version control system, and probably many other kinds
of applications as well.  Blockchain is a much broader idea than
cryptocurrency.

    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.

[(1)]: https://en.wikipedia.org/wiki/Blockchain
[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
1
2
3
4

5
6





7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

25
26
27
28
29
30
31
32
33


34
35
36

37
38
39
40
41
42
43
44

45
46





47
48
49
50
51
52
53
54
55
56
57

58
59
60
61
62
63

64
65


66
67
68
69

70
71


72
73

74







75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106

107



108
109
110
111
112
113
114
115




116
117
118
119
120
121
122





123
124
125
126
127
128
129
130
131
132
133
134
135
136
137


138
139
140

141
142

143
144
145

146
147
148















149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170

171
172
173
174

175
176
177
178
179
180

181
182
183
184
185





186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213

214
215
216
217
218
219
220
221

222
223
224
225

226
227
228
229
230

231
232

233
234



235
236
237
238
239
240
241
242
243
244
245
246
247
248
249


250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391

392
393

394
395





396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432

433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449

450
451
452
453
454
455
456
457




-
+

-
-
-
-
-
+
+
+
+
+
+
+
+
+
+








-
+








-
-
+


-
+







-
+

-
-
-
-
-
+
+
+
+
+
+
+
+
+
+

-
+


+
+
+
-
+

-
-
+
+


-
+

-
-
+
+
-

-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+
-
-
-
+
+
+
+
+
+

+
-
-
-
-
+
+
+
+
+
+

-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+


-
-
+
+

-
+

-
+


-
+

+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+



-
+





-
+
+



-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+







-
+



-
+




-
+

-
+

-
-
-
+
+
+



+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+

-
+

-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


-
+
















-
+







<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.
linearly, as shown in Figure 1.

<table border=1 cellpadding=10 hspace=10 vspace=10 align="center">
<tr><td align="center">
<img src="branch01.gif" width=280 height=68><br>
Figure 1
</td></tr></table>
<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
The arrows in Figure 1 show the evolution of a project.  The initial
check-in is 1.  Check-in 2 is derived from 1.  In other words, check-in 2
was created by making edits to check-in 1 and then committing those edits.
We say that 2 is a <i>child</i> of 1
and that 1 is a <i>parent</i> of 2.
Check-in 3 is derived from check-in 2, making
3 a child of 2.  We say that 3 is a <i>descendant</i> of both 1 and 2 and that 1
and 2 are both <i>ancestors</i> of 3.

<a name="dag"></a>
<h2>DAGs</h2>
<h2 id="dag">DAGs</h2>

The graph of check-ins is a
[http://en.wikipedia.org/wiki/Directed_acyclic_graph | directed acyclic graph]
[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:
After both changes are committed, the check-in graph looks like Figure 2:

<table border=1 cellpadding=10 hspace=10 vspace=10 align="center">
<tr><td align="center">
<img src="branch02.gif" width=210 height=140><br>
Figure 2
</td></tr></table>
<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
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.

Fossil tries to prevent forks. Suppose two programmers named Alice and
Suppose two programmers named Alice and
Bob are each editing check-in 2 separately. Alice finishes her edits
first and commits her changes, resulting in check-in 3. Later, when Bob
attempts to commit his changes, fossil verifies that check-in 2 is still
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" which pulls in Alice's changes, merging them into his own
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 [./concepts.wiki#workflow |
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.
"autosync"] mode.

But perhaps Bob is off-network when he does his commit, so he
has no way of knowing that Alice has already committed her changes.
Or, it could be that Bob has turned off "autosync" mode in Fossil.  Or,
maybe Bob just doesn't want to merge in Alice's changes before he has
saved his own, so he forces the commit to occur using the "--allow-fork"
option to the fossil <b>commit</b> command.  For any of these reasons,
two commits against check-in 2 have occurred and now the DAG has two leaves.
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.

So which version of the project is the "latest" in the sense of having
Because of these potential problems, we strongly recommend that you do
the most features and the most bug fixes?  When there is more than
one leaf in the graph, you don't really know.  So we like to have
graphs with a single leaf.
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
To resolve this situation, Alice can use the fossil <b>merge</b> command
to merge in Bob's changes in her local copy of check-in 3.  Then she
can commit the results as check-in 5.  This results in a DAG as shown
in figure 3.
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.

<table border=1 cellpadding=10 hspace=10 vspace=10 align="center">
<tr><td align="center">
<img src="branch03.gif" width=282 height=152><br>
Figure 3
</td></tr></table>
<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 check-in 5 also inherits the changes from check-in 4 by
virtue of the merge.  So we say that check-in 5 is a <i>merge child</i>
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).
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
We have already seen that if Fossil is in autosync mode then Bob would
have been warned about the potential fork the first time he tried to
commit check-in 4.  If Bob had updated his local check-out to merge in
Alice's check-in 3 changes, then committed, then the fork would have
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.
in figure 1.  Really the graph of figure 1 is a subset of figure 3.
Hold your hand over the check-in 4 circle of figure 3 and then figure
3 looks exactly like figure 1 (except that the leaf has a different check-in
number, but that is just a notational difference - the two check-ins have
exactly the same content).  In other words, figure 3 is really 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 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 a
linear line of development and because the merging happens automatically
instead of as a separate manual step.  We will not take sides in that
debate.  We will simply point out that fossil enables you to do it either way.

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>Forking Versus Branching</h2>
<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.
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>.
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.

<table border=1 cellpadding=10 hspace=10 vspace=10 align="center">
<tr><td align="center">
<img src="branch04.gif" width=426 height=123><br>
Figure 4
</td></tr></table>
<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>

The hypothetical scenario of figure 4 is this:  The project starts and
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.  This is shown by check-ins 6 and 9.  Meanwhile the development
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
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,
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
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.  So, to a good
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.
<a name="tags"></a>
<h2>Tags And Properties</h2>

<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
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:
same scenario as Figure 4 but with tags and properties added:

<table border=1 cellpadding=10 hspace=10 vspace=10 align="center">
<tr><td align="center">
<img src="branch05.gif" width=485 height=177><br>
Figure 5
</td></tr></table>
<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
<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
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.
224
225
226
227
228
229
230
231

















































































































































































































232
233
234

235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253

254
255
256


257
258
259
260
261


262
263

264
265


266
267
268
269
270
271




























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








+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


-
+


















-
+

-
-
+
+



-
-
+
+

-
+


+
+






+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

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 &amp; 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>

<blockquote><dl>
<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></blockquote>
</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)
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
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."
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.

110
111
112
113
114
115
116
117
118


119
120
121
122
123
124
125
126
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".  Every new fossil
repository is created with default scripts.  Paul Ruizendaal has written
scripts written a Tcl-like language called "[./th1.md|TH1]".  Every new fossil
repository is created with default scripts. Administrators wishing to
documentation on the TH1 language that is available at
[http://www.sqliteconcepts.org/THManual.pdf].  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.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

































































































































































































































































































1
2
3
4

5
6
7
8
9
10

11
12
13
14
15
16
17
18

19
20
21
22
23
24
25
26
27

28
29
30
31

32
33
34
35
36

37
38
39

40
41


42
43
44

45
46

47
48

49
50


51
52
53
54
55
56
57
58
59

60
61
62
63
64
65
66
67

68
69
70
71
72
73
74
75
76
77
78


79
80
81
82
83
84
85
86
87
88
89



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115



116
117
118
119
120
121
122
123
124

125
126
127
128
129

130
131
132
133
134
135


136
137
138
139


140
141
142

143
144
145

146
147





148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173


174
175
176
177
178



179
180
181
182
183
184
185


186
187
188
189
190
191
192


193
194

195
196

197
198


199
200
201

202
203
204
205

206
207


208
209
210

211
212

213
214
215

216
217
218
219
220
221
222


223
224
225
226

227
228
229
230


231
232
233
234
235
236
237
238

239
240
241
242
243
244
245
246

247
248
249
250
251
252
253
254

255
256
257

258
259
260
261
262
263

264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
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>

<p>[/uv/download.html|Pre-compiled binaries] are available for recent
[/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.</p>
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>

<p>Building and installing is very simple.  Three steps:</p>
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>

<p><hr>
<hr>

<h2>1.0 Obtaining The Source Code</h2>

<p>Fossil is self-hosting, so you can obtain a ZIP archive or tarball
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:</p>
To obtain a development version of fossil, follow these steps:

<ol>
<li><p>Point your web browser to [https://www.fossil-scm.org/]</li>
<li>Point your web browser to [https://fossil-scm.org/]</li>

<li><p>Click on the [/timeline|Timeline]
link at the top of the page.</p></li>
<li>Click on the [/timeline|Timeline]
link at the top of the page.</li>

<li><p>Select a version of of Fossil you want to download.  The latest
<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.</p></li>
link.</li>

<li><p>Finally, click on one of the
<li>Finally, click on one of the
"Zip Archive" or "Tarball" links, according to your preference.
These link will build a ZIP archive or a gzip-compressed tarball of the
complete source code and download it to your computer.
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
[http://fossil-scm.org/fossil/timeline | Fossil self-hosting repository]
[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 [http://www.fossil-scm.org/fossil], usually run a version
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">
<p>Unpack the ZIP or tarball you downloaded then
<b>cd</b> into the directory created.</p></li>
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><p>
If you do not have the OpenSSL library installed on your system, then
add <b>--with-openssl=none</b> to omit the https functionality.
<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><p>
To build a statically linked binary (suitable for use inside a chroot
jail) add the <b>--static</b> option.
<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><p>
<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><p>
<li>
Other configuration options can be seen by running
<b>./configure --help</b>
</li>
</ol>

<li><p>Run "<b>make</b>" to build the "fossil" or "fossil.exe" executable.
The details depend on your platform and compiler.
<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><p><i>Unix</i> → the configure-generated Makefile should work on
all Unix and Unix-like systems.  Simply type "<b>make</b>".
<li><i>Unix</i> → the configure-generated Makefile should work on
all Unix and Unix-like systems.  Simply type "<b>make</b>".</li>

<li><p><i>Unix without running "configure"</i> → if you prefer to avoid
<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.
system.</li>

<li><p><i>MinGW 3.x (<u>not</u> 4.x) / MinGW-w64</i> → Use the MinGW makefile:
"<b>make -f win/Makefile.mingw</b>".  On a Windows box you will need either
Cygwin or Msys as build environment. On Cygwin, Linux or Darwin you may want
to make minor edits to win/Makefile.mingw to configure the cross-compile
environment.
<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, <b>./configure</b> may now be used to create a Makefile
suitable for use with MinGW; however, options passed to configure that are
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).

<i>HINT</i>: Do <u>not</u> use MinGW-4.x, it may compile but the Fossil binary
will not work correctly, see
[https://www.fossil-scm.org/index.html/tktview/18cff45a4e210430e24c | ticket].
<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.

<li><p><i>MSVC</i> → Use the MSVC makefile.  First
change to the "win/" subdirectory ("<b>cd win</b>") then run
First, change to the "win/" subdirectory ("<b>cd win</b>"), then run
"<b>nmake /f Makefile.msc</b>".<br><br>Alternatively, the batch
file "<b>win\buildmsvc.bat</b>" may be used and it will attempt to
detect and use the latest installed version of MSVC.<br><br>To enable
the optional <a href="https://www.openssl.org/">OpenSSL</a> support,
first <a href="https://www.openssl.org/source/">download the official
source code for OpenSSL</a> and extract it to an appropriately named
"<b>openssl-X.Y.ZA</b>" subdirectory within the local
[/tree?ci=trunk&name=compat | compat] directory (e.g.
"<b>openssl</b>" subdirectory within the local
[/tree?ci=trunk&name=compat | compat] directory then make sure that some recent
"<b>compat/openssl-1.0.2p</b>"), then make sure that some recent
<a href="http://www.perl.org/">Perl</a> binaries are installed locally,
and finally run one of the following commands:
<blockquote><pre>
<pre>
nmake /f Makefile.msc FOSSIL_ENABLE_SSL=1 FOSSIL_BUILD_SSL=1 PERLDIR=C:\full\path\to\Perl\bin
</pre></blockquote>
<blockquote><pre>
</pre>
<pre>
buildmsvc.bat FOSSIL_ENABLE_SSL=1 FOSSIL_BUILD_SSL=1 PERLDIR=C:\full\path\to\Perl\bin
</pre></blockquote>
</pre>
To enable the optional native [./th1.md#tclEval | Tcl integration feature],
run one of the following commands or add the &quot;FOSSIL_ENABLE_TCL=1&quot;
argument to one of the other NMAKE command lines:
<blockquote><pre>
<pre>
nmake /f Makefile.msc FOSSIL_ENABLE_TCL=1
</pre></blockquote>
<blockquote><pre>
</pre>
<pre>
buildmsvc.bat FOSSIL_ENABLE_TCL=1
</pre></blockquote>
</pre>

<li><p><i>Cygwin</i> → The same as other Unix-like systems. It is
<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.
"openssl-devel" packages installed first.</li>
</ol>
</ol>

<h2>3.0 Installing</h2>

<ol>
<li value="8">
<p>The finished binary is named "fossil" (or "fossil.exe" on Windows).
<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.</p>
It does not matter where.
</li>

<li>
<p><b>(Optional:)</b>
To uninstall, just delete the binary.</p>
<b>(Optional:)</b>
To uninstall, just delete the binary.
</li>
</ol>

<h2>4.0 Additional Considerations</h2>

<ul>
<li><p>
<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><p>
<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><p>
<li>
  To build on older Macs (circa 2002, MacOS 10.2) edit the Makefile
  generated by configure to add the following lines:
  <blockquote><pre>
  <pre>
  TCC += -DSQLITE_WITHOUT_ZONEMALLOC
  TCC += -D_BSD_SOURCE
  TCC += -DWITHOUT_ICONV
  TCC += -Dsocketlen_t=int
  TCC += -DSQLITE_MAX_MMAP_SIZE=0
</pre></blockquote>
  </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
&gt; cd /data/local/tmp
# Fossil requires a HOME directory to work with:
&gt; export HOME=$PWD
&gt; export PATH=$PWD:$PATH
&gt; fossil version
This is fossil version 2.11 &#91;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* &or; *reader)* &ge; *anonymous* &ge; *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* &ge; *admin* &ge; *moderator* &ge; *(developer* &or; *reader)* &ge; *[subscriber]* &ge; *anonymous* &ge; *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:&nbsp;argument&nbsp;...</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.

Changes to 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
1
2
3


4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
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


+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+














-
+







<title>Change Log</title>

<h2 id='v2_24'>Changes for version 2.24 (2024-04-23)</h2>
<a name='v2_7'></a>
<h2>Changes for Version 2.7 (2018-09-22)</h2>

  *  Apache change work-around &rarr; 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 were 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.  The hash is shown 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 &lt;table&gt;) in most cases.  Only content
     of &lt;pre&gt; and &lt;script&gt; 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 &lt;del&gt; and &lt;ins&gt; 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: &lt;script&gt;) 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 &lt;head&gt; section, so that it is always available, even
     if a custom skin overrides the HTML &lt;head&gt; 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 &lt;img&gt; 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.
     &rarr; 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 &#91;a-z&#93;, but with the
     enhancements, the supply of lower case letters was exhausted.
     User capabilities are now letters in &#91;a-zA-Z0-9&#93;.
  *  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 
  *  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
39
40
41
42
43
44
45
46
47

48
49
50
51
52
53
54
951
952
953
954
955
956
957


958
959
960
961
962
963
964
965







-
-
+







  *  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.

<a name='v2_6'></a>
<h2>Changes for Version 2.6 (2018-05-04)</h2>
<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.
68
69
70
71
72
73
74
75
76

77
78
79
80
81
82
83
979
980
981
982
983
984
985


986
987
988
989
990
991
992
993







-
-
+







     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.

<a name='v2_5'></a>
<h2>Changes for Version 2.5 (2018-02-07)</h2>
<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.
104
105
106
107
108
109
110
111
112

113
114
115
116
117
118
119
1014
1015
1016
1017
1018
1019
1020


1021
1022
1023
1024
1025
1026
1027
1028







-
-
+







  *  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.

<a name='v2_4'></a>
<h2>Changes for Version 2.4 (2017-11-03)</h2>
<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
144
145
146
147
148
149
150
151
152

153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173

174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190

191
192
193
194
195
196
197
198
199
200
201

202
203
204
205

206
207
208
209
210
211
212
213

214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229

230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251

252
253
254
255
256
257
258
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







-
-
+



















-
-
+















-
-
+









-
-
+



-
+






-
-
+















-
+




















-
-
+







     [/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.

<a name='v2_3'></a>
<h2>Changes for Version 2.3 (2017-07-21)</h2>
<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

<a name='v2_2'></a>
<h2>Changes for Version 2.2 (2017-04-11)</h2>
<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.

<a name='v2_1'></a>
<h2>Changes for Version 2.1 (2017-03-10)</h2>
<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.

<a name='v2_0'></a>
<h2>Changes for Version 2.0 (2017-03-03)</h2>
<h2 id='v2_0'>Changes for Version 2.0 (2017-03-03)</h2>

  *  Use the
     [https://github.com/cr-marcstevens/sha1collisiondetection|hardened SHA1]
     implemenation by Marc Stevens and Dan Shumow.
     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.

<a name='v1_37'></a>
<h2>Changes for Version 1.37 (2017-01-16)</h2>
<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
     respository database.
     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.


<a name='v1_36'></a>
<h2>Changes for Version 1.36 (2016-10-24)</h2>
<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.
274
275
276
277
278
279
280
281
282

283
284
285
286
287
288
289
1177
1178
1179
1180
1181
1182
1183


1184
1185
1186
1187
1188
1189
1190
1191







-
-
+







     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.

<a name='v1_35'></a>
<h2>Changes for Version 1.35 (2016-06-14)</h2>
<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
315
316
317
318
319
320
321
322

323
324
325
326

327
328
329
330
331
332
333
1217
1218
1219
1220
1221
1222
1223

1224
1225
1226


1227
1228
1229
1230
1231
1232
1233
1234







-
+


-
-
+







  *  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.
  *  Numerious documentation improvements.
  *  Numerous documentation improvements.
  *  Update the built-in SQLite to version 3.13.0.

<a name='v1_34'></a>
<h2>Changes for Version 1.34 (2015-11-02)</h2>
<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".
354
355
356
357
358
359
360
361
362

363
364
365
366
367
368
369
1255
1256
1257
1258
1259
1260
1261


1262
1263
1264
1265
1266
1267
1268
1269







-
-
+







  *  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).

<a name='v1_33'></a>
<h2>Changes for Version 1.33 (2015-05-23)</h2>
<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].
404
405
406
407
408
409
410
411
412

413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429

430
431
432
433
434
435
436
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







-
-
+















-
-
+







  *  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.

<a name='v1_33'></a>
<h2>Changes for Version 1.32 (2015-03-14)</h2>
<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].

<a name='v1_31'></a>
<h2>Changes For Version 1.31 (2015-02-23)</h2>
<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
473
474
475
476
477
478
479
480
481

482
483
484
485
486
487
488
1371
1372
1373
1374
1375
1376
1377


1378
1379
1380
1381
1382
1383
1384
1385







-
-
+







  *  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.

<a name='v1_30'></a>
<h2>Changes For Version 1.30 (2015-01-19)</h2>
<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
544
545
546
547
548
549
550
551
552

553
554
555
556
557
558
559
1441
1442
1443
1444
1445
1446
1447


1448
1449
1450
1451
1452
1453
1454
1455







-
-
+







     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.

<a name='v1_29'></a>
<h2>Changes For Version 1.29 (2014-06-12)</h2>
<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
616
617
618
619
620
621
622
623

624
625
626
627
628
629
630
1512
1513
1514
1515
1516
1517
1518

1519
1520
1521
1522
1523
1524
1525
1526







-
+







  *  Enhance the SSH transport mechanism so that it runs a single instance of
     the "fossil" executable on the remote side, obviating the need for a shell
     on the remote side.  Some users may need to add the "?fossil=/path/to/fossil"
     query parameter to "ssh:" URIs if their fossil binary is not in a standard
     place.
  *  Add the "[/help?cmd=blame | fossil blame]" command that works just like
     "fossil annotate" but uses a different output format that includes the
     user who made each changes and omits line numbers.
     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
822
823
824
825
826
827
828
829

830
831
832
833
834
835
836
1718
1719
1720
1721
1722
1723
1724

1725
1726
1727
1728
1729
1730
1731
1732







-
+







  *  Improved detection of forks in a commit race.
  *  Added the --analyze option to "fossil rebuild".

<h2>Changes For Version 1.24 (2012-10-22)</h2>
  *  Added support for WYSIWYG editing of wiki pages. WYSIWYG is turned off
     by default and can be turned on by setting a configuration option.
  *  Allow style= attribute to occur in HTML markup on wiki pages.
  *  Added the --tk option to the "fossi diff" and "fossil stash diff"
  *  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=
1040
1041
1042
1043
1044
1045
1046
1047

1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1936
1937
1938
1939
1940
1941
1942

1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954







-
+











  *  Performance improvements on SHA1 checksums
  *  Update to the latest SQLite version 3.7.8 alpha.
  *  Fix the tarball generator to work with very log pathnames

<h2>Changes For Version 1.18 (2011-07-14)</h2>
  *  Added this Change Log
  *  Added sequential version numbering
  *  Added a optional configure script - the Makefile still works for most
  *  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** &rarr;
     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** &rarr;
     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** &rarr;
     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) &rarr;
     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) &rarr;
     Sends a new chat message to the server.

  *  [/chat-delete](/help?name=/chat-delete) &rarr;
     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) &rarr;
    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`.

Changes to www/checkin.wiki.

39
40
41
42
43
44
45
46
47


48
49
50

51
52
53
54
55
56
57
39
40
41
42
43
44
45


46
47
48
49

50
51
52
53
54
55
56
57







-
-
+
+


-
+







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>-N</b>
command-line option to show the complete text newly added files.
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 -N</b>
   <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

Changes to 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




















1
2


3
4



5


6
7
8
9
10

11
12
13
14
15
16
17
18
19

20
21
22



23
24
25
26

27
28
29



30
31
32
33
34
35



36
37
38
39


40
41
42
43
44
45
46
47

48
49
50


51
52
53

54
55

56
57

58
59
60
61
62
63

64
65
66
67

68
69


70
71
72
73

74
75
76
77



78
79
80
81

82
83

84
85

86
87



88
89
90



91



92
93
94
95

96
97
98



99
100
101
102
103
104



105
106
107
108
109
110
111
112

113
114

115
116
117

118
119
120

121
122
123

124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141

142
143
144
145
146




147
148
149
150
151
152
153
154
155


156
157
158
159
160

161
162
163
164
165
166
167

168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185



186
187
188
189


190
191
192
193



194
195
196
197
198
199
200
201

202
203
204
205
206
207
208

209
210

211
212
213

214
215
216

217
218
219

220
221
222
223
224

225
226
227

228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253

254
255


256
257
258
259
260





261
262
263
264
265
266
267
268
269
270
271
272
273
274
275

276




277
278
279
280
281
282

283
284
285
286
287

288
289

290
291
292
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>

<table align="right" border="1" width="33%" cellpadding="10">
<tr><td>
<div class="sidebar no-label">
<b>Quick Reference</b>
<h3>Executive Summary</h3>
<p>A check-in can be identified using any of the following
names:
<ul>
<li> Cryptographic hash prefix
<li> Tag or branchname
<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 :</b> <i>branchname</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> for embedded docs
<li> <b>ckout</b> (<a href='./embeddeddoc.wiki'>embedded docs</a> only)
</ul>
</ul>
</td></tr>
</table>
Many Fossil [/help|commands] and [./webui.wiki | web-interface] URLs accept
</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 checkout
accepts an optional check-in name to identify the specific check-in
about which information is desired:

<blockquote>
<tt>fossil info</tt> <i>checkin-name</i>
</blockquote>
<pre style="white-space: pre-wrap">
fossil info <i>checkin-name</i>
</pre>

You are perhaps reading this page from the following URL:

<blockquote>
http://www.fossil-scm.org/fossil/doc/<b>trunk</b>/www/checkin_names.wiki
</blockquote>
<verbatim>
https://fossil-scm.org/home/doc/trunk/www/checkin_names.wiki
</verbatim>

The URL above is an example of an [./embeddeddoc.wiki | embedded documentation]
page in Fossil.  The bold term of the pathname is a check-in name that
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>Canonical Check-in Name</h2>
<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 40-or-more character
lowercase hexadecimal number.  For example:
[./fileformat.wiki#manifest | manifest] expressed as a
[./hashes.md | long lowercase hexadecimal number].  For example:

<blockquote><pre>
<pre>
fossil info e5a734a19a9826973e1d073b49dc2a16aa2308f9
</pre></blockquote>
</pre>

The full 40+ character hash is unwieldy to remember and type, though,
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:

<blockquote><pre>
<pre>
fossil info e5a734a19a9
fossil info E5a734A
fossil info e5a7
</blockquote>
</pre>

Many web-interface screens identify check-ins by 10- or 16-character
prefix of canonical name.
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>Tags And Branch Names</h2>
<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, as of this writing the most recent check-in that
is tagged with "release" is [d0753799e44].
So the command:
So for example, the most recent check-in that
is tagged with "release" as of this writing is [b98ce23d4fc].
The command:

<blockquote><pre>
<pre>
fossil info release
</pre></blockquote>
</pre>

Results in the following input:
…results in the following output:

<blockquote><pre>
uuid:         d0753799e447b795933e9f266233767d84aa1d84 2010-11-01 14:23:35 UTC
parent:       4e1241f3236236187ad2a8f205323c05b98c9895 2010-10-31 21:51:11 UTC
<pre>
hash:         b98ce23d4fc3b734cdc058ee8a67e6dad675ca13 2020-08-20 13:27:04 UTC
parent:       40feec329163103293d98dfcc2d119d1a16b227a 2020-08-20 13:01:51 UTC
child:        4a094f46ade70bd9d1e4ffa48cbe94b4d3750aef 2010-11-01 18:52:37 UTC
child:        f4033ec09ee6bb2a73fa588c217527a1f311bd27 2010-11-01 23:38:34 UTC
tags:         trunk, release
tags:         release, branch-2.12, version-2.12.1
comment:      Fix a typo in the file format documentation reported on the
              Tcl/Tk chatroom. (user: drh)
</pre></blockquote>
comment:      Version 2.12.1 (user: drh)
</pre>

There are multiple check-ins that are tagged with "release" but
(as of this writing) the [d0753799e44]
(as of this writing) the [b98ce23d4fc]
check-in is the most recent so it is the one that is selected.

Note that unlike other command DVCSes, 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
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) be an ambiguity between tag names
and canonical names.  Suppose, for example, you had a check-in with
the canonical name deed28aa99a835f01fa06d5b4a41ecc2121bf419 and you
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:

<blockquote><tt>
<pre>
fossil info tag:deed2
</tt></blockquote>
</pre>

The "tag:deed2" name will refer to the most recent check-in
tagged with "deed2" not to the
tagged with "deed2" rather than the
check-in whose canonical name begins with "deed2".

<h2>Whole Branches</h2>
<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
that branch, but for some commands (ex: [/help/purge|purge]) a branch name
on the argument means the earliest connected check-in on the branch.  This
seems confusing when being explained here, but it works out to be intuitive
in practice.

For example, the command "fossil purge XYZ" means to purge the check-in XYZ
and all of its descendants.  But when XYZ is in the form of a branch name, one
generally wants to purge the entire branch, not just the last check-in on the
branch.  And so for this reason, commands like purge will interpret a branch
name to be the first check-in of the branch rather than the last.  If there
are two or more branches with the same name, then these commands will select
the first check-in of the branch that has the most recent check-in.  What
happens is that Fossil searches for the most recent check-in with the given
tag, just as it always does.  But if that tag is a branch name, it then walks
back down the branch looking for the first check-in of that branch.

Again, this behavior only occurs on a few commands where it make sense.

<h2>Timestamps</h2>
<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:

  *   <i>YYYY-MM-DD</i>
  *   <i>YYYY-MM-DD HH:MM</i>
  *   <i>YYYY-MM-DD HH:MM:SS</i>
  *   <i>YYYY-MM-DD HH:MM:SS.SSS</i>
  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
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.
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
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:

<blockquote>
http://www.fossil-scm.org/fossil/doc/<b>trunk</b>/www/index.wiki
</blockquote>
<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 what the
Fossil website looked like on January 1, 2009, one has merely to change
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:

<blockquote>
http://www.fossil-scm.org/fossil/doc/<b>2009-01-01</b>/www/index.wiki
</blockquote>
<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>Tag And Timestamp</h2>
<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:

<blockquote>
<pre>
fossil update trunk:2010-07-01T14:30
</blockquote>
</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 that 14:30 (UTC) on
check-in on the trunk that is not more recent than 14:30 (UTC) on
July 1, 2010.

<h2>Root Of A Branch</h2>
<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 in the parent branch prior to the beginning of the branch.
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":

<blockquote>
<pre>
fossil diff --from root:xyzzy --to xyzzy
</pre>
</blockquote>

<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>Special Tags</h2>
<h2 id="special">Special Tags</h2>

The tag "tip" means the most recent check-in.  The "tip" tag is roughly
equivalent to the timestamp tag "5000-01-01".
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:
If the command is being run from a working check-out (not against a bare
repository) then a few extra tags apply.  The "current" tag means the
current check-out.  The "next" tag means the youngest child of the
current check-out.  And the "previous" or "prev" tag means the primary
(non-merge) parent of the current check-out.

<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, the tag "ckout" means the version as present in

the local source tree on disk, provided that the web server is started using
"fossil ui" or "fossil server" from within the source tree. This tag can be
used to preview local changes to documentation before committing them. It does
not apply to CLI commands.
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>Additional Examples</h2>
<h2 id="examples">Additional Examples</h2>

To view the changes in the most recent check-in prior to the version currently
checked out:

<blockquote><pre>
<pre>
fossil diff --from previous --to current
</pre></blockquote>
</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:

<blockquote><pre>
<pre>
fossil diff --from release --to trunk
</pre></blockquote>
</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>

Changes to www/childprojects.wiki.

26
27
28
29
30
31
32
33

34
35
36
37
38
39
40

41
42
43
44
45
46
47
26
27
28
29
30
31
32

33
34
35
36
37
38
39

40
41
42
43
44
45
46
47







-
+






-
+







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:

<blockquote><verbatim>
<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></blockquote>
</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.

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>

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 (&lt;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 (&gt;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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21


22
23
24

















25
26
27
28
29
30
31
1

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18


19
20
21
22

23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

-

















-
-
+
+


-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







<title>Fossil Concepts</title>
<h1 align="center">Fossil Concepts</h1>

<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:

  *  [./whyusefossil.wiki#definitions|Definitions]
  *  [./quickstart.wiki|Quick start guide]
  *  [./glossary.md | Glossary]
  *  [./quickstart.wiki | Quick start guide]

<h2>2.0 Composition Of A Project</h2>
<img src="concept1.gif" align="right" hspace="10">

<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.
73
74
75
76
77
78
79
80


81
82
83
84
85

86
87
88
89
90
91








92
93
94
95
96





97
98
99
100
101
102
103
104





105
106

107
108
109
110
111
112
113
88
89
90
91
92
93
94

95
96
97
98
99
100

101
102





103
104
105
106
107
108
109
110





111
112
113
114
115
116
117
118





119
120
121
122
123
124

125
126
127
128
129
130
131
132







-
+
+




-
+

-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+



-
-
-
-
-
+
+
+
+
+

-
+







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 via HTTP.  Remote
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>2.1 Identification Of Artifacts</h3>
<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> hash of the content
of that file expressed as 40 characters of lower-case hexadecimal.  Such
a hash is referred to as the Artifact Identifier or Artifact ID
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
for the artifact.  The SHA1 algorithm is created with the purpose of
providing a highly forgery-resistant identifier for a file.  Given any
file it is simple to find the artifact ID for that file.  But given a
artifact ID it is computationally intractable to generate a file that will
have that Artifact ID.
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:

<blockquote><b>
6089f0b563a9db0a6d90682fe47fd7161ff867c8<br>
59712614a1b3ccfd84078a37fa5b606e28434326<br>
19dbf73078be9779edd6a0156195e610f81c94f9<br>
b4104959a67175f02d6b415480be22a239f1f077<br>
<pre>
6089f0b563a9db0a6d90682fe47fd7161ff867c8
59712614a1b3ccfd84078a37fa5b606e28434326
19dbf73078be9779edd6a0156195e610f81c94f9
b4104959a67175f02d6b415480be22a239f1f077
997c9d6ae03ad114b2b57f04e9eeef17dcb82788
</b></blockquote>
</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.

137
138
139
140
141
142
143
144

145
146
147
148
149
150
151
152

153
154
155
156
157
158

159
160

161
162
163
164
165
166

167
168
169

170
171
172
173
174
175
176
156
157
158
159
160
161
162

163
164
165
166
167
168
169
170

171
172
173
174
175
176

177
178

179
180
181
182
183
184

185
186


187
188
189
190
191
192
193
194







-
+







-
+





-
+

-
+





-
+

-
-
+







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.

<p>The manifest file is not normally a real file on disk.  Instead,
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.

<p>Fossil automatically generates a manifest whenever you "commit"
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.</p>
need to do so.

<p>In addition to identifying all files in the check-in, a
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.</p>
be PGP clearsigned.

<a name="keyconc"></a>
<h3>2.3 Key concepts</h3>
<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>
185
186
187
188
189
190
191
192

193
194
195
196
197
198

199
200
201
202
203
204
205
203
204
205
206
207
208
209

210
211
212
213
214
215

216
217
218
219
220
221
222
223







-
+





-
+







<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://www.fossil-scm.org/fossil/uv/download.html">pre-compiled version</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,
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,
218
219
220
221
222
223
224
225
226

227
228
229
230
231
232
233
234

235

















236

237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256





257
258
259
260
261
262
263
236
237
238
239
240
241
242


243

244
245
246
247
248


249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267

268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283





284
285
286
287
288
289
290
291
292
293
294
295







-
-
+
-





-
-
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+















-
-
-
-
-
+
+
+
+
+







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:

<blockquote><b>
fossil help
<pre>fossil help</pre>
</b></blockquote>

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.

<a name="workflow"></a>
<h2>4.0 Workflow</h2>
<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>
<img src="concept2.gif" align="right" hspace="10">


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:

<blockquote>
<b>fossil setting autosync on<br>
fossil setting autosync off<br>
<b>fossil settings</b>
</blockquote>
<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.
398
399
400
401
402
403
404
405

406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422















423
424
425

426
427
428
429
430
431
432
430
431
432
433
434
435
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







-
+

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-

-
+







</ol>

<h2>5.0 Setting Up A Fossil Server</h2>

With other configuration management software, setting up a server is
a lot of work and normally takes time, patience, and a lot of system
knowledge.  Fossil is designed to avoid this frustration.  Setting up
a server with Fossil is ridiculously easy.  You have four options:</p>
a server with Fossil is ridiculously easy.  You have four options:

<ol>
<li><p><b>Stand-alone server.</b>
Simply run the [/help?cmd=server|fossil server] or
[/help?cmd=ui|fossil ui] command from the command-line.

<li><p><b>CGI.</b>
Install a 2-line CGI script on a CGI-enabled web-server like Apache.

<li><p><b>SCGI.</b>
Start an SCGI server using the
[/help?cmd=server| fossil server --scgi] command for handling
SCGI requests from web-servers like Nginx.

<li><p><b>Inetd or Stunnel.</b>
Configure programs like inetd, xinetd, or stunnel to hand off HTTP requests
directly to the [/help?cmd=http|fossil http] command.
  #  <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.
</ol>

See the [./server.wiki | How To Configure A Fossil Server] document
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>

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&nbsp;at&nbsp;sqlite&nbsp;dot&nbsp;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&nbsp;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 &mdash; the machine name &mdash; 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>

Changes to 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
1
2

3
4
5
6
7
8

9
10
11
12
13


14
15
16
17
18
19

20
21
22
23
24
25
26
27
28

29
30
31
32
33
34
35
36
37
38
39
40

41
42
43
44
45
46
47
48
49
50
51




52
53
54
55
56

57
58

59
60

61
62
63
64
65
66
67



68
69
70
71
72
73
74
75

76
77
78
79
80

81
82
83
84
85
86
87
88
89
90






91
92
93
94
95
96
97
98
99
100
101
102
103
104
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>

Users are encouraged to contributed enhancements back to the Fossil
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 your contributions, we <u>must</u> have a
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 larger companies
and other lawyer-rich organizations require this as a precondition to using
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
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.  Email patches to
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 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.
However, without a contributor agreement on file, your patch will be
used for reference only - it will not be applied to the code.  This
may delay acceptance of your patch.
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 patches or changes might not be accepted even if you do have
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 patches are rejected
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.  Or, there might be an alternative
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 the
[http://www.mail-archive.com/fossil-users@lists.fossil-scm.org/ | mailing list].
A contributor agreement is, of course, a prerequisite for check-in
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 following the
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 notecard beside their workstations, for quick reference.
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 has the beginnings of a
[../test/release-checklist.wiki | release checklist] but this is an
area that needs further work.  (Your contributions here are welcomed!)
Contributors with check-in privileges are expected to run the release
checklist on any major changes they contribute, and if appropriate expand
the checklist and/or the automated test scripts to cover their additions.
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>5.0 See Also</h2>
<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]

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 &lt;table&gt;.  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
&lt;table&gt;.  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 &lt;table&gt; 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 &lt;pre&gt; that contains the
appropriate diff-text for that column.  Scrolling is done on the
&lt;pre&gt; element.

Within text columns, highlighting is done with &lt;del&gt; and
&lt;ins&gt; markup.  All text on a line that contains an isert or
delete is surrounded by &lt;ins&gt;...&lt;/ins&gt; or
&lt;del&gt;..&lt;/del&gt;.  Within that line, specific characters
of text that specifically inserted deleted have an additional
layer of &lt;ins&gt; or &lt;del&gt; 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 &lt;ins&gt; or &lt;del&gt; that mark
specific characters that change correspond to a delete/insert on the other
side, they they have the "edit" class tag.  (ex:  &lt;ins&nbsp;class='edit'&gt;
or &lt;del&nbsp;class='edit'&gt;).  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 &lt;ins&gt; and &lt;del&gt; 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
1

2
3

4
5
6
7

8


9
10
11
12
13
14
15
16
17
18
19
20
21


22


23
24
25
26
27
28
29
30
31
32
33
34
35
36
37


38


39
40
41
42








43
44
45
46
47
48
49
50
51
52
53
54
55











56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73


74
75
76
77
78
79
80


81
82
83
84









85
86
87
88
89
90
91
92
93
94
95

96


97


98
99
100






101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119



120
121
122
123
124
125




126
127
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>
<nowiki>

<h2>Introduction</h2>
<p>

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.
</p>

<h2>First modify the TICKET table</h2><blockquote>
<p>
<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?
</p>
</blockquote>

<h2>Next add assignees</h2><blockquote>
<p>
<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).
</p>
</blockquote>

<h2>Now modify the 'new ticket' page</h2><blockquote>
<p>
<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:
<pre>
  if {[info exists submit]} {
     set status Open
     set opened_by $login
	 set assigned_to "unassigned"
     submit_ticket
  }
</pre>

<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:
<pre>
&lt;th1>enable_output [expr { "$login" eq "anonymous"}]&lt;/th1>
&lt;tr>
&lt;td align="right">EMail:
&lt;input type="text" name="private_contact" value="$&lt;private_contact>" size="30">
&lt;/td>
&lt;td>&lt;u>Not publicly visible&lt;/u>. Used by developers to contact you with
questions.&lt;/td>
&lt;/tr>
&lt;th1>enable_output 1&lt;/th1>
</pre>

<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.
</p>
</blockquote>

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><blockquote>
<p>
<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:
<pre>
<tr>
  &lt;td align="right">Assigned to:&lt;/td>&lt;td bgcolor="#d0d0d0">
  $&lt;assigned_to>
  &lt;/td>
  &lt;td align="right">Opened by:&lt;/td>&lt;td bgcolor="#d0d0d0">
  $&lt;opened_by>
  &lt;/td>
</pre>

<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
"edit" capability.
<a href="./caps/ref.html#w">ticket "edit" capability</a>.
</p>
</blockquote>

<h2>Modify the 'edit ticket' page</h2><blockquote>
<p>
<h2>Modify the 'edit ticket' page</h2>

Before the "Severity:" line, add this:
<pre>
&lt;tr>&lt;td align="right">Assigned to:&lt;/td>&lt;td>
&lt;th1>combobox assigned_to $assigned_choices 1&lt;/th1>
&lt;/td>&lt;/tr>
</pre>
That will give you a drop-down list of assignees.  Now, similar to the previous

<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:
<pre>
  &lt;tr>&lt;td align="right">Reported by:&lt;/td>&lt;td>
  &lt;input type="text" name="opened_by" size="40"

<verbatim>
<tr>
  <td align="right">Reported by:</td>
  <td>
    <input type="text" name="opened_by" size="40" value="$<opened_by>">
   value="$&lt;opened_by>">
  &lt;/td>&lt;/tr>
</pre>
</p>
  </td>
</tr>
</verbatim>
</blockquote>

<h2>What next?</h2><blockquote>
<p>
<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:
<pre>

<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()
</pre>
</p>
</verbatim>
</blockquote>
</nowiki>

Changes to 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
1
2
3
4
5
6
7
8
9
10
11

12
13
14
15

16
17
18
19

20
21
22
23

24
25
26
27
28
29
30
31











-
+



-
+



-
+



-
+







# 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`
*   **`timeline-arrowheads`**

    Set this to `0` to hide arrowheads on primary child lines.

*   ###`timeline-circle-nodes`
*   **`timeline-circle-nodes`**

    Set this to `1` to make check-in nodes circular instead of square.

*   ###`timeline-color-graph-lines`
*   **`timeline-color-graph-lines`**

    Set this to `1` to colorize primary child lines.

*   ###`white-foreground`
*   **`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

40
41
42
43
44
45
46
47

48
49
50
51
52

53
54
55
56

57
58
59
60
61
62
63
64
65

66
67
68
69
70
71
72
73

74
75
76
77
78
79
80
81
82
83
84
85
86

87
88
89
90

91
92
93
94
95
96
97
98
99
100
101
102

103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123

124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142

143
144
145
146
147
148
149
40
41
42
43
44
45
46

47
48
49
50
51

52
53
54
55

56
57
58
59
60
61
62
63
64

65
66
67
68
69
70
71
72

73
74
75
76
77
78
79
80
81
82
83
84
85

86
87
88
89

90
91
92
93
94
95
96
97
98
99
100
101

102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122

123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141

142
143
144
145
146
147
148
149







-
+




-
+



-
+








-
+







-
+












-
+



-
+











-
+




















-
+


















-
+







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`
*   <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
    **Additional Classes**

    * `.sel`: See [`.tl-node`](#tl-node) for more information.

*   ###<a id="tl-rail"></a>`.tl-rail`
*   <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 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`
*   <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 id="tl-node"></a>**`.tl-node`**

    A node exists for each check-in in the timeline.

    #### Additional Classes
    **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`
*   <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`
*   <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
    **Additional Classes**

    *   `.merge`: A merge line.

    *   `.h` or `.v`: Horizontal or vertical.

    *   `.warp`: A timewarped line.

Changes to 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

1

2


3
4








5




6

7






8

9





10




11








12
13


14


15
16
17
18


19
20
21
22
23


24
25
26

27




28


29

30

31
32
33
34

35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

55
56
57
58


59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328



329
330
331
332
333


334
335
336
337
338

339
340
341


342
343
344


345


346





347

348

349


350



351
352




353
354
355
356
357
358

359

360
361
362
363



364
365
366
367
368

369
370

371
372
373
374
375


376
377
378
379
380
381
382


383
384
385

386
387
388

389
390
391
392
393
394





















395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422

423

424
425
426

427
428

429
430
431
432

433
434
435
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
-
+
-

-
-
+
+
-
-
-
-
-
-
-
-
+
-
-
-
-
+
-

-
-
-
-
-
-
+
-

-
-
-
-
-
+
-
-
-
-
+
-
-
-
-
-
-
-
-
+
+
-
-

-
-
+
+
+

-
-
+
+
+
+
+
-
-
+
+

-
+
-
-
-
-
+
-
-

-
+
-




-
+



















-
+


+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
+
+
+


-
-
+
+



-
+


-
-
+
+

-
-
+
-
-
+
-
-
-
-
-

-
+
-

-
-
+
-
-
-
+
+
-
-
-
-
+





-
+
-




-
-
-
+
+
+


-
+

-
+
+



-
-
+
+
+
+
+

+
-
-
+
+

-
+


-
+





-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+






+
-
+
-



-
+

-
+



-
+



-
+







-
+

-
+


-
+



-
+


-
+



-
+

+
+
+
+
+
+
+
+
-
+

-
+


-
+


-
+


-
+


-
+


-
+


-
+







+
-
+
-


















-
+




-
+








+
-
+
-


Theming
# Skinning the Fossil Web Interface
=======

Every HTML page generated by Fossil has the following basic structure:

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
<blockquote><table border=1 cellpadding=10><tbody>
<tr><td style='background-color:lightblue;text-align:center;'>Header</td></tr>
<tr><td style='background-color:lightgreen;text-align:center;'>
Fossil-Generated Content</td></tr>
<tr><td style='background-color:lightblue;text-align:center;'>Footer</td></tr>
<tr><td style='background-color:lightyellow;text-align:center;'>Javascript (optional)</td></tr>
</tbody></table></blockquote>

the look and feel (the "skin") of Fossil to better suite your own individual tastes.
The header and footer control the "look" of Fossil pages.  Those
two sections can be customized separately for each repository to
develop a new theme.

This document provides background information to aid you in that task.
The header will normally look something like this:

        <html>
        <head> ... </head>
        <body>
        ... top banner and menu bar ...
        <div class='content'>

## <a id="builtin"></a>Built-in Skins
And the footer will look something like this:

        </div>
        ... bottom material ...
        </body>
        </html>

Fossil comes with [multiple built-in skins](/skins).  If the default skin does not
The &lt;head&gt; element in the header will normally reference the
/style.css CSS file that Fossil stores internally.  (The $stylesheet_url
TH1 variable, described below, is useful for accomplishing this.)

suite your tastes, perhaps one of the other built-in skins will work better.
The middle "content" section comprised the bulk of most pages and
contains the actual Fossil-generated data
that the user is interested in seeing.  The text of this content
section is not normally configurable.  The content text can be styled
using CSS, but it otherwise fixed.  Hence it is the header and footer
and the CSS that determine the look of a repository.
We call the bundle of built-in CSS, header, and footer a "skin".

If nothing else, the built-in skins can serve as examples or templates that
you can use to develop your own custom skin.
Built-in Skins
--------------

Fossil comes with several built-in skins.  The sources to these built-ins can
be found in the Fossil source tree under the skins/ folder.  The skins/
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 four files, "css.txt", "details.txt",
"footer.txt", and "header.txt",
subfolders holding at least these five files:

   * css.txt
   * details.txt
   * footer.txt
that describe the CSS, rendering options,
footer, and header for that skin, respectively.
   * header.txt
   * js.txt

The skin of a repository can be changed to any of the built-in skins using
Try out the built-in skins by using the --skin option on the
the web interface by going to the /setup_skin web page (requires Admin
privileges) and clicking the appropriate button.  Or, the --skin command
line option can be used for the
[fossil ui](../../../help?cmd=ui) or
[fossil ui](/help?cmd=ui) or [fossil server](/help?cmd=server) commands.
[fossil server](../../../help?cmd=server) commands to force that particular
instance of Fossil to use the specified built-in skin.

Sharing Skins
## <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](/help?cmd=configuration) command.
The "fossil config push skin" command will send the local skin to a remote
repository and the "fossil config pull skin" command will import a skin
from a remote repository.  The "fossil config export skin FILENAME"
will export the skin for a repository into a file FILENAME.  This file
can then be imported into a different repository using the
"fossil config import FILENAME" command.  Unlike "push" and "pull",
the "export" and "import" commands are able to move skins between
repositories for different projects.  So, for example, if you have a
group of related repositories, you can develop a skin for one of them,
then get a consistent look across all the repositories by exporting
the skin from the first repository and importing into all the others.

The file generated by "fossil config export" could be checked into
one of your repositories and versioned, if desired.  This will not
automatically change the skin when looking backwards in time, but it
will provide an historical record of what the skin used to be and
allow the historical look of the repositories to be recreated if
necessary.

When cloning a repository, the skin of new repository is initialized to
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
Header And Footer Processing
----------------------------

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>
&lt;script nonce="$nonce"&gt;
  &lt;th1&gt;styleScript&lt;/th1&gt;
&lt;/script&gt;
</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>
&lt;th1&gt;builtin_request_js hbmenu.js&lt;/th1&gt;
</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 files of a skin are merely the HTML text
of the header and footer.  Except, before being prepended and appended to
the content, the header and footer text are run through a
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 &lt;th1&gt;...&lt;/th1&gt; is elided from the
     output and that text is instead run as a TH1 script.  That TH1
  *  All text within &lt;th1&gt;...&lt;/th1&gt; 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 for the form "$NAME" or "$&lt;NAME&gt;" is replace with
  *  Text of the form "$NAME" or "$&lt;NAME&gt;" is replaced with
     the value of the TH1 variable NAME.

For example, the following is the first few lines of a typical
header file:
For example, first few lines of a typical Skin Header will look
like this:

       <html>
        <head>
    <div class="header">
         <base href="$baseurl/$current_page" />
         <title>$<project_name>: $<title></title>
      <div class="title"><h1>$<project_name></h1>$<title>/div>
         <link rel="alternate" type="application/rss+xml" title="RSS Feed"
               href="$home/timeline.rss" />
         <link rel="stylesheet" href="$stylesheet_url" type="text/css"
               media="screen" />
       </head>

After variables are substituted by TH1, the final header text
After variables are substituted by TH1, that will look more like this:
delivered to the web browser might look something like this:

        <html>
         <head>
    <div class="header">
          <base href="https://www.fossil-scm.org/skin2/timeline" />
          <title>Fossil: Timeline</title>
          <link rel="alternate" type="application/rss+xml" title="RSS Feed"
      <div class="title"><h1>Project Name</h1>Page Title</div>

                href="/skin2/timeline.rss" />
          <link rel="stylesheet" href="/skin2/style.css?default" type="text/css"
                media="screen" />
         </head>
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.

Customizing the ≡ Hamburger Menu
## <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 contained in the optional Javascript part (js.txt)
of the default skin. Out of the box, the drop-down menu shows the [Site
Map](../../../sitemap), loaded by an AJAX request prior to the first display.
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 command in the default skin header.txt, right before the menu bar links:
TH1 commands in the `header.txt` file, right before the menu bar links:

        html "<a id='hbbtn' href='#'>&#9776;</a>"
    html "<a id='hbbtn' href='$home/sitemap'>&#9776;</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 statement (the Javascript logic detects this case and
remains idle, so it's not necessary to modify the default skin js.txt).
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 empty element at the bottom of the default skin header.txt serves
as the panel to hold the drop-down menu elements:
the following `<div>` element somewhere in your header, in which to build
the hamburger menu.

        <div id='hbdrop'></div>
    <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
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>
    <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"`.


TH1 Variables
## <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
respository settings and the specific page being generated.
repository settings and the specific page being generated.

   *   **project_name** - The project_name variable is filled with the
   *   **`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
   *   **`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
   *   **`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.
   *   **`baseurl`** - The root of the URL namespace for this server.

   *   **secureurl** - The same as $baseurl except that if the scheme is
   *   **`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,
   *   **`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
   *   **`index_page`** - The landing page URI as
       specified by the Admin/Configuration setup page.

   *   **current_page** - The name of the page currently being processed,
   *   **`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.
   *   **`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"
   *   **`release_version`** - The release version of Fossil.  Ex: "1.31"

   *   **manifest_version** - A prefix on the check-in hash of the
   *   **`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
   *   **`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
   *   **`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.
   *   **`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
   *   **`stylesheet_url`** - A URL for the internal style-sheet maintained
       by Fossil.

   *   **log\_image\_url** - A URL for the logo image for this project, as
   *   **`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
   *   **`background_image_url`** - A URL for a background image for this
       project, as configured on the Admin/Logo page.

All of the above are variables in the sense that either the header or the
footer is free to change or erase them.  But they should probably be treated
as constants.  New predefined values are likely to be added in future
releases of Fossil.


Suggested Skin Customization Procedure
## <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
   2.  Run the [fossil ui](/help?cmd=ui) command with an extra
       option "--skin SKINDIR" where SKINDIR is the name of the directory
       in which the three txt files were stored in step 1.   This will bring
       up the Fossil website using the tree files in SKINDIR.

   3.  Edit the four txt files in SKINDIR.  After making each small change,
   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
## 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&nbsp;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
1

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

38
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>
<nowiki>

<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>

<a name="argresparam"></a><h2>1.0 Arguments, Results, and Parameters</h2>
<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>

<a name="operation"></a><h2>2.0 Operation</h2>
<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>

<a name="preprocessing"></a><h3>2.1 Preprocessing the original</h3>
<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
81
82
83
84
85
86
87
88

89
90
91
92
93
94
95
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>

<a name="processing"></a><h3>2.1 Processing the target</h3>
<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>
105
106
107
108
109
110
111












112















113
114
115
116
117
118
119
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
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 ""
]
<img src="encode10.gif" align="right" hspace="10">

[ "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
165
166
167
168
169
170
171
172

173
174
175
176
177
178
179
180
181
182

183
184
185
186
187
188
189
190

191
192
193
194
195
196
197
198



199
200
201
202
203
204

205
206
207
208
209
210
211
212
213
214
215



216
217
218
219
220
221
222
223
224
225
226
191
192
193
194
195
196
197

198
199
200
201
202
203
204
205
206
207

208
209
210
211
212
213
214
215

216
217
218
219
220




221
222
223

224
225
226
227

228
229
230
231
232
233
234
235




236
237
238

239
240
241
242
243
244
245
246
247
248







-
+









-
+







-
+




-
-
-
-
+
+
+
-




-
+







-
-
-
-
+
+
+
-










</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>

<a name="exceptions"></a><h2>3.0 Exceptions</h2>
<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>

<a name="rollhash"></a><h2>4.0 The rolling hash</h2>
<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>

<a name="rhdef"></a><h3>4.1 Definition</h3>
<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>

<p align=center><table><tr><td>
<p><img src="encode1.gif" align="center"></p>
<p><img src="encode2.gif" align="center"></p>
<p><img src="encode3.gif" align="center"></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>
</td></tr></table></p>

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.

<a name="rhincr"></a><h3>4.2 Incremental recalculation</h3>
<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>

<p align=center><table><tr><td>
<p><img src="encode6.gif" align="center"></p>
<p><img src="encode7.gif" align="center"></p>
<p><img src="encode8.gif" align="center"></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>
</td></tr></table></p>

<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
1


2
3

4
5

6
7

8

9
10


11
12
13
14
15
16
17










18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46


47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71


72
73
74

75
76

77
78


79
80
81
82
83
84
85

86
87

88
89

90
91
92

93
94


95
96
97
98
99
100
101

102
103
104

105
106

107
108
109

110
111

112
113

114
115




116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145

146
147

148
149

150
151

152
153


154
155
156
157
158
159
160
161
162



163
164
165
166

167
168

169
170
171

172
173
174
175
176
177
178
179
180



181
182
183
184
185

186
187

188
189
190

191
192
193
194

195



196
197
198
199
200
201
202
203
204
205

206
207

208
209

210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228

229
230

231
232

233
234
235

236
237

238
239

240
241










242
243
244
245
246
247
248
249
250
251
252
253
254
255
256

257
258

259
260
261
262
263
264
265
266

-
-
+

-
+

-
+

-

-
+

-
-
+
+
+

+
+
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
-
-
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+

-
+

-
+

-
-
+
+
+
+
+
+

-
+

-
+

-
+


-
+

-
-
+
+
+
+
+
+

-
+


-
+

-
+


-
+

-
+

-
+

-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



-
+

-
+

-
+

-
+

-
-
+
+

+
+
+
+
+
+
-
-
-
+
+
+

-
+

-
+


-
+

+
+
+
+
+
+
+
-
-
-
+
+
+
+

-
+

-



-
+
+
+

-
+
-
-
-
+
+

+
+
+
+
+
+
+
-
+

-
+

-
+


















-
+

-
+

-
+


-
+

-
+

-
+

-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+





-
+

-
+







<title>Fossil Delta Format</title>
<nowiki>
<h2>Abstract</h2>
<h1>1.0 Overview</h1>

<p>Fossil achieves efficient storage and low-bandwidth synchronization
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
or transmitting the complete content of an artifact, Fossil stores or
transmits only the changes relative to a related artifact.
</p>

<p>This document describes the delta-encoding format used by fossil.
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.</p>
<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.
<p>Note that the delta-encoding is not a fundamental element of the
state of a fossil repository.  A state of a fossil repository is
defined by the uncompressed and undeltaed content of all artifacts.
The fact the artifacts
are stored on disk using this delta-encoding format is merely an
optimization.  One could, in theory, create an entirely new and
compatible implementation of fossil that used a different delta-encoding
or did no delta-encoding at all.  However, experience has shown that
the delta-encoding described here is both efficient to compute and
results in very small deltas, so its continued use is recommended.</p>

<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] &rarr; Run self-tests of
       the delta logic

   *   [/help?cmd=test-delta-create|fossil test-delta-create X Y] &rarr; compute
       a delta that converts file X into file Y.  Output that delta.

   *   [/help?cmd=test-delta-apply|fossil test-delta-apply X D] &rarr; apply
       delta D to input file X and output the result.

   *   [/help?cmd=test-delta-analyze|fossil test-delta-analyze X Y] &rarr; 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:
<a name="structure"></a><h2>1.0 Structure</h2>
<img src="delta1.gif" align="left" hspace="10">

   *   <b>delta_create(</b><i>X</i><b>,</b><i>Y</i><b>)</b> &rarr;
       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> &rarr;
       Apply delta D to input blob X return a new blob which is the result.


   *   <b>delta_output_size(</b><i>D</i>)</b> &rarr;
       Return the size of the output that would result from applying delta D.

   *   <b>delta_parse(</b><i>D</i>)</b> &rarr; 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>

<p>A delta consists of three parts, a "header", a "trailer", and a
"segment-list" between them.</p>
A delta consists of three parts, a "header", a "trailer", and a
"segment-list" between them.

<p>Both header and trailer provide information about the target
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.</p>
be constructed from the original.

<a name="header"></a><h3>1.1 Header</h3>
<img src="delta6.gif" align="left" hspace="10">
<h2 id="header">2.1 Header</h2>
<verbatim type="pikchr">
    leftmargin = 0.1
    box height 50% "Size"
    box same "\"\\n\""
</verbatim>

<p>The header consists of a single number followed by a newline
The header consists of a single number followed by a newline
character (ASCII 0x0a). The number is the length of the target in
bytes.</p>
bytes.

<p>This means that, given a delta, the decoder can compute the size of
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.</p>
there. In other words, before it has to decode everything else.

<a name="trailer"></a><h3>1.2 Trailer</h3>
<img src="delta5.gif" align="left" hspace="10">
<h2 id="trailer">2.2 Trailer</h2>
<verbatim type="pikchr">
    leftmargin = 0.1
    box height 50% "Checksum"
    box same "\";\""
</verbatim>

<p>The trailer consists of a single number followed by a semicolon (ASCII
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.</p>
target from the original.

<p>The checksum is computed by treating the target as a series of
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.</p>
0-bytes (ASCII 0x00) at the end.

<p>By putting this information at the end of the delta a decoder has
By putting this information at the end of the delta a decoder has
it available immediately after the target has been reconstructed
fully.</p>
fully.

<a name="slist"></a><h3>1.3 Segment-List</h3>
<img src="delta2.gif" align="left" hspace="10">

<p>The segment-list of a delta describes how to create the target from
<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.</p>
original and target in small copy instructions.

<p>The target is constructed from beginning to end, with the data
The target is constructed from beginning to end, with the data
generated by each instruction appended after the data of all previous
instructions, with no gaps.</p>
instructions, with no gaps.

<a name="insertlit"></a><h4>1.3.1 Insert Literal</h4>
<h3 id="insertlit">2.3.1 Insert Literal</h3>

<p>A literal is specified by two elements, the size of the literal in
bytes, and the bytes of the literal itself.</p>
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>
<img src="delta4.gif" align="left" hspace="10">
<p>The length is written first, followed by a colon character (ASCII
0x3a), followed by the bytes of the literal.</p>

The length is written first, followed by a colon character (ASCII
0x3a), followed by the bytes of the literal.

<a name="copyrange"></a><h4>1.3.2 Copy Range</h4>
<h3 id="copyrange">2.3.2 Copy Range</h3>

<p>A range to copy is specified by two numbers, the offset of the
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.</p>
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>
<img src="delta3.gif" align="left" hspace="10">
<p>The length is written first, followed by an "at" character (ASCII
0x40), then the offset, followed by a comma (ASCII 0x2c).</p>


The length is written first, followed by an "at" character (ASCII
0x40), then the offset, followed by a comma (ASCII 0x2c).

<a name="intcoding"></a><h2>2.0 Encoding of integers</h2>
<h1 id="intcoding">3.0 Encoding of integers</h1>

<p>
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").
</p>

The base-64 encoding uses one character for each 6 bits of
the integer to be encoded.  The encoding characters are:

<p>
<pre>
The base-64 coding is described in
<a href="http://www.ietf.org/rfc/rfc3548.txt">RFC 3548</a>.
</p>
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.

<a name="examples"></a><h2>3.0 Examples</h2>
<h1 id="examples">4.0 Examples</h1>

<a name="examplesint"></a><h3>3.1 Integer encoding</h3>
<h2 id="examplesint">4.1 Integer encoding</h2>

<table border=1>
<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>

<a name="examplesdelta"></a><h3>3.2 Delta encoding</h3>
<h2 id="examplesdelta">4.2 Delta encoding</h2>

<p>An example of a delta using the specified encoding is:</p>
An example of a delta using the specified encoding is:

<table border=1><tr><td><pre>
<pre>
1Xb
4E@0,2:thFN@4C,6:scenda1B@Jd,6:scenda5x@Kt,6:pieces79@Qt,F: Example: eskil~E@Y0,2zMM3E;</pre>
</td></tr></table>
</pre>

<p>This can be taken apart into the following parts:</p>
This can be taken apart into the following parts:

<table border=1>
<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>&nbsp;</td> <td>2:th	         </td><td>Literal </td><td> 2 'th'	     </td></tr>
<tr><td>&nbsp;</td> <td>FN@4C,	         </td><td>Copy    </td><td> 983 @ 268	     </td></tr>
<tr><td>&nbsp;</td> <td>6:scenda         </td><td>Literal </td><td> 6 'scenda'	     </td></tr>
<tr><td>&nbsp;</td> <td>1B@Jd,	         </td><td>Copy    </td><td> 75 @ 1256	     </td></tr>
<tr><td>&nbsp;</td> <td>6:scenda         </td><td>Literal </td><td> 6 'scenda'	     </td></tr>
<tr><td>&nbsp;</td> <td>5x@Kt,	         </td><td>Copy    </td><td> 380 @ 1336	     </td></tr>
<tr><td>&nbsp;</td> <td>6:pieces	 </td><td>Literal </td><td> 6 'pieces'	     </td></tr>
<tr><td>&nbsp;</td> <td>79@Qt,	         </td><td>Copy    </td><td> 457 @ 1720     </td></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>&nbsp;</td> <td>2:th             </td><td>Literal </td><td> 2 'th'       </td></tr>
<tr><td>&nbsp;</td> <td>FN@4C,           </td><td>Copy    </td><td> 983 @ 268        </td></tr>
<tr><td>&nbsp;</td> <td>6:scenda         </td><td>Literal </td><td> 6 'scenda'       </td></tr>
<tr><td>&nbsp;</td> <td>1B@Jd,           </td><td>Copy    </td><td> 75 @ 1256        </td></tr>
<tr><td>&nbsp;</td> <td>6:scenda         </td><td>Literal </td><td> 6 'scenda'       </td></tr>
<tr><td>&nbsp;</td> <td>5x@Kt,           </td><td>Copy    </td><td> 380 @ 1336       </td></tr>
<tr><td>&nbsp;</td> <td>6:pieces     </td><td>Literal </td><td> 6 'pieces'       </td></tr>
<tr><td>&nbsp;</td> <td>79@Qt,           </td><td>Copy    </td><td> 457 @ 1720     </td></tr>
<tr><td>&nbsp;</td> <td>F: Example: eskil</td><td>Literal </td><td> 15 ' Example: eskil'</td></tr>
<tr><td>&nbsp;</td> <td>~E@Y0,           </td><td>Copy    </td><td>  4046 @ 2176        </td></tr>
<tr><td>Trailer</td><td>2zMM3E           </td><td>Checksum</td><td> -1101438770         </td></tr>
</table>

<p>The unified diff behind the above delta is</p>
The unified diff behind the above delta is

<table border=1><tr><td><pre>
<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
199
200
201
202
203
204
205
206

207
208
209
210
211

212
213
214
215
216
217
218
219
220
221
222
223
293
294
295
296
297
298
299

300

301
302
303

304
305
306
307
308
309
310
311
312
313
314
315
316







-
+
-



-
+












     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>
</pre></td></tr></table>



<a name="notes"></a><h2>Notes</h2>
<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
9
1

2
3
4
5
6
7
8

-







<title>Project Documentation</title>
<h1 align="center">Project Documentation</h1>

Fossil provides a built-in <a href="wikitheory.wiki">wiki</a>
that can be used to store the
documentation for a project.  This is sufficient for many projects.
If your project is well-served by wiki documentation, then you
need read no further.

22
23
24
25
26
27
28
29

30
31
32
33
34
35

36
37

38
39
40
41

42
43

44
45
46
47
48
49
50
51
52
53









54
55

56
57
58
59
60
61























62
63
64
65
66
67
68
21
22
23
24
25
26
27

28
29
30
31
32
33

34
35

36
37
38
39

40


41
42
43
44







45
46
47
48
49
50
51
52
53


54
55





56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85







-
+





-
+

-
+



-
+
-
-
+



-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-
+

-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







  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".

<h2>Fossil Support For Embedded Documentation</h2>
<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:

<blockquote>
<pre>
<i>&lt;baseurl&gt;</i><big><b>/doc/</b></big><i>&lt;version&gt;</i><big><b>/</b></big><i>&lt;filename&gt;</i>
</blockquote>
</pre>

The <i>&lt;baseurl&gt;</i> is the main URL used to access the fossil web server.
For example, the <i>&lt;baseurl&gt;</i> for the fossil project itself is
either <b>http://www.fossil-scm.org/fossil</b> or
[https://fossil-scm.org/home].
<b>http://www.hwaci.com/cgi-bin/fossil</b>.
If you launch the web server using the "<b>fossil server</b>" command line,
If you launch the web server using the "[/help?cmd=ui|fossil ui]" command line,
then the <i>&lt;baseurl&gt;</i> is usually
<b>http://localhost:8080/</b>.

The <i>&lt;version&gt;</i> is any unique prefix of the check-in ID for
the check-in containing the documentation you want to access.
Or <i>&lt;version&gt;</i> can be the name of a
[./branching.wiki | branch] in order to show
the documentation for the latest version of that branch.
Or <i>&lt;version&gt;</i> can be one of the keywords "<b>tip</b>" or
"<b>ckout</b>".  The "<b>tip</b>" keyword means to use the most recent
The <i>&lt;version&gt;</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">&lt;version&gt;</i> can
also be the special identifier "<b>ckout</b>".
check-in.  This is useful if you want to see the very latest
version of the documentation.  The "<b>ckout</b>" keywords means to
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 normally
only works when you start your server using the "<b>fossil server</b>"
or "<b>fossil ui</b>"
command line and is intended to show what the documentation you are currently
editing looks like before you check it in.
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>&lt;filename&gt;</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
78
79
80
81
82
83
84


85
86
87
88
89
90
91
92
93
94
95
96
97


















































































98
99

100
101
102
103
104
105
106
107

108
109

110
111
112
113
114
115





116
117

118
119
120

121
122
123


124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140


141
142
143
144
145
146
147
148
149

150
151

152
153
154
155
156
157
158
159
160
161
162
163
164
165
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196

197
198
199
200
201
202
203
204

205
206

207
208





209
210
211
212
213
214

215
216
217

218
219


220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236


237
238
239
240
241
242
243
244
245


246


247

248
249
250
251
252
253
254
255
256
257
258
259
260







+
+










-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+







-
+

-
+

-
-
-
-
-
+
+
+
+
+

-
+


-
+

-
-
+
+















-
-
+
+







-
-
+
-
-
+
-













[/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 &lt;div&gt; element like this:

    <b>&lt;div class='fossil-doc' data-title='<i>Title Text</i>'&gt;</b>

Then the standard Fossil header and footer are added to the document
prior to being displayed.  The "class='fossil-doc'" attribute is
required for this to occur.  The "data-title='...'" attribute is
optional, but if it is present the text will become the title displayed
in the Fossil header.  An example of this can be seen in the text
of the [/artifact/84b4b3d041d93a?txt=1 | Index Of Fossil Documentation]
document.
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>&lt;script&gt;</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 "&lt;baseurl&gt;" 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.


<h2>Examples</h2>
<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:

   [http://www.fossil-scm.org/index.html/doc/trunk/www/embeddeddoc.wiki].
<pre>[https://fossil-scm.org/home/doc/trunk/www/embeddeddoc.wiki]</pre>

The first part of this path, the "[http://www.fossil-scm.org/index.html]",
The first part of this path, the "[https://fossil-scm.org/home]",
is the base URL.  You might have originally typed:
[http://www.fossil-scm.org/].  The web server at the www.fossil-scm.org
site automatically redirects such links by appending "index.html".  The
"index.html" file on www.fossil-scm.org is really a CGI script
(do not be mislead by the name) which runs the fossil web service in
CGI mode.  The "index.html" CGI script looks like this:
[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:

<blockquote><pre>
<pre>
#!/usr/bin/fossil
repository: /fossil/fossil.fossil
</pre></blockquote>
</pre>

This is one of four ways to set up a
<a href="./server.wiki">fossil web server</a>.
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</i><b>T</b><i>HH:MM</i>
<li> <i>YYYY-MM-DD</i><b>T</b><i>HH:MM:SS</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:

<blockquote>
<a href="http://www.fossil-scm.org/index.html/doc/2010-01-01/www/index.wiki">
<pre><a href="/doc/2010-01-01/www/index.wiki">https://fossil-scm.org/home/doc/<b>2010-01-01</b>/www/index.wiki
http://www.fossil-scm.org/index.html/doc/<b>2010-01-01</b>/www/index.wiki
</a>
</a></pre>
</blockquote>

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

Changes to 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
1
2

3
4
5
6
7


8
9
10
11


12
13
14
15
16

17
18

19
20

21
22
23
24

25
26


27
28
29
30
31

32
33



34
35
36
37
38
39
40

41
42
43
44
45
46

47
48
49


50
51
52
53
54

55
56

57
58
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><blockquote>
<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.
</blockquote>
<h2>Building An Encryption-Enabled Fossil</h2><blockquote>

<h2>Building An Encryption-Enabled Fossil</h2>

The SQLite Encryption Extension (SEE) is proprietary software and requires
[http://www.hwaci.com/cgi-bin/see-step1|purchasing a license].
<p>
[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 src/ subfolder of the Fossil sources, right beside
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 src/ subfolder
"shell.c" file, renamed as "shell-see.c", and place it in the extsrc/ subfolder
beside the original "shell.c".
<p>

Add the --with-see command-line option to the configuration script to enable
the use of SEE on unix-like systems.

<blockquote><pre>
<pre>
./configure --with-see; make
</pre></blockquote>
<p>To build for Windows using MSVC, add
</pre>

To build for Windows using MSVC, add
the "USE_SEE=1" argument to the "nmake" command line.

<blockquote><pre>
<pre>
nmake -f makefile.msc USE_SEE=1
</pre></blockquote>
</blockquote>
<h2>Using Encrypted Repositories</h2><blockquote>
</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.
<p>

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.
<p>

On Windows, the "fossil server", "fossil ui", and "fossil shell" commands do not
(currently) work on an encrypted repository.
</blockquote>
<h2>Additional Security</h2><blockquote>

<h2>Additional Security</h2>

Use the FOSSIL_SECURITY_LEVEL environment for additional protection.

<blockquote><pre>
<pre>
export FOSSIL_SECURITY_LEVEL=1
</pre></blockquote>
</pre>

A setting of 1 or greater
prevents fossil from trying to remember the previous sync password.

<blockquote><pre>
<pre>
export FOSSIL_SECURITY_LEVEL=2
</pre></blockquote>
</pre>

A setting of 2 or greater
causes all password prompts to be preceeded by a random translation matrix similar
causes all password prompts to be preceded by a random translation matrix similar
to the following:

<blockquote><pre>
<pre>
abcde fghij klmno pqrst uvwyz
qresw gjymu dpcoa fhkzv inlbt
</pre></blockquote>
</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.
</blockquote>

Changes to www/env-opts.md.

24
25
26
27
28
29
30


31
32
33
34
35
36
37
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39







+
+







`--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
53
54
55
56
57
58
59



60
61
62
63
64
65
66
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71







+
+
+







  * _8_ &mdash; Attempts to break lines on word boundaries while honoring the
        logical line length.

  * _16_ &mdash; 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
107
108
109
110
111
112
113
114
115
116



117
118
119

120
121
122
123
124
125
126
112
113
114
115
116
117
118



119
120
121



122
123
124
125
126
127
128
129







-
-
-
+
+
+
-
-
-
+








`--vfs VFSNAME`: Load the named VFS into SQLite.


Environment Variables
---------------------


`APPDATA`: (Windows) Location of the `~/.fossil` file. The first
environment variable found in the environment from the list
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
`FOSSIL_HOME`, `LOCALAPPDATA` (Windows), `APPDATA` (Windows),
`HOMEDRIVE` and `HOMEPATH` (Windows, used together), and `HOME` is
used as the location of the `~/.fossil` file.
[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
135
136
137
138
139
140
141
142

143
144
145
146
147


148
149
150

151
152
153
154
155
156
157
158
159












160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186

187
188
189
190

191
192
193
194
195
196
197
138
139
140
141
142
143
144

145





146
147
148
149

150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176





177
178
179
180
181





182
183
184
185
186
187

188




189
190
191
192
193
194
195
196







-
+
-
-
-
-
-
+
+


-
+









+
+
+
+
+
+
+
+
+
+
+
+





-
-
-
-
-





-
-
-
-
-






-
+
-
-
-
-
+







`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 the `~/.fossil` file. The first environment
`FOSSIL_HOME`: Location of [configuration database][configdb].
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.

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 actaul encryption key.  This has no
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_ &mdash; Do not remember passwords.

  * _≥2_ &mdash; 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_TEMP`: Fallback location of the temporary directories and files
created and deleted when running the test suite. The first environment
variable found in the environment from the list `FOSSIL_TEST_TEMP`,
`FOSSIL_TEMP`, `TEMP`, and `TMP` is used.

`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_TEST_TEMP`: Primary location of the temporary directories
and files created and deleted when running the test suite. The
first environment variable found in the environment from the list
`FOSSIL_TEST_TEMP`, `FOSSIL_TEMP`, `TEMP`, and `TMP` is used.

`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`: Location of the `~/.fossil` file. The first environment
`HOME`: Potential location of the [configuration database][configdb].
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.
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.

230
231
232
233
234
235
236



237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250






251
252
253
254
255
256
257







+
+
+












-
-
-
-
-
-







`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.

`TEMP`: On Windows, the location of temporary files. The first
environment variable found in the environment that names an existing
directory from the list `TMP`, `TEMP`, `USERPROFILE`, the Windows
directory (usually `C:\WINDOWS`), `TEMP`, `TMP`, and the current
directory (aka `.`) is the temporary folder.

`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.

279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
275
276
277
278
279
280
281






282
283
284
285
286
287
288
289
290
291
292






293
294
295
296
297
298
299







-
-
-
-
-
-











-
-
-
-
-
-







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.

`TMP`: On Windows, the location of temporary files. The first
environment variable found in the environment that names an existing
directory from the list `TMP`, `TEMP`, `USERPROFILE`, the Windows
directory (usually `C:\WINDOWS`), `TEMP`, `TMP`, and the current
directory (aka `.`) is the temporary folder.

`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.

`USERPROFILE`: On Windows, the location of temporary files. The first
environment variable found in the environment that names an existing
directory from the list `TMP`, `TEMP`, `USERPROFILE`, the Windows
directory (usually `C:\WINDOWS`), `TEMP`, `TMP`, and the current
directory (aka `.`) is the temporary folder.

`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
404
405
406
407
408
409
410
411

412
413
414
415




416
417
418
419
420




421
422
423
424
425



426
427

428


429
430
431
432
433
434
435
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
388
389
390
391
392
393
394

395
396



397
398
399
400
401




402
403
404
405
406




407
408
409

410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426

427
428

429
430
431
432







433
434
435
436
437
438
439
440
441
442
443
444
445
446
447


448




449
450
451


452
453
454
455
456
457
458
459







-
+

-
-
-
+
+
+
+

-
-
-
-
+
+
+
+

-
-
-
-
+
+
+
-

+

+
+












-
+

-
+



-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
+
-
-
-
-
+
+
+
-
-
+







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".


### Home Directory
### Configuration Database Location

Fossil keeps some information interesting to each user in the user's
home directory. This includes the global settings and the list of
repositories and checkouts used by `fossil all`.
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 user's home directory is specified by 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`.
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:

SQLite has its own notion of the user's home directory, which is only
exposed if the interactive SQL shell is run with the "fossil
sqlite3" command. Being a separate library, SQLite uses many of the
same variables to find the home directory, but uses them in a
  *  Traditional unix &rarr; "`$HOME/.fossil`"
  *  Windows &rarr; "`%LOCALAPPDATA%/_fossil`"
  *  [XDG-unix][xdg] &rarr; "`$HOME/.config/fossil.db`"
different order, and does not use the `FOSSIL_HOME` variable at all.

[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.


### Temporary File Location
### <a id="temp"></a>Temporary File Location

Fossil places some temporary files in the current directory, notably
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 home. On Unix-like systems, the first
folder from the hard coded list `/var/tmp`, `/usr/tmp`, `/tmp`,
`/temp`, and `.` that is found to exist in the file system is used by
fossil. The SQLite library has its own code for finding a safe place for
temporary files. It checks the environment variables `SQLITE_TMPDIR`
and `TMPDIR` ahead of the hard coded list `/var/tmp`, `/usr/tmp`,
`/tmp`, and `.` for the first directory that exists.
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.

On Windows, fossil calls [`GetTempPath`][gtp], and also queries the
environment variables `TEMP`, and `TMP`. If none of those three places
For Cygwin builds, SQLite instead uses the first defined variable in
exist, then it uses `.`. Notice that `GetTempPath` itself used `TMP`,
`TEMP`, `USERPROFILE`, and the Windows folder (named in the variable
`SystemRoot`). Since the Windows folder always exists, but in modern
versions of Windows is generally *not* writable by the logged in user,
this list: `SQLITE_TMPDIR`, `TMPDIR`, `TMP`, `TEMP`, and `USERPROFILE`.

For native Windows builds, SQLite simply calls the OS’s [`GetTempPath()`
not having `TEMP`, `TMP`, or `USERPROFILE` set is almost guaranteed to
cause trouble.
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.
489
490
491
492
493
494
495



479
480
481
482
483
484
485
486
487
488







+
+
+
`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

Changes to www/event.wiki.

29
30
31
32
33
34
35
36


37
38

39
40
41
42
43
44
45
46






47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

64
65
66
67






































68
69
70
71
72
73
74
75
29
30
31
32
33
34
35

36
37
38

39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69

70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120







-
+
+

-
+








+
+
+
+
+
+
















-
+




+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+








     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.
     ran it recorded in the wiki content. The technote can be added
     from a script.

  *  <b>News Articles</b>.  Significant occurrences in the lifecycle of
  *  <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 And Editing Technotes</h2>
<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.

10
11
12
13
14
15
16
17

18
19

20
21
22
23
24
25
26
10
11
12
13
14
15
16

17
18

19
20
21
22
23
24
25
26







-
+

-
+








faq {
  What GUIs are available for fossil?
} {
  The fossil executable comes with a [./webui.wiki | web-based GUI] built in.
  Just run:

  <blockquote>
  <pre>
  <b>fossil [/help/ui|ui]</b> <i>REPOSITORY-FILENAME</i>
  </blockquote>
  </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 {
40
41
42
43
44
45
46
47

48
49

50
51
52
53
54
55
56
40
41
42
43
44
45
46

47
48

49
50
51
52
53
54
55
56







-
+

-
+







  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:

  <blockquote>
  <pre>
  <b>fossil [/help/branch|branch] new</b> <i>BRANCH-NAME BASIS</i>
  </blockquote>
  </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.
73
74
75
76
77
78
79
80

81
82

83
84
85
86
87
88
89
73
74
75
76
77
78
79

80
81

82
83
84
85
86
87
88
89







-
+

-
+







  "--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:

  <blockquote>
  <pre>
  <b>fossil [/help/branch|tag] add</b> <i>TAGNAME</i> <i>CHECK-IN</i>
  </blockquote>
  </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
125
126
127
128
129
130
131

132
133
134
135
136






137
138

139

140
141


142
143

144

145
146

147
148
149
150
151
152
153
154
155
156
157
158
159
160

161
162

163
164
165
166
167
168
169
170
171
172
173

174
175
176

177
178
125
126
127
128
129
130
131
132





133
134
135
136
137
138
139
140
141

142
143

144
145
146
147
148

149
150

151
152
153
154
155
156
157
158
159
160
161
162
163
164

165


166
167
168
169
170
171
172
173
174
175


176
177
178

179
180
181







+
-
-
-
-
-
+
+
+
+
+
+


+
-
+

-
+
+


+
-
+

-
+













-
+
-
-
+









-
-
+


-
+


  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:

  <blockquote><pre>
  fossil [/help/clone|clone]  http://www.fossil-scm.org/  fossil.fossil
  fossil [/help/clone|clone]  http://www2.fossil-scm.org/  fossil.fossil
  fossil [/help/clone|clone]  http://www3.fossil-scm.org/site.cgi  fossil.fossil
  </pre></blockquote>
  <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:

  <blockquote><pre>
  <pre>
  mkdir src; cd src; fossil [/help/open|open] ../fossil.fossil
  </pre></blockquote>
  </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:

  <blockquote><pre>
  <pre>
  fossil [/help/update|update]
  </pre></blockquote>
  </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>"
puts "<title>Fossil FAQ</title>\n"
puts "<h1 align=\"center\">Frequently Asked Questions</h1>\n"
puts "<p>Note: See also <a href=\"qandc.wiki\">Questions and Criticisms</a>.\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 "<a name=\"q$i\"></a>"
  puts "<p><b>($i) [lindex $faq($i) 0]</b></p>\n"
  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 "<blockquote>$body</blockquote></li>\n"
  puts "$body</li>\n"
}
puts {</ol>}

Changes to www/faq.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
1

2

3
4
5
6
7
8
9
10
11
12
13
14
15
16


17
18

19
20
21

22
23

24
25
26
27

28
29





30
31

32
33

34
35


36
37

38
39
40
41
42
43
44
45
46

47
48

49
50
51
52
53
54
55
56
57
58
59
60
61
62

63
64


65
66

67
68
69
70
71
72
73
74
75
76
77

78
79

80
81
82
83
84
85
86
87
88
89
90

91
92


93
94
95

96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114

115
116


117
118

119
120


121
122






123
124
125
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>
<h1 align="center">Frequently Asked Questions</h1>

<p>Note: See also <a href="qandc.wiki">Questions and Criticisms</a>.
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>
<a name="q1"></a>
<p><b>(1) What GUIs are available for fossil?</b></p>
<p id="q1"><b>(1) What GUIs are available for fossil?</b></p>

<blockquote>The fossil executable comes with a [./webui.wiki | web-based GUI] built in.
The fossil executable comes with a [./webui.wiki | web-based GUI] built in.
Just run:

<blockquote>
<pre>
<b>fossil [/help/ui|ui]</b> <i>REPOSITORY-FILENAME</i>
</blockquote>
</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.)
if you are within an open check-out.)</li>

See also: [http://fuelscm.org/]
</blockquote></li>

<a name="q2"></a>
<p><b>(2) What is the difference between a "branch" and a "fork"?</b></p>
<p id="q2"><b>(2) What is the difference between a "branch" and a "fork"?</b></p>

<blockquote>This is a big question - too big to answer in a FAQ.  Please
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.</blockquote></li>
and Tagging</a> document.</li>

<a name="q3"></a>
<p><b>(3) How do I create a new branch?</b></p>
<p id="q3"><b>(3) How do I create a new branch?</b></p>

<blockquote>There are lots of ways:
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:

<blockquote>
<pre>
<b>fossil [/help/branch|branch] new</b> <i>BRANCH-NAME BASIS</i>
</blockquote>
</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.</blockquote></li>
button.</li>

<a name="q4"></a>
<p><b>(4) How do I tag a check-in?</b></p>
<p id="q4"><b>(4) How do I tag a check-in?</b></p>

<blockquote>There are several ways:
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:

<blockquote>
<pre>
<b>fossil [/help/branch|tag] add</b> <i>TAGNAME</i> <i>CHECK-IN</i>
</blockquote>
</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.</blockquote></li>
tags to be removed.</li>

<a name="q5"></a>
<p><b>(5) How do I create a private branch that won't get pushed back to the
<p id="q5"><b>(5) How do I create a private branch that won't get pushed back to the
  main repository.</b></p>

<blockquote>Use the <b>--private</b> command-line option on the
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]</blockquote></li>
[./private.wiki | Additional information]</li>

<a name="q6"></a>
<p><b>(6) How can I delete inappropriate content from my fossil repository?</b></p>
<p id="q6"><b>(6) How can I delete inappropriate content from my fossil repository?</b></p>

<blockquote>See the article on [./shunning.wiki | "shunning"] for details.</blockquote></li>
See the article on [./shunning.wiki | "shunning"] for details.</li>

<a name="q7"></a>
<p><b>(7) How do I make a clone of the fossil self-hosting repository?</b></p>
<p id="q7"><b>(7) How do I make a clone of the fossil self-hosting repository?</b></p>

<blockquote>Any of the following commands should work:
<blockquote><pre>
fossil [/help/clone|clone]  http://www.fossil-scm.org/  fossil.fossil
fossil [/help/clone|clone]  http://www2.fossil-scm.org/  fossil.fossil
fossil [/help/clone|clone]  http://www3.fossil-scm.org/site.cgi  fossil.fossil
</pre></blockquote>
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:

<blockquote><pre>
<pre>
mkdir src; cd src; fossil [/help/open|open] ../fossil.fossil
</pre></blockquote>
</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:

<blockquote><pre>
<pre>
fossil [/help/update|update]
</pre></blockquote></blockquote></li>
</pre></li>

<a name="q8"></a>
<p><b>(8) How do I import or export content from and to other version control systems?</b></p>
<p id="q8"><b>(8) How do I import or export content from and to other version control systems?</b></p>

<blockquote>Please see [./inout.wiki | Import And Export]</blockquote></li>
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
2
3
4
5
6
7
8
9
10
11
1



2
3
4
5
6
7
8

-
-
-







<title>Fossil File Formats</title>
<h1 align="center">
Fossil File Formats
</h1>

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
30
31
32
33
34
35
36
37
38

39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81

82
83
84
85
86
87
88
89
90


91

92
93
94
95
96
97
98
99
100
101
102
103
104

105
106
107
108
109
110
111
112
113
114
115
116

117
118
119
120
121
122
123
124
125
126
127
128

129
130
131
132
133
134
135
27
28
29
30
31
32
33


34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54


55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75

76
77
78
79
80
81
82
83
84
85
86
87

88
89
90
91
92
93
94
95
96
97
98
99


100
101
102
103
104
105
106
107
108
109
110
111

112
113
114
115
116
117
118
119
120
121
122
123

124
125
126
127
128
129
130
131







-
-
+




















-
-
+




















-
+









+
+
-
+











-
-
+











-
+











-
+







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.

<a name="names"></a>
<h2>1.0 Artifact Names</h2>
<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.

<a name="structural"></a>
<h2>2.0 Structural Artifacts</h2>
<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 ASCII text.  The artifact may be PGP clearsigned.
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]).
lexicographical order.  There may not be any duplicate cards.
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.

<a name="manifest"></a>
<h3>2.1 The Manifest</h3>
<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:

<blockquote>
<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>
</blockquote>
</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.
147
148
149
150
151
152
153
154
155


156
157
158
159
160
161
162
163
164
143
144
145
146
147
148
149


150
151


152
153
154
155
156
157
158







-
-
+
+
-
-







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:

<blockquote>
<i>YYYY</i><b>-</b><i>MM</i><b>-</b><i>DD</i><b>T</b><i>HH</i><b>:</b><i>MM</i><b>:</b><i>SS</i><br>
<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>
<i>YYYY</i><b>-</b><i>MM</i><b>-</b><i>DD</i><b>T</b><i>HH</i><b>:</b><i>MM</i><b>:</b><i>SS</i><b>.</b><i>SSS</i>
</blockquote>

A manifest has zero or more <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
252
253
254
255
256
257
258
259
260

261
262
263
264
265
266
267
268
269
270

271
272
273

274
275
276
277
278
279
280
281
282
283
284
285
286

287
288
289
290
291
292

293
294
295
296
297

298
299
300
301
302
303
304
305
306
307
308
309
310

311
312
313
314
315
316
317
246
247
248
249
250
251
252


253
254
255
256
257
258
259
260
261
262

263
264
265

266
267
268
269
270
271
272
273
274
275
276
277


278
279
280
281
282
283

284
285
286
287
288

289
290
291
292
293
294
295
296
297
298
299
300
301

302
303
304
305
306
307
308
309







-
-
+









-
+


-
+











-
-
+





-
+




-
+












-
+







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].

<a name="cluster"></a>
<h3>2.2 Clusters</h3>
<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:

<blockquote>
<div class="indent">
<b>M</b> <i>artifact-id</i><br />
<b>Z</b> <i>checksum</i>
</blockquote>
</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].

<a name="ctrl"></a>
<h3>2.3 Control Artifacts</h3>
<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:

<blockquote>
<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 />
</blockquote>
</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 40 character lowercase artifact ID of the artifact
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
329
330
331
332
333
334
335
336

337
338
339
340

341
342
343
344
345
346
347


348
349
350
351
352
353
354
355

356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371







372
373
374
375
376

377
378
379
380
381

382
383
384
385
386
387

388
389
390
391
392
393
394
321
322
323
324
325
326
327

328
329
330


331
332
333
334
335
336
337

338
339
340
341
342
343
344
345
346

347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373


374
375
376
377
378

379
380
381
382
383
384

385
386
387
388
389
390
391
392







-
+


-
-
+






-
+
+







-
+
















+
+
+
+
+
+
+



-
-
+




-
+





-
+







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 artifacts can be seen [/info/9d302ccda8 | here].
An example control artifact can be seen [/info/9d302ccda8 | here].


<a name="wikichng"></a>
<h3>2.4 Wiki Pages</h3>
<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:

<blockquote>
<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>
</blockquote>
</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].

<a name="tktchng"></a>
<h3>2.5 Ticket Changes</h3>
<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:

<blockquote>
<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>
</blockquote>
</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:
414
415
416
417
418
419
420
421
422

423
424
425
426
427
428
429
430

431
432
433
434
435
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
412
413
414
415
416
417
418


419
420
421
422
423
424
425
426

427
428
429
430
431
432
433

434
435
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







-
-
+







-
+






-
+




-
+




















-
-
+








-
+









-
+







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].

<a name="attachment"></a>
<h3>2.6 Attachments</h3>
<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:

<blockquote>
<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>
</blockquote>
</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 40-character artifact
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.


<a name="event"></a>
<h3>2.7 Technical Notes</h3>
<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:

<blockquote>
<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>
</blockquote>
</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.
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
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







-
-
+

-
+




-
+









-
+


-
+







-
+








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.

<a name="forum"></a>
<h3>2.8 Forum Posts</h3>
<h3 id="forum">2.8 Forum Posts</h3>

Forum posts are intended as a mechanism for users and developers to 
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:

<blockquote>
<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>
</blockquote>
</div>

Every forum post must have either one <b>I</b> card and one <b>G</b> card
or one <b>H</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 G and <b>I</b> cards is the
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
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
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







-
-
+








-
+

-
+
-
-
-







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.


<a name="summary"></a>
<h2>3.0 Card Summary</h2>
<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 border=1 width="100%">
<table>
<tr>
<th rowspan=2 valign=bottom>Card Format</th>
<th>⇩ Card Format / Used By ⇨</th>
<th colspan=8>Used By</th>
</tr>
<tr>
<th>Manifest</th>
<th>Cluster</th>
<th>Control</th>
<th>Wiki</th>
<th>Ticket</th>
<th>Attachment</th>
<th>Technote</th>
651
652
653
654
655
656
657
658

659
660
661
662
663
664
665
642
643
644
645
646
647
648

649
650
651
652
653
654
655
656







-
+







<td>&nbsp;</td>
</tr>
<tr>
<td><b>C</b> <i>comment-text</i></td>
<td align=center><b>1</b></td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td align=center><b>0-1</b></td>
<td>&nbsp;</td>
<td align=center><b>0-1</b></td>
<td align=center><b>0-1</b></td>
<td>&nbsp;</td>
</tr>
<tr>
<td><b>D</b> <i>date-time-stamp</i></td>
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
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







-
+










-
+







<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td align=center><b>0-1</b></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>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td align=center><b>0-1</b></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>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
787
788
789
790
791
792
793
794

795
796
797
798
799
800
801
778
779
780
781
782
783
784

785
786
787
788
789
790
791
792







-
+







<td align=center><b>0-1</b></td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td align=center><b>0-1</b></td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td align=center><b>0-1</b></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>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
811
812
813
814
815
816
817
818

819
820
821

822
823
824
825

826
827
828
829
830
831
832
802
803
804
805
806
807
808

809
810
811

812
813
814
815

816
817
818
819
820
821
822
823







-
+


-
+



-
+







<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<tr>
<td><b>T</b> (<b>+</b>|<b>*</b>|<b>-</b>)<i>tagname</i> <i>uuid</i> ?<i>value</i>?</td>
<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>&nbsp;</td>
<td align=center><b>1+</b></td>
<td align=center><b>1+</b><sup><nowiki>[2]</nowiki></sup></td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td align=center><b>0+</b></td>
<td align=center><b>0+</b><sup><nowiki>[3]</nowiki></sup></td>
<td>&nbsp;</td>
</tr>
<tr>
<td><b>U</b> <i>username</i></td>
<td align=center><b>1</b></td>
<td>&nbsp;</td>
<td align=center><b>1</b></td>
856
857
858
859
860
861
862

863


864
865
















866
867
868
869






































870

871
872
873
874
875
876
877
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







+

+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+




+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+







<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.
<a name="addenda"></a>
<h2>4.0 Addenda</h2>

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.1 R-Card Hash Calculation</h3>
<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.

Changes to 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
1
2
3
4
5
6
7
8
9

10
11

12
13
14
15


16
17
18
19
20

21
22
23
24



25
26
27
28
29
30
31

32
33
34
35



36
37
38
39
40



41
42
43
44
45
46
47
48



49
50
51
52
53
54
55




56
57
58
59
60
61
62
63

64
65

66
67

68
69
70
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>

<p>This short document explains the main basic Fossil commands for a single
This short document explains the main basic Fossil commands for a single
user, i.e. with no additional users, with no need to synchronize with some remote
repository, and no need for branching/forking.</p>
repository, and no need for branching/forking.

<h2>Create a new repository</h2>

<p>fossil new c:\test.repo</p>
<p>This will create the new SQLite binary file that holds the repository, i.e.
<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.</p>
after they've been checked out of the repository.

<h2>Open the repository</h2>

<p>cd c:\temp\test.fossil</p>
<p>fossil open c:\test.repo</p>
<p>This will check out the last revision of all the files in the repository,
<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>).</p>
<tt>.fslckout</tt>).

<h2>Add new files</h2>

<p>fossil add .</p>
<p>To tell Fossil to add new files to the repository. The files aren't actually
added until you run &quot;commit&quot;. When using &quot;.&quot;, it tells Fossil
<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.</p>
<p>Note: To tell Fossil to ignore some extensions:</p>
<p>fossil settings ignore-glob &quot;*.o,*.obj,*.exe&quot; --global</p>
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>

<p>fossil delete myfile.c</p>
<p>This will simply remove the item from the list of files that were previously
added through &quot;fossil add&quot;.</p>
<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>

<p>fossil changes</p>
<p>This shows the list of changes that have been done and will be committed the
next time you run &quot;fossil commit&quot;. It's a useful command to run before
running &quot;fossil commit&quot; just to check that things are OK before proceeding.</p>
<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>

<p>To actually apply the pending changes to the repository, e.g. new files marked
To actually apply the pending changes to the repository, e.g. new files marked
for addition, checked-out files that have been edited and must be checked-in,
etc.</p>
etc.

<p>fossil commit -m "Added stuff"</p>
<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>

<p>If you wish to compare the last revision of a file and its checked out version
in your work directory:</p>
<p>fossil gdiff myfile.c</p>
<p>If you wish to compare two different revisions of a file in the repository:</p>
<p>fossil finfo myfile: Note the first hash, which is the UUID of the commit
when the file was committed</p>
<p>fossil gdiff --from UUID#1 --to UUID#2 myfile.c</p>
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>

<p>fossil revert myfile.c</p>
<p>Fossil does not prompt when reverting a file. It simply reminds the user about the
"undo" command, just in case the revert was a mistake.</p>
<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.


<h2>Close the repository</h2>
<p>fossil close</p>
<p>This will simply remove the _FOSSIL_ at the root of the work directory but
will not delete the files in the work directory. From then on, any use of &quot;fossil&quot;
will trigger an error since there is no longer any connection.</p>

Changes to 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
1
2
3
4

5
6
7
8
9
10

11




12
13
14
15
16
17
18
19
20
21




22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

41

42
43
44
45

46
47

48
49

50





51



52
53
54





55


56







57








58







59






60






61






62








63

















64
65
66
67
68





























69
70
71
72
73
74
75


76
77
78
79
80
81
82
83

84
85
86


87
88
89

90
91
92
93
94

95
96
97
98
99
100
101
102
103
104
105
106
107

108
109
110
111
112
113
114
115




-
+
+
+
+

+
-
+
-
-
-
-
+
+
+
+
+
+
+
+
+

-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


-
+
-
+
+


-
+

-
+

-
+
-
-
-
-
-

-
-
-
+
+
+
-
-
-
-
-
+
-
-
+
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
+
-
-
-
-
-
-
+
-
-
-
-
-
-
+
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-





-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-







-
-
+
+
+
+




-
+


-
-
+
+

-
+
+



-
+












-
+







<title>Fossil Forums</title>

<h2>Introduction</h2>

As of Fossil 2.7, Fossil includes a built-in discussion forum feature.
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
Any project complex enough to benefit from being managed by Fossil and
     [./server/|Fossil server] with [./alerts.md|email alerts]
which has more than one user can probably also benefit from having a
discussion forum. Even if your project has a discussion forum already,
there are many benefits to using Fossil's built-in forum feature, some
of which you cannot get by using third-party alternatives:
     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>Easy to Administer:</b> Third-party discussion forum and mailing
     list software tends to be difficult to install, set up, and
     administer. The Fossil forum feature aims to be as close to
     zero-configuration as is practical.
  *  <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 inherently [./antibot.wiki | protected
     only via the web UI, it is more resistent to spam.  Passers-by
     against bots].
     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 | block chain technology]
     repository using the same [./fileformat.wiki | DAG/Merkle-tree design]
     that Fossil uses to store your check-ins, wiki documents, etc.
     Posts sync to cloned repositories in a tamper-proof fashion.
     Forum posts sync to cloned repositories.

  *  <b>Space Efficient:</b> Because of Fossil's [./delta_format.wiki |
<h2>Example Installations</h2>
     delta compression technology], discussions add little to the size
     of a cloned repository. Ten years of the SQLite project's
     discussions — averaging about 2 dozen posts per day — compress down
     to [https://fossil-scm.org/forum/forumpost/9b6f3f36bdb | just
     35&nbsp;MB of space] in a Fossil forum repository.

  *  <b>Built-in Full-Text Search:</b> Fossil forums use
     [https://sqlite.org/fts3.html | SQLite's powerful FTS4 engine] to
     handle searches. If your project currently uses a mailing list for
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
     discussions, this means you are no longer reliant upon third-party
     mailing list archive services to provide a useful search engine for
     your discussions. If you are running a private Fossil repository,
     you may not even have the <em>option</em> of delegating this useful
     service to a third-party; Fossil provides this service out of the
to post anonymously provides a low-resistance path for people to report
     box.

problems, resulting in more problems being reported and fixed.
  *  <b>One Result Per Matching Post:</b> When you search the forum
     archives via the Fossil web interface, you get only one result for
     each matching post. When you search for project information via a
     standard web search engine, you might get a result from the project
     site's own mail archive plus one from Nabble, one from Gmane, one
     from The Mail Archive...

The ability to moderate and amend forum posts means that the 
  *  <b>Search Off-Line:</b> Because Fossil is a [./concepts.wiki |
     distributed version control system], project members can search
     your forum archive while disconnected from the network where the
     central Fossil instance runs. Your past discussions are potentially
     just as valuable as a wiki document or checkin comment: there is no
     good reason why you should have to wait to get back on the Internet
     or back to the office before you can search for past posts.

forums contain better information.  And backups and archives
  *  <b>Contribute Off-Line:</b> Fossil forum posts work like any other
     insertion into the repository, so a user can create new threads and
     reply to existing ones while off-line, then sync their
     contributions to the server they cloned from when back on-line.
     Yes, you can post to the forum from inside a tent, miles from the
     nearest wifi router or cellular data tower.

are as easy as running "clone".
  *  <b>Interlink with Other Fossil-Managed Artifacts:</b> Because forum
     posts are normal Fossil artifacts, you can interlink them with
     other Fossil artifacts using short internal links: link to forum
     threads from a [./tickets.wiki | ticket], link to a wiki document
     from a forum post, etc.
     

  *  <b>Durable Links:</b> Once you create a valid internal artifact
     link in Fossil, it <em>remains</em> valid, durably. With
     third-party forum software and mailing list search engines, your
     links are only valid until the third-party component changes its
     URL scheme or disappears from the web.

Both Fossil and SQLite keep their forums as separate repositories.
  *  <b>Role-Based Access Control:</b> The forum uses the same
     [https://en.wikipedia.org/wiki/Role-based_access_control | RBAC
     system] that Fossil uses to control all other repository accesses.
     The Fossil forum feature simply adds several new fine-grained
     capability bits to the existing system.

But there is no requirement to do this.  A forum can be coresident in
  *  <b>Enduring, Open File Format:</b> Since Fossil has an
     [./fileformat.wiki | open and well-documented file format], your
     discussion archives are truly that: <em>archives</em>. You are no
     longer dependent on the lifetime and business model of a
     third-party piece of software or service. Should you choose to stop
     using Fossil, you can easily extract your discussion traffic for
     transfer to another system.

the same repository with the source code.
  *  <b>Lightweight Markup:</b> Posts can be marked up using Fossil's
     existing [/md_rules | Markdown] and [/wiki_rules | Wiki] markup
     processors. No longer must you choose between two bad options: to
     restrict posts to plain text only or to allow wild-west
     HTML-formatted MIME email. Fossil's lightweight markup language
     formatting features give you a middle path, providing your users
     enough formatting power to communicate complex ideas well without
     providing so much power as to risk
     [https://wonko.com/post/html-escaping | security problems].

  *  <b>Easy Email Alerts:</b> You can configure Fossil to
     [./alerts.md | send email alerts]. Forum post emails include the
     complete message content for the benefit of those that prefer to
     visit the forum only when they need to post something.  Alerts are
     optional, and each user gets the choice of immediate or daily
     digest delivery.


<h2 id="setup">Setting up a Fossil Forum</h2>

<h3 id="caps">Capabilities</h3>

Fossil forums use the same role-based access control mechanism as
for normal Fossil repository logins.

There are several dedicated forum-related capability bits you can grant
a user:

  *  <b>Read Forum</b> (<tt>2</tt>): The user may read forum posts.

  *  <b>Write Forum</b> (<tt>3</tt>): The user may create new forum
     threads, reply to existing threads, and edit their own posts. New
     posts are held for moderation, and they are marked to prevent them
     from being included in clone and sync operations.

  *  <b>WriteTrusted Forum</b> (<tt>4</tt>): Same as <b>Write Forum</b>
     except that forum updates bypass the [#moderation | moderation and
     private artifact restrictions].

  *  <b>Moderate Forum</b> (<tt>5</tt>): User gets buttons on posts
     which allow them to either reject or approve posts held for
     moderation. User also gets access to a page (<tt>/modreq</tt>)
     showing the list of pending moderation tasks.

  *  <b>Supervise Forum</b> (<tt>6</tt>): User can grant or revoke
     <b>WriteTrusted</b> capability for other users. (Currently
     unimplemented.)

  *  <b>Email Alerts</b> (<tt>7</tt>): User can sign themselves up for
     email alerts, a.k.a. notifications.

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 &rarr; Access and enable the "Allow
users to register themselves" setting. You may also wish to give users
in the <tt>anonymous</tt> category the Read Forum (2) and Write Forum
(3) capabilities: this allows people to post without creating an account
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
Read Forum capability (2) to users in the <tt>reader</tt> category.
<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 WriteTrusted capability (4) to users in the <tt>developer</tt>
category. If you did not give the Read Forum capability (2) to
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 capability 3 or 4.
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 Email Alerts capability (7) to give others access to this feature.
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 &rarr;
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 Moderate Forum capability (5) to an uncommonly
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
203
204
205
206
207
208
209
210
211
212



213
214
215
216
217



218
219
220
221
222
223
224
225
226
227
228
229
230
231
232



233
234
235
236
237
238
239
240
241
242
243
244



245
246
247
248
249
250
251
252
253
254
255



256
257
258
259
260
261
262
132
133
134
135
136
137
138



139
140
141
142
143



144
145
146
147
148
149
150
151
152
153
154
155
156
157
158



159
160
161
162
163
164
165
166
167
168
169
170



171
172
173
174
175
176
177
178
179
180
181



182
183
184
185
186
187
188
189
190
191







-
-
-
+
+
+


-
-
-
+
+
+












-
-
-
+
+
+









-
-
-
+
+
+








-
-
-
+
+
+







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
  }
if {[anycap 23456] || [anoncap 2] || [anoncap 3]} {
  menulink /forum Forum
}
</verbatim>

These rules say that any logged-in user with any forum-related
capability (2-6 inclusive, as of this writing) or an anonymous user with
read or write capability on the forum (2, 3) will see the "Forum" navbar
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);
  }
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);
  }
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;
  }
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
301
302
303
304
305
306
307
308

309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335




336
337
338
339


340
341

342
343
344
345
346
347
348
230
231
232
233
234
235
236

237



238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257




258
259
260
261




262
263
264

265
266
267
268
269
270
271
272







-
+
-
-
-




















-
-
-
-
+
+
+
+
-
-
-
-
+
+

-
+







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 which is probably less well known than it should be, and which
problem: [./caps/login-groups.md | login groups].
has been a feature of Fossil since April of 2011: Admin &rarr;
Login-Group. This allows one Fossil repository to recognize users
authorized on a different Fossil repository.


<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>
    <p>If you're using the default Fossil skin as shipped with Fossil
    2.7 or one updated to include the changes since 2.6 or prior, there
    is a Forum button in the navbar which appears for users with any of
    the forum-related user capabilities: 2 through 6 inclusive for those
    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
    with repository logins, or caps 2 and 3 for users without a user
    account but who have solved the Anonymous user CAPTCHA.</p>
    <p>This button will not appear in the default skin for such users if
    their browser window is not greater than 1200 pixels wide.  The
    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.</p>
    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>

366
367
368
369
370
371
372
373

374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391

392
393

394
395
396
397
398
399
400
401
402
403
404
405
406
407
408

409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427

428
429
430
431



432
433
434
435
436
437


438
439
440
441
442
443
444
445
446
447
448









































290
291
292
293
294
295
296

297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314

315
316

317

318
319
320
321
322
323
324
325
326
327
328
329
330

331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349

350
351



352
353
354

355
356
357


358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411







-
+

















-
+

-
+
-













-
+


















-
+

-
-
-
+
+
+
-



-
-
+
+











+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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>Write Forum</b> capability (<tt>3</tt>)
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 semi-permanent. This changes how Fossil renders the
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
clones, if the sync is done by a user with <b>Check-Out</b> capability
users with <b>[./caps/ref.html#g | Clone]</b> capability.
(<tt>o</tt>).

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>WriteTrusted Forum</b> capability (<tt>4</tt>)
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>Moderate Forum</b> capability (<tt>5</tt>) to any of
    <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 Setup (<tt>s</tt>) or Admin (<tt>a</tt>)
    capability; it's
    [http://fossil-scm.org/index.html/artifact/b16221ffb736caa2?ln=1246-1257
    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>
    | 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>Write Forum</b> capability (<tt>3</tt>), any
    other user with the <b>Moderate Forum</b> capability (<tt>5</tt>)
    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.

Changes to 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

113
1
2
3

4
5
6

7

8
9
10
11
12
13

14
15

16
17

18
19

20
21
22
23
24
25
26
27
28
29
30
31

32
33

34
35
36
37
38
39
40
41

42
43
44

45
46
47
48
49
50
51

52
53
54

55
56

57
58
59
60
61
62
63
64
65
66
67
68
69

70
71
72
73
74
75
76
77
78
79

80
81

82
83

84
85
86

87
88

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>

<p>This checklist is loosely derived from Tom "Spot" Callaway's Fail Score
This checklist is loosely derived from Tom "Spot" Callaway's Fail Score
blog post <a href="http://spot.livejournal.com/308370.html">
http://spot.livejournal.com/308370.html</a> (see also
<a href="http://www.theopensourceway.org/book/The_Open_Source_Way-How_to_tell_if_a_FLOSS_project_is_doomed_to_FAIL.html">[1]</a> and
<a href="http://www.theopensourceway.org/book/The_Open_Source_Way-How_to_tell_if_a_FLOSS_project_is_doomed_to_FAIL.html">[1]</a>).
<a href="https://www.theopensourceway.org/wiki/How_to_tell_if_a_FLOSS_project_is_doomed_to_FAIL">[2]</a>).
Tom's original post assigned point scores to the various elements and
by adding together the individual points, the reader is supposed to be able
to judge the likelihood that the project will fail.
The point scores, and the items on the list, clearly reflect Tom's
biases and are not necessarily those of the larger open-source community.
Nevertheless, the policy of the Fossil shall be to strive for a perfect
score.</p>
score.

<p>This checklist is an inversion of Tom's original post in that it strives to
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.</p>
should not do to avoid failure.  The point values are omitted.

<p>See also:
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><p>The source code size is less than 100 MB, uncompressed.
<li>The source code size is less than 100 MB, uncompressed.

<li><p>The project uses a Version Control System (VCS).
<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><p>The project comes with documentation on how to build from source
<li>The project comes with documentation on how to build from source
and that documentation is lucid, correct, and up-to-date.

<li><p>The project is configurable using an autoconf-generated configure
<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><p>The project should be buildable using commonly available and
<li>The project should be buildable using commonly available and
       standard tools like "make".

<li><p>The project does not depend on 3rd-party proprietary build tools.
<li>The project does not depend on 3rd-party proprietary build tools.

<li><p>The project is able to dynamically link against standard libraries
<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><p>"make install" works and can be configured to target any
<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><p>The source code uses only \n for line endings, not \r\n.
<li>The source code uses only \n for line endings, not \r\n.

<li><p>The code does not depend on any special compiler features or bugs.
<li>The code does not depend on any special compiler features or bugs.

<li><p>The project has a mailing list and announces releases on
<li>The project has a mailing list and announces releases on
       the mailing list.

<li><p>The project has a bug tracker.
<li>The project has a bug tracker.

<li><p>The project has a website.
<li>The project has a website.

<li><p>Release version numbers are in the traditional X.Y or X.Y.Z format.
<li>Release version numbers are in the traditional X.Y or X.Y.Z format.

<li><p>Releases can be downloaded as tarball using
<li>Releases can be downloaded as tarball using
       gzip or bzip2 compression.

<li><p>Releases unpack into a versioned top-level directory.
<li>Releases unpack into a versioned top-level directory.
       (ex:  "projectname-1.2.3/").

<li><p>A statement of license appears at the top of every source code file
<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><p>There are no incompatible licenses in the code.
<li>There are no incompatible licenses in the code.

<li><p>The project has not been blithely proclaimed "public domain" without
<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><p>There is an accurate change log in the code and on the website.
<li>There is an accurate change log in the code and on the website.

<li><p>There is documentation in the code and on the website.
<li>There is documentation in the code and on the website.
</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.

Changes to 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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16


17
18
19



20
21
22

23
24
25
26

27
28
29



30
31
32
33
34
35
36
37
38
















39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105

106
107
108

109
110






111
112
113
114
115
116
117
118
119
120
121
122
123
124



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148



149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164

165
166
167




168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258




259
260
261
262
263




264
265
266
267
268
269

270
271
272
273



274
275
276
277
278
279
280
281





282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323


324
325
326
327
328
329
330
331
332
333
334




335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351


352
353
354
355




356
357
358
359
360
361
362
363
364
365
366
367

368
369
370
371
372
373
374
375
376
377
378






379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399




400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423






424
425
426
427
428
429













430
431
432
433
434
435
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
If you start out using one DVCS and later decide you like the other better,
you can easily [./inout.wiki | move your content]&#185;.
out using one DVCS and later decide you like the other better, you can
easily [./inout.wiki | move your version-controlled file content]

Fossil and [http://git-scm.com | Git] are very similar in many respects,
but they also have important differences.
See the table below for
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.
a high-level summary and the text that follows for more details.

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
might be biased in favor of Fossil.  Ask around for second opinions from
time using Fossil, not Git.  Ask around for second opinions from
people who have used <em>both</em> Fossil and Git.

&#185;<small><i>Git does not support
wiki, tickets, or tech-notes, so those elements will not transfer when
exporting from Fossil to Git.</i></small>
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.

<blockquote><table border=1 cellpadding=5 align=center>
<tr><th width="50%">GIT</th><th width="50%">FOSSIL</th></tr>
<tr><td>File versioning only</td>
    <td>Versioning, Tickets, Wiki, and Technotes</td></tr>
<tr><td>Ad-hoc, pile-of-files key/value database</td>
    <td>Relational SQL database</td></tr>
<tr><td>Bazaar-style development</td><td>Cathedral-style development</td></tr>
<tr><td>Designed for Linux development</td>
    <td>Designed for SQLite development</td></tr>
<tr><td>Lots of little tools</td><td>Stand-alone executable</td></tr>
<tr><td>One check-out per repository</td>
    <td>Many check-outs per repository</td></tr>
<tr><td>Remembers what you should have done</td>
    <td>Remembers what you actually did</td></tr>
<tr><td>GPL</td><td>BSD</td></tr>
</table></blockquote>
<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&nbsp;&darr;</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&nbsp;&darr;</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&nbsp;&darr;</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&nbsp;&darr;</a></td>
</tr>
<tr>
    <td>Bazaar-style development</td>
    <td>Cathedral-style development</td>
    <td><a href="#devorg">2.5.1&nbsp;&darr;</a></td>
</tr>
<tr>
    <td>Designed for Linux kernel development</td>
    <td>Designed for SQLite development</td>
    <td><a href="#scale">2.5.2&nbsp;&darr;</a></td>
</tr>
<tr>
    <td>Many contributors</td>
    <td>Select contributors</td>
    <td><a href="#contrib">2.5.3&nbsp;&darr;</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&nbsp;&darr;</a></td>
</tr>
<tr>
    <td>One check-out per repository</td>
    <td>Many check-outs per repository</td>
    <td><a href="#checkouts">2.6&nbsp;&darr;</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&nbsp;&darr;</a></td>
</tr>
<tr>
    <td>Commit first</td>
    <td>Test first</td>
    <td><a href="#testing">2.8&nbsp;&darr;</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&nbsp;&darr;</a></td>
</tr>
</table>

<h3>2.1 Feature Set</h3>
<h3 id="features">2.1 Featureful</h3>

Git provides file versioning services only, whereas Fossil adds
integrated [./wikitheory.wiki | wiki],
an integrated [./wikitheory.wiki | wiki],
[./bugtheory.wiki | ticketing &amp; bug tracking],
[./embeddeddoc.wiki | embedded documentation], and
[./event.wiki | Technical notes].
These additional capabilities are available for Git as 3rd-party and/or
user-installed add-ons, but with Fossil they are integrated into
the design.  One way to describe Fossil is that it is
"[https://github.com/ | github]-in-a-box".
[./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.
If you clone 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.

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 using a
3rd-party service such as GitHub) Fossil is much easier to set up, since
the stand-alone Fossil executable together with a 2-line CGI script
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>2.2 Database</h3>


<h3 id="selfcontained" name="selfcontained">2.2 Self Contained</h3>
The baseline data structures for Fossil and Git are the same (modulo
formatting details).  Both systems store check-ins as immutable
objects referencing their immediate ancestors and named by a
cryptographic hash of the check-in content.

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&nbsp;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&nbsp;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&nbsp;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&nbsp;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 ".git" folder or compressed into
bespoke "pack-files", whereas Fossil stores its objects in a
relational ([https://www.sqlite.org/|SQLite]) database file.  To put it
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
another way, Git uses an ad-hoc pile-of-files key/value database whereas
Fossil uses a proven, general-purpose SQL database.  This
difference is more than an implementation detail.  It
has important consequences.
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.
With Git, one can easily locate the ancestors of a particular check-in
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.  With Git, if you are looking at some
historical check-in then you cannot ask
"what came next" or "what are the children of this check-in".
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".

Fossil, on the other hand, parses essential information about check-ins
(parents, children, committers, comments, files changed, etc.)
into a relational database that can be easily
queried using concise SQL statements to find both ancestors and
descendents of a check-in.
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>

Leaf check-ins in Git that lack a "ref" become "detached", making them
difficult to locate and subject to garbage collection.  This
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
"detached head" problem has caused untold grief for countless
Git users.  With Fossil, all check-ins are easily located using
a variety of attributes (parents, children, committer, date, full-text
search of the check-in comment) and so detached heads are simply not possible.
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
The ease with which check-ins can be located and queried in Fossil
has resulted in a huge variety of reports and status screens
[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.

([./webpage-ex.md|examples]) that show project state
in ways that help developers
maintain enhanced awareness and comprehension
and avoid errors.
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.
<h3>2.3 Cathedral vs. Bazaar</h3>

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.  Git promotes a
"bazaar" development style in which numerous anonymous developers make
small and sometimes haphazard contributions.  Fossil
promotes a "cathedral" development model in which the project is
closely supervised by an highly engaged architect and implemented by
a clique of developers.
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>
Nota Bene:  This is not to say that Git cannot be used for cathedral-style
development or that Fossil cannot be used for bazaar-style development.
They can be.  But those modes are not their design intent nor their
low-friction path.

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.

Git encourages a style in which individual developers work in relative
isolation, maintaining their
own branches and occasionally rebasing and pushing selected changes up
to the main repository.  Developers using Git often have their own
private branches that nobody else ever sees.  Work becomes siloed.
This is exactly what one wants when doing bazaar-style development.
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:
Fossil, in contrast, strives to keep all changes from all contributors
mirrored in the main repository (in separate branches) at all times.
Work in progress from one developer is readily visible to all other
developers and to the project leader, well before the code is ready
to integrate.  Fossil places a lot of emphasis on reporting the state
of the project, and the changes underway by all developers, so that
all developers and especially the project leader can maintain a better
mental picture of what is happening, and better situational awareness.

<h3>2.4 Linux vs. SQLite</h3>

Git was specifically designed to support the development of Linux.
Fossil was specifically designed to support the development of SQLite.

  *  <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
Both SQLite and Linux are important pieces of software.
SQLite is found on far more systems than Linux.  (Almost every Linux
awareness.

system uses SQLite, but there are many non-Linux systems such as
iPhones, PlayStations, and Windows PCs that use SQLite.)  On the other
hand, for those systems that do use Linux, Linux is a far more important
component.
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.
Linux uses a bazaar-style development model.  There are thousands and
thousands of contributors, most of whom do not know each others names.
Git is designed for this scenario.

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.
SQLite uses cathedral-style development.  95% of the code in SQLite
comes from just three programmers, 64% from just the lead developer.
And all SQLite developers know each other well and interact daily.
Fossil is designed for this development model.

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.
<h3>2.5 Lots of little tools vs. Self-contained system</h3>

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.

Git consists 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 and requires an "installer"
script or program to get it running.
<h4 id="branches">2.5.3 Individual Branches vs. The Entire Change History</h4>

Fossil is a single self-contained stand-alone executable with hardly
any dependencies.  Fossil can be (and often is) run inside a
minimally configured chroot jail.  To install Fossil,
one merely puts the executable on $PATH.
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.
The designer of Git says that the unix philosophy is to have lots of
small tools that collaborate to get the job done.  The designer of
Fossil says that the unix philosophy is "it just works".  Both
individuals have written their DVCSes to reflect their own view
of the "unix philosophy".


<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
<h3>2.6 One vs. Many Check-outs per Repository</h3>
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.
A "repository" in Git is a pile-of-files in the ".git" subdirectory
of a single check-out.  The check-out and the repository are inseperable.

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.

With Fossil, a "repository" is a single SQLite database file
that can be stored anywhere.  There
can be multiple active check-outs from the same repository, perhaps
open on different branches or on different snapshots of the same branch.
Long-running tests or builds can be running in one check-out while
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>.

changes are being committed in another.

<h3>2.7 What you should have done vs. What you actually did</h3>
<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.  And
branches are often rebased before being pushed, to make
it appear as if development had been linear.  Git strives to record what
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 the added "mess"
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.
One commentator has mused that Git records history according to

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>2.8 GPL vs. BSD</h3>

<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
Git is covered by the GPL license whereas Fossil is covered by
design difference falls naturally out of Fossil's default-enabled
a two-clause BSD license.
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
Consider the difference between GPL and BSD licenses:  GPL is designed
to make writing easier at the expense of making reading harder.  BSD is
| the golden rule of rebasing].

designed to make reading easier at the expense of making writing harder.
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.

To a first approximation, the GPL license grants the right to read
source code to anyone who promises to give back enhancements.  In other
words, the act of reading GPL source code (a prerequiste for making changes)
implies acceptance of the license which requires updates to be contributed
back under the same license.  (The details are more complex, but the
foregoing captures the essence of the idea.)  A big advantage of the GPL
is that anybody can contribute to the code without having to sign additional
legal documentation because they have implied their acceptance of the GPL
license by the very act of reading the source code.  This means that a GPL
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].
project can legally accept anonymous and drive-by patches.

Instead of jumping straight to the commit step, Fossil
The BSD licenses, on the other hand, make reading much easier than the GPL,
because the reader need not surrender proprietary interest
in their own enhancements.  On the flip side, BSD and similarly licensed
projects must obtain legal affidavits from authors before
new content can be added into the project.  Anonymous and drive-by
patches cannot be accepted.  This makes signing up new contributors for
BSD licensed projects harder.
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:
The licenses on the implementations of Git and Fossil only apply to the
implementations themselves, not to the projects which the systems store.

<pre>
$ git merge HASH 
$ git cherry-pick HASH 
$ git revert HASH
</pre>

Nevertheless, one can see a more GPL-oriented world-view in Git and a
more BSD-oriented world-view in Fossil.  Git encourages anonymous contributions
and siloed development, which are hallmarks of the GPL/bazaar approach to
software, whereas Fossil encourages a more tightly collaborative,
...are all the same command in Fossil:

<pre>
$ fossil merge HASH
cliquish, cathedral-style approach more typical of BSD-licensed projects.
$ 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
<h2>3.0 Missing Features</h2>
operation.

Most of the capabilities found in Git are also available in Fossil and
the other way around. For example, both systems have local check-outs,
remote repositories, push/pull/sync, bisect capabilities, and a "stash".
Both systems store project history as a directed acyclic graph (DAG)
of immutable check-in objects.
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.

But there are a few capabilities in one system that are missing from the

other.
<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
<h3>3.1 Features found in Fossil but missing from Git</h3>
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.

  *  <b>The ability to show descendents of a check-in.</b>
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.

   Both Git and Fossil can easily find the ancestors of a check-in.  But
   only Fossil shows the descendents.  (It is possible to find the
   descendents of a check-in in Git using the log, but that is sufficiently
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
   difficult that nobody ever actually does it.)
[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].

  *  <b>Wiki, Embedded documentation, Trouble-tickets, and Tech-Notes</b>
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.

   Git only provides versioning of source code.  Fossil strives to provide
Almost three years after Fossil solved this problem, the
   other related configuration management services as well.
[https://sha-mbles.github.io/ | SHAmbles attack] was published, further
weakening the case for continuing to use SHA-1.

  *  <b>Named branches</b>
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.

   Branches in Fossil have persistent names that are propagated
   to collaborators via [/help?cmd=push|push] and [/help?cmd=pull|pull].
   All developers see the same name on the same branch.  Git, in contrast,
   uses only local branch names, so developers working on the
   same project can (and frequently do) use a different name for the
   same branch.
<hr/>

  *  <b>The [/help?cmd=all|fossil all] command</b>
<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,
   Fossil keeps track of all repositories and check-outs and allows
   operations over all of them with a single command.  For example, in
   Fossil is possible to request a pull of all repositories on a laptop
   from their respective servers, prior to taking the laptop off network.
   Or it is possible to do "fossil all status" to see if there are any
    [./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
   uncommitted changes that were overlooked prior to the end of the workday.

  *  <b>The [/help?cmd=ui|fossil ui] command</b>
    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
   Fossil supports an integrated web interface.  Some of the same features
   are available using third-party add-ons for Git, but they do not provide
    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
   nearly as many features and they are not nearly as convenient to use.

    requirements among Digital Ocean's offerings currently costs
    $40/month.

<h3>3.2 Features found in Git but missing from Fossil</h3>
    <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
  *  <b>Rebase</b>
    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].

   Because of its emphasis on recording history exactly as it happened,
   rather than as we would have liked it to happen, Fossil deliberately
   does not provide a "rebase" command.  One can rebase manually in Fossil,
   with sufficient perserverence, but it is not something that can be done with
   a single command.

  *  <b>Push or pull a single branch</b>
    <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&nbsp;MiB but the current <tt>fossil.exe</tt>
    zip file for Windows is 2.24&nbsp;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.

   The [/help?cmd=push|fossil push], [/help?cmd=pull|fossil pull], and
    <li><p>Both Fossil and Git support
   [/help?cmd=sync|fossil sync] commands do not provide the capability to
   push or pull individual branches.  Pushing and pulling in Fossil is
   all or nothing.  This is in keeping with Fossil's emphasis on maintaining
    [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>
   a complete record and on sharing everything between all developers.
</ol></i></small>

Changes to www/fossil3.gif.

cannot compute difference between binary files

Changes to 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
57

58
59
60

61
62
63
64

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

18
19
20
21
22
23
24
25
26
27
28
29

30
31
32
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 "^[^ ]*:" |
  fossil info 2> /dev/null | sed 's/"//g'|grep "^[^ ]*:" | while read LINE ; do
  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.
# 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
  if [ -n "$fossil_info_project_name" ] ; then 
    project=$fossil_info_project_name
    checkout=`echo $fossil_info_checkout | sed 's/^\(........\).*/\1/'`
    date=`echo $fossil_info_checkout | sed 's/^[^ ]* *..//' | sed 's/:[^:]*$//'`
    tags=$fossil_info_tags
    local_root=`echo $fossil_info_local_root | sed 's/\/$//'`
    local=`pwd | sed "s*${local_root}**" | sed "s/^$/\//"`

    # Color the first part of the prompt blue if this is a clean checkout.
    # 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 "
    PS1="$c1${project}.${tags}$c2 ${checkout} (${date}):${local}$c1\$$c2 "
  else
    PS1="\u@\h:\w\$ "
    PS1="\[\033[01;32m\]\u@\h\[\033[00m\]:\w\$ "
  fi
}

PROMPT_COMMAND=set_prompt

Changes to 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
23
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>
<h1>2013-02-21</h1>

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 timestamp for the
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:

<blockquote><pre>
<pre>
. fossil_prompt.sh
</pre></blockquote>
</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

Changes to 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
1
2

3



4
5
6
7
8
9
10
11
12


13

14
15
16
17
18

19
20
21
22

23




24
25
26
27
28
29



30
31
32
33
34

35



36

37
38
39
40

41
42
43
44
45
46
47


48
49
50
51



52
53


54
55


56


57
58
59
60
61

62


63
64
65
66
67
68
69

70
71
72
73
74
75
76

77
78
79
80
81


82
83
84
85
86
87
88
89
90
91
92
93






94
95
96
97
98
99
100
101
102



103
104
105




106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121


122
123
124
125
126


127
128
129

130
131
132
133
134
135
136
137
138

139

140





141
142
143
144
145
146
147
148
149
150
151
152


153
154
155
156
157



158
159
160
161
162

163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181

182
183
184
185
186
187
188
189


-

-
-
-
+
+
+
+
+
+
+
+

-
-
+
-
+
+
+
+

-
+
+


-
+
-
-
-
-
+
+
+
+
+

-
-
-
+
+
+
+

-
+
-
-
-
+
-

+
+

-
+
+
+
+
+

+
-
-
+
+
+
+
-
-
-
+

-
-
+
+
-
-

-
-
+
+
+
+

-
+
-
-
+






-
+

+
+
+
+
+
-
+
+

+
+
-
-
+
+

+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-
-



-
-
-
-
















-
-
+
+



-
-
+
+

-
+








-
+
-

-
-
-
-
-
+
+
+
+
+
+
+

+
+
+

-
-
+
+
+
+

-
-
-
+
+
+
+

-
+
+
+
+
+










+



-
+







# File Name Glob Patterns


A [glob pattern][glob] is a text expression that matches one or more
file names using wild cards 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.
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.

Note that although both are notations for describing patterns in text,
glob patterns are not the same thing as a [regular expression or
A glob should not be confused with a [regular expression][regexp] (RE)
regexp][regexp].
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) (Wikipedia)
[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
A number of fossil setting values hold one or more file glob patterns
that will identify files needing special treatment.  Glob patterns are
also accepted in options to certain commands as well as query
parameters to certain pages.
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.

In many cases more than one glob may be specified in a setting,
option, or query parameter by listing multiple globs separated by a
comma or white space.
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.

Of course, many fossil commands also accept lists of files to act on,

and those also may be specified with globs. Although those glob
patterns are similar to what is described here, they are not defined
by fossil, but rather by the conventions of the operating system in
## <a id="syntax"></a>Syntax
use.

Where Fossil accepts glob patterns, it will usually accept a *list* of
individual patterns separated from the others by whitespace or commas.

## Syntax
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
A list of glob patterns is simply one or more glob patterns separated
by white space or commas. If a glob must contain white spaces or

…constitutes _two_ patterns rather than one with an embedded space, in
contravention of normal shell quoting rules.

commas, it can be quoted with either single or double quotation marks.
A list is said to match if any one (or more) globs in the list
matches.
A list matches a file when any pattern in that list matches.

A glob pattern is a collection of characters compared to a target
text, usually a file name. The whole glob is said to match if it
A pattern must consume and
match the *entire* file name to succeed. Partial matches are failed matches.
successfully consumes and matches the entire target text. Glob
patterns are made up of ordinary characters and special characters.

Ordinary characters consume a single character of the target and must
match it exactly.
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.

Special characters (and special character sequences) consume zero or
Other characters have special meaning, and they may include otherwise
more characters from the target and describe what matches. The special
characters (and sequences) are:
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
`[^...]` | 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.
Special character sequences have some additional features:

[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-d]` matches
    exactly the same characters as `[abcd]`. Ranges reflect Unicode
 *  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>.

 *  Include `-` in a list by placing it last, just before the `]`.
 *  Include `]` in a list by making the first character after the `[` or
    `[^`. At any other place, `]` ends the list.
 *  Include `^` in a list by placing anywhere except first after the
    `[`.
 *  Beware that ranges in lists may include more than you expect:
 *  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 `[`.

    `[A-z]` Matches `A` and `Z`, but also matches `a` and some less
    obvious characters such as `[`, `\`, and `]` with code point
    values between `Z` and `a`.
 *  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.
 *  Note that unlike typical Unix shell globs, wildcards (`*`, `?`,
    and character lists) are allowed to match `/` directory
    separators as well as the initial `.` in the name of a hidden
    file or directory.

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, and specifically does not
include U+00A0 NO-BREAK SPACE.
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).
into lines between globs when the list is stored in a file, as for a
versioned setting.

Similarly 'single quotes' and "double quotes" are the ASCII straight
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. The glob must match the entire canonical file
to a canonical form:
name to be considered a match.

The canonical name of a file has all directory separators changed to
`/`, redundant slashes are removed, all `.` path components are
removed, and all `..` path components are resolved. (There are
additional details we are ignoring here, but they cover rare edge
cases and also follow the principle of least surprise.)
  *  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 on Windows, Unix, and any
other platform where fossil is run.
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 is case sensitive. This will
not be a surprise on Unix where all file names are also case
sensitive. However, most Windows file systems are case preserving and
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 names of the same file; on Unix they are different files.
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

### Settings that are Globs
### <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
169
170
171
172
173
174
175



176

177

178
179
180
181
182
183
184
185
186
187
188
189
190
191
192

193
194
195
196
197
198
199
200
201
202
203
204
205

206
207
208
209
210
211
212
213
214









215
216
217


218
219
220
221
222




223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241


242
243
244
245
246
247
248

249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264

265
266
267
268
269
270
271
206
207
208
209
210
211
212
213
214
215
216
217

218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246

247
248








249
250
251
252
253
254
255
256
257
258


259
260
261




262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282


283
284
285
286
287
288
289
290

291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306

307
308
309
310
311
312
313
314







+
+
+

+
-
+















+












-
+

-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+

-
-
+
+

-
-
-
-
+
+
+
+

















-
-
+
+






-
+















-
+







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.


### Commands that Refer to Globs
### <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
[`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
[`unset`]: /help?cmd=unset
[`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
[`tarball`]:   /help?cmd=tarball
[`zip`]:       /help?cmd=zip

[`http`]: /help?cmd=http
[`cgi`]: /help?cmd=cgi
[`server`]: /help?cmd=server
[`ui`]: /help?cmd=ui
[`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
[`/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. Because other parts of your operating system may interpret glob
[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 &rarr; 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.


## POSIX Systems
### <a id="posix"></a>POSIX Systems

If you are using Fossil on a system with a POSIX-compatible shell
&mdash; Linux, macOS, the BSDs, Unix, Cygwin, WSL etc. &mdash; 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
327
328
329
330
331
332
333
334
335


336
337
338
339

340
341
342
343
344
345
346
347
348
349
350

















351

352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370







371
372
373
374
375
376
377

378
379
380
381
382
383
384
385
386
387
388
389
390
391
392


393
394
395
396
397
398
399



400
401
402
403
404

405
406









407

408
409
410
411
412
413
414
415



416
417


418







419


420
421
422
423

424
425
426
427
428
429




















430
431
432
433
434
435

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
370
371
372
373
374
375
376


377
378
379
380
381

382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410

411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426




427
428
429
430
431
432
433


434
435
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







-
-
+
+



-
+











+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+















-
-
-
-
+
+
+
+
+
+
+
-
-




-
+



-
-
-
-
-
-
-
-
-

-
-
+
+






-
+
+
+





+
-
-
+
+
+
+
+
+
+
+
+
-
+





-
-
-
+
+
+

-
+
+

+
+
+
+
+
+
+
-
+
+



-
+




-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





-
+


-
+













-
-
+
+






-
+

-
-
+
+
+
+

-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+

-
+








    $ 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 have to
be:
and that was your current working directory, the command would instead
have to be:

    $ fossil add --ignore "'doc/REALLY SECRET STUFF.txt'" READ*

instead. The Fossil glob pattern still needs the `doc/` prefix because
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
## Windows
for Windows.)

Neither standard Windows command shell &mdash; `cmd.exe` or PowerShell
&mdash; 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`

These factors also affect how a program like `fossil.exe` interprets
quotation marks on its command line.

The fifth item above does not apply to `fossil.exe` when built with
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.
typical tool chains, but we will see an example below where the exception
applies in a way that affects how Fossil interprets the glob pattern.

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. Windows is
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.

(This section does not cover the [Microsoft POSIX
subsystem](https://en.wikipedia.org/wiki/Microsoft_POSIX_subsystem),
Windows' obsolete [Services for Unix
3.*x*](https://en.wikipedia.org/wiki/Windows_Services_for_UNIX) feature,
or the [Windows Subsystem for
Linux](https://en.wikipedia.org/wiki/Windows_Subsystem_for_Linux). (The
latter is sometimes incorrectly called "Bash on Windows" or "Ubuntu on
Windows.") See the POSIX Systems section above for those cases.)

For example, consider how you would set `crlf-glob` to `*` in order to
disable Fossil's "looks like a binary file" checks. The na&iuml;ve
approach will not work:
get normal Windows text files with CR+LF line endings past Fossil's
"looks like a binary file" check. The na&iuml;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.
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
That may or may not work. Either `'*'` or `*` needs to be passed through
to Fossil untouched for this to do what you expect, which may or may not
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 Fossils `setting` command
happen, depending on the factors listed above.
parser.

An approach that *will* work reliably is:

    C:\...> echo * | fossil setting crlf-glob --args -

This works because the built-in command `echo` does not expand its
arguments, and the `--args -` option makes it read further command
arguments from Fossil's standard input, which is connected to the output
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.")
"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 is:
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 command shell from
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 at the root of the Fossil checkout
directory and nothing else."
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
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 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
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:
Fossil:

 *  "A blank line matches no files..." is the same in fossil.
 *  "A line starting with # serves as a comment...." not in 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
    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.
    that is not needed in Fossil.

### Example

In a project with source and documentation:

    work
      +-- doc
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
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








-
+
-
-
-
-
-
-
-



-
-
+
+

-
-
+
+

+
+
+
-
-
-
+
+
+
+
+
+
-
-
+
-
-
+
-





## Implementation and References

Most of the implementation of glob pattern handling in fossil is found
The implementation of the Fossil-specific glob pattern handling is here:
`glob.c`, `file.c`, and each individual command and web page that uses
a glob pattern. Find commands and pages in the fossil sources by
looking for comments like `COMMAND: add` or `WEBPAGE: timeline` in
front of the function that implements the command or page in files
`src/*.c`. (Fossil's build system creates the tables used to dispatch
commands at build time by searching the sources for those comments.) A
few starting points:

:File            |:Description
--------------------------------------------------------------------------------
[`src/glob.c`][] | Implementation of glob pattern list loading, parsing, and matching.
[`src/file.c`][] | Implementation of various kinds of canonical names of a file.
[`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://www.fossil-scm.org/index.html/file/src/glob.c
[`src/file.c`]: https://www.fossil-scm.org/index.html/file/src/file.c
[`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 is implemented in SQL, so the
documentation for `GLOB` and the other string matching operators in
[SQLite] (https://sqlite.org/lang_expr.html#like) is useful. Of
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
course, the SQLite [source code]
(https://www.sqlite.org/src/artifact?name=9d52522cc8ae7f5c&ln=570-768)
[gsrc]: https://www.sqlite.org/src/artifact?name=9d52522cc8ae7f5c&ln=570-768
and [test harnesses]
(https://www.sqlite.org/src/artifact?name=66a2c9ac34f74f03&ln=586-673)
[gtst]: https://www.sqlite.org/src/artifact?name=66a2c9ac34f74f03&ln=586-673
also make entertaining reading.

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>

Changes to 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
1
2
3

4
5
6
7
8
9
10
11

12
13
14
15
16
17
18
19
20
21

22
23

24
25





26
27
28
29
30
31
32
33
34
35
36
37
38
39
40



41

42

43






44
45
46
47
48
49
50
51
52
53

54
55
56
57
58
59
60

61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82

83
84
85
86
87
88
89
90
91
92





93
94
95
96
97
98
99
100
101
102
103
104



-
+







-
+




+


+
+
-
+

-
+

-
-
-
-
-
+
+



+
+








-
-
-
+
-

-
+
-
-
-
-
-
-
+
+
+
+
+
+
+

+
+
-
+

+
+
+
+
+
-
+
+




















-
+









-
-
-
-
-
+
+
+
+
+







# 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 a single file name.
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` supports only a small subset of the options specified for
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`   | print each checkin ID considered, regardless of whether it matches
| `-v`   | invert the sense of the match

That leaves many divergences at the option level from POSIX `grep`:
That leaves several divergences at the option level from POSIX `grep`:

*   There is no built-in way to get a count of matches, as with `grep
    -c`.

*    You cannot give more than one pattern, as with `grep -e` or `grep
     -f`.
*   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.

*   There is no way to suppress all output, returning only a status code
    to indicate whether the pattern matched, as with `grep -q`.

Patches to remove those limitations will be thoughtfully considered.
*   There is no way to suppress error output, as with `grep -s`.

*   Fossil `grep` accepts only a single input file name. You cannot give
Fossil `grep` doesn’t support any of the GNU and BSD `grep` extensions.
    it a list of file names, and you cannot give it a directory name for
    Fossil to expand to the set of all files under that directory. This
    means Fossil `grep` has no equivalent of the common POSIX `grep -R`
    extension. (And if it did, it would probably have a different option
    letter, since `-R` in Fossil has a different meaning, by
    convention.)
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
*   You cannot invert the match, as with `grep -v`.
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.
Patches to remove those limitations will be thoughtfully considered.

[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`    | 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
| `\d`    | Digit
| `\D`    | Non-digit
| `\s`    | Whitespace character
| `\S`    | Non-whitespace character
| `\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:]`.

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

```


Changes to www/hacker-howto.wiki.

1

2
3
4

5
6
7

8
9
10
11
12

13
14
15
16

1
2
3

4
5
6
7
8
9
10

11
12
13
14
15
16
17
-
+


-
+



+


-


+




<title>Fossil Hackers How-To</title>
<title>Fossil Developer How-To</title>

The following links are of interest to programmers who want to modify
or enhance Fossil.  Ordinary users can safely ignore this information.
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]
  *  [./adding_code.wiki | Adding Features To 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 &mdash; available when [customizing
the ticket system][ctkt] &mdash; 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

Changes to 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











1
2
3
4

5
6






7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

25
26
27
28
29
30
31
32
33

34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

51
52
53
54
55

56
57
58
59

60
61
62
63
64
65
66
67
68


69
70
71
72
73
74
75
76
77
78
79
80
81
82

83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103

104
105
106
107
108
109
110
111
112
113
114
115
116

117
118
119
120
121
122
123
124
125
126
127
128
129
130
131


132
133
134
135
136
137
138
139
140
141
142
143
144
145



146
147
148
149
150





151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168



169
170
171
172

173
174

175
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>

<h2> Executive Summary, Or How To Avoid Reading This Article </h2>
<b>Or: How To Avoid Reading This Article</b>

There is much angst over the [http://www.shattered.io|Shattered attack]
against SHA1.  If you are concerned about this and its implications for
Fossil, simply upgrade to Fossil 2.0 or later and the problem will go away.
Everything will continue to work as before.  All of your legacy repositories
will continue to work and all of your old check-ins will still have the
same name.  Your workflow will be unchanged.
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>
<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
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"
&#91;[https://github.com/cr-marcstevens/sha1collisiondetection|1]&#93;
&#91;[https://marc-stevens.nl/research/papers/C13-S.pdf|2]&#93;.

The Hardened SHA1 implement automatically detects when the artifact
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 possible, that means that Hardened SHA1 will get a different
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
&#91;[https://marc-stevens.nl/research/papers/C13-S.pdf|2]&#93;)
report that the probability of a false positive is vanishingly small -
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," 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 sequel.)
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</h2>
<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
<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 respository twice, once under their
SHA1 name and again under their SHA3 name.  But delta compression will
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
only hash policy supported was "sha1".  All new artifacts were named
using their SHA1 hash.
Even though Fossil 2.0 was capable of understanding SHA3 hashes, it
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.

Beginning with Fossil 2.1, the default hash policy for legacy repositories
changed to "auto".
That means Fossil 2.1 will continue to generate only SHA1 hashes until it
encounters one artifact with a SHA3 hash.  Once a single SHA3 hash is
seen, Fossil automatically switches to "sha3" mode and thereafter generates
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.

Even after upgrading to Fossil 2.1, Fossil will continue to use nothing
but SHA1 hashes on legacy repositories, thus preserving complete
compatibility with Fossil 1.37 and before.  If you want Fossil to go
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:

<blockquote><verbatim>
<verbatim>
fossil hash-policy sha3
</verbatim></blockquote>
</verbatim>

The next check-in will use a SHA3 hash.  And when that check-in is pushed
to colleagues, their copies of Fossil will see the new SHA3-named artifact
and automatically convert to SHA3 as well.
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>

At some point in the future, years from now, after everybody has finally
upgraded to Fossil 2.0 or later, the default hash policy will probably
change to "sha3", or maybe even "shun-sha1".  By the time that happens,
you will probably already be using SHA3 on all your projects and so you
are unlikely to notice.
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.

Changes to www/hints.wiki.

29
30
31
32
33
34
35
36

37
38
39
40
41
42
43


44
45


46
47
48

49
50
51
52
53

54
55
56
57
58
59
60
29
30
31
32
33
34
35

36
37
38
39
40
41


42
43
44

45
46
47
48

49
50
51
52
53

54
55
56
57
58
59
60
61







-
+





-
-
+
+

-
+
+


-
+




-
+








  6.  You can manually add a "c=CHECKIN" query parameter to the timeline
      URL to get a snapshot of what was going on about the time of some
      check-in.  The "CHECKIN" can be
      [./checkin_names.wiki | any valid check-in or version name], including
      tags, branch names, and dates.  For example, to see what was going
      on in the Fossil repository on 2008-01-01, visit
      [http://www.fossil-scm.org/fossil/timeline?c=2008-01-01].
      [/timeline?c=2008-01-01].

  7.  Further to the previous two hints, there are lots of query parameters
      that you can add to timeline pages.  The available query parameters
      are tersely documented [/help?cmd=/timeline | here].

  8.  You can run "[/help?cmd=test-diff | fossil test-diff --tk $file1 $file2]"
      to get a pop-up  window with side-by-side diffs of two files, even if
  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 "test-diff", not "diff".
      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
      [http://www.fossil-scm.org/fossil/artifact/c7dd1de9f]) you can manually
      [/artifact/c7dd1de9f]) you can manually
      add a query parameter of the form "ln=FROM,TO" to the URL that
      will cause the range of lines indicated to be highlighted.  This
      is useful in pointing out a few lines of code using a hyperlink
      in an email or text message.  Example:
      [http://www.fossil-scm.org/fossil/artifact/c7dd1de9f?ln=28,30].
      [/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".

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
109
110
111
112
113
114
115
116
117
118
119

120
121
122
123
124
125
126
127
128
129
130
131
132

133
134
135
136
137
138
139
140

141
142

143
144

145
146
147
148
149


150
151
152
153
154

1
2

3
4

5
6
7
8
9


10
11
12
13
14
15
16


17


18

19
20
21


22
23
24
25

26
27
28
29

30
31
32
33


34
35

36
37
38

39
40
41
42
43
44
45
46

47
48


49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

64


65
66

67
68
69

70

71





72
73
74
75
76
77
78

79
80
81
82
83
84
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>Home</title>
<title>A Coherent Software Configuration Management System</title>

<h3>What Is Fossil?</h3>
<h3>What Is It?</h3>

<div style='width:200px;float:right;border:2px solid #446979;padding:10px;margin:0px 10px;'>
<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> [../COPYRIGHT-BSD2.txt | License]
<li> [./faq.wiki | FAQ]
<li> [https://fossil-scm.org/forum | Support/Forum ]
<li> [./hints.wiki | Tips &amp; 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> [./hints.wiki | Tip &amp; Hints]
<li> [./permutedindex.html | Documentation Index]
<li> [./permutedindex.html | Doc Index]
<li> [https://fossil-scm.org/forum | Forum ]
<li> [http://www.mail-archive.com/fossil-users@lists.fossil-scm.org | Mailing list archives]
</ul>
<img src="fossil3.gif" align="center">
<p style="text-align:center"><img src="fossil3.gif" alt="Fossil logo"></p>
</div>

<p>Fossil is a simple, high-reliability, distributed software configuration
management system with these advanced features:
Fossil is a simple, high-reliability, distributed
[https://en.wikipedia.org/wiki/Software_configuration_management | SCM]
system with these advanced features:

  1.  <b>Integrated Bug Tracking, Wiki, Forum, and Technotes</b> -
  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], and
      [./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,
  2.  <b>Built-in Web Interface</b> 
      Fossil has a built-in, [/skins | themeable], [./serverext.wiki | extensible],
      [https://fossil-scm.org/skins/index.html | themeable],
      and intuitive [./webui.wiki | web interface]
      with a rich variety of information pages
      ([./webpage-ex.md|examples]) promoting situational awareness.
      <p>
      <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.
      you get more than just source code  you get this entire website.

  3.  <b>Self-Contained</b> -
      Fossil is a single self-contained stand-alone executable.
  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].

  4.  <b>Simple Networking</b> -
  5.  <b>Simple Networking</b> 
      No custom protocols or TCP ports.
      Fossil uses ordinary HTTP (or HTTPS or SSH)
      Fossil uses ordinary HTTPS (or SSH if you prefer)
      for network communications, so it works fine from behind
      restrictive firewalls, including [./quickstart.wiki#proxy|proxies].
      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 or over the exceedingly slow Wifi on
      used comfortably over dial-up, weak 3G, or airliner Wifi.
      airliners.

  5.  <b>CGI/SCGI Enabled</b> -  No server is required, but if you want to
      set one up, Fossil supports four easy
      [./server.wiki | server configurations].

  6.  <b>Autosync</b> -
  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 &amp; Reliable</b> -
  7.  <b>Robust &amp; Reliable</b> 
      Fossil stores content using an [./fileformat.wiki | enduring file format]
      in an SQLite database so that transactions are
      atomic even if interrupted by a power loss or system crash.
      Automatic [./selfcheck.wiki | self-checks] verify that all aspects of
      the repository are consistent prior to each commit.

  8.  <b>Free and Open-Source</b> - Uses the [../COPYRIGHT-BSD2.txt|2-clause BSD license].
  8.  <b>Free and Open-Source</b>  [../COPYRIGHT-BSD2.txt|2-clause BSD license].

<hr>
<h3>Latest Release: 2.24 ([/timeline?c=version-2.24|2024-04-23])</h3>

  *  [/uv/download.html|Download]
  *  [./changes.wiki#v2_24|Change Summary]
  *  [/timeline?p=version-2.24&bt=version-2.23&y=ci|Check-ins in version 2.24]
  *  [/timeline?df=version-2.24&y=ci|Check-ins derived from the 2.24 release]
  *  [/timeline?t=release|Timeline of all past releases]

<hr>
<h3>Links For Fossil Users:</h3>
<h3>Quick Start</h3>

  *  [./permutedindex.html | Documentation index] with [/search?c=d | full text search].
  1.  [/uv/download.html|Download] or install using a package manager or
  *  [./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.
     [./whyusefossil.wiki#definitions | Another viewpoint].
  *  [./quickstart.wiki | Quick Start] guide to using Fossil.
  *  [./qandc.wiki | Questions &amp; Criticisms] directed at Fossil.
  *  [./build.wiki | Compiling and Installing]
      [./build.wiki|compile from sources].
  *  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.
  *  There is a
     [http://lists.fossil-scm.org:8080/cgi-bin/mailman/listinfo/fossil-users | mailing list]
     (with publicly readable
     [http://www.mail-archive.com/fossil-users@lists.fossil-scm.org | archives])
     available for discussing Fossil issues.
  *  [./stats.wiki | Performance statistics] taken from real-world projects
     hosted on Fossil.
  *  How to [./shunning.wiki | delete content] from a Fossil repository.
  2.  <tt>fossil init</tt> <i>REPOSITORY-DIR/new-repository</i>
  *  How Fossil does [./password.wiki | password management].
  *  On-line [/help | help].
  *  Documentation on the
     [http://www.sqliteconcepts.org/THManual.pdf | TH1 scripting language],
     used to customize [./custom_ticket.wiki | ticketing], and several other
     subsystems, including [./customskin.md | theming].
  *  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.wiki | set up a server] for your repository.
  3.  <tt>fossil open</tt> <i>REPOSITORY-DIR/new-repository</i>
  *  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].

  4.  <tt>fossil add</tt> <i>files-or-directories</i>
<h3>Links For Fossil Developers:</h3>

  5.  <tt>fossil commit -m</tt> "<i>commit message</i>"
  *  [./contribute.wiki | Contributing] code or documentation to the
     Fossil project.
  6.  <tt>fossil ui</tt>
  *  [./theory1.wiki | Thoughts On The Design Of Fossil].
  *  [./pop.wiki | Principles Of Operation]
  *  [./tech_overview.wiki | A Technical Overview Of Fossil].
  *  The [./fileformat.wiki | file format] used by every content
     file stored in the repository.
  7.  Repeat steps 4, 5, and 6, in any order, as necessary.
      See the [./quickstart.wiki|Quick Start Guide] for more detail.
  *  The [./delta_format.wiki | format of deltas] used to
     efficiently store changes between file revisions.
  *  The [./delta_encoder_algorithm.wiki | encoder algorithm] used to
     efficiently generate deltas.
  *  The [./sync.wiki | synchronization protocol].

Changes to www/inout.wiki.

1
2
3
4
5
6
7
8
9
10
11

12
13

14
15
16

17
18
19

20
21
22
23
24
25
26
27
28






























29
30
31
32
33
34

35
36
37
38

39
40
41
42
43
44
45
46
47
48
49
50
51
52









53
54
55
56
57
58
59
60
61
62
63
64

65
66
67
68
69
70
71
72
73
74
75

76
77
78
79
80
81

82
83
84
85
86
87
88
89
90

91
92
93
94
95

96
97
98
99
100
101
102

1
2
3
4
5
6
7
8
9
10

11
12

13
14
15

16
17


18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62

63
64
65
66

67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101

102
103
104
105
106
107
108
109
110
111
112

113
114
115
116
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, run commands like this:
To import a Git repository into Fossil, say something like:

<blockquote><pre>
<pre>
cd git-repo
git fast-export --all | fossil import --git new-repo.fossil
</pre></blockquote>
</pre>

In other words, simply pipe the output of the "git fast-export" command
into the "fossil import --git" command.  The 3rd argument to the "fossil import"
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:

<blockquote><pre>
<pre>
git init new-repo
cd new-repo
fossil export --git ../repo.fossil | git fast-import
</pre></blockquote>
</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:

<blockquote><pre>
<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></blockquote>
</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:

<blockquote><pre>
<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></blockquote>
</pre>

Changes in the Git repository can be exported to the Fossil repository and then
pushed to the remote:

<blockquote><pre>
<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></blockquote>
</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> &rarr; the PageName begins with the "/" character
      or is an empty string.

  2.  <b>Hash Links</b> &rarr; the PageName is a hexadecimal number with
      at least four digits.

  3.  <b>Wiki Links</b> &rarr; 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&rarr;target_", but it also tracks "_target&rarr;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” (&#9776;) 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
([&#x2b11;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
([&#x2b11;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
([&#x2b11;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
([&#x2b11;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
([&#x2b11;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
([&#x2b11;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
([&#x2b11;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
([&#x2b11;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
([&#x2b11;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
([&#x2b11;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
([&#x2b11;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
([&#x2b11;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
([&#x2b11;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
([&#x2b11;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
([&#x2b11;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
([&#x2b11;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] &lt;i&gt;How to...&lt;/i&gt;",
    "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
([&#x2b11;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
([&#x2b11;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
([&#x2b11;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
([&#x2b11;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
([&#x2b11;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 &amp; 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
([&#x2b11;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
([&#x2b11;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

Changes to 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
1
2
3
4
5
6
7

8
9
10
11
12
13
14
15
16


17
18
19
20
21
22
23
24
25
26
27



28
29
30
31
32
33
34
35
36
37







-
+








-
-
+










-
-
-
+
+
+







<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://www.fossil-scm.org/fossil/uv/download.html | precompiled binary]
[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.

<a name="srctour"></a>
<h1>2.0 Source Code Tour</h1>
<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/src/mkindex.c | mkindex.c]
  2.  [/file/src/translate.c | translate.c]
  3.  [/file/src/makeheaders.c | makeheaders.c]
  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
61
62
63
64
65
66
67
68

69
70
71
72
73
74
75

76
77
78
79
80
81
82

83
84
85
86
87

88
89
90
91
92
93
94
95
96
97

98
99

100
101

102
103
104

105
106

107
108
109
110
111
112
113
60
61
62
63
64
65
66

67
68
69
70
71
72
73

74
75
76
77
78
79
80

81
82
83
84
85

86
87
88
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 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/src/mkversion.c | mkversion.c]
  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/src/mkbuiltin.c | mkbuiltin.c]
  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/src/makemake.tcl | makemake.tcl].
[/file/tools/makemake.tcl | makemake.tcl].

The src/ subdirectory also contains documentation about the
makeheaders preprocessor program:

  13.  [../src/makeheaders.html | makeheaders.html]
  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 rebuild.
all of the makefiles for all targets will be rebuilt.

There is an option code verification step implemented using
There is an optional code verification step implemented using

  15.  [/file/src/codecheck1.c | codecheck1.c]
  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 
that scans other Fossil source files looking for errors in printf-style
format strings.
The codecheck1 utility detects missing or surplus arguments on 
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
140
141
142
143
144
145
146
147
148


149
150
151
152

153
154

155
156
157
158
159
160
161
162
163


164
165
166
167

168
169

170
171
172
173
174
175
176
177

178
179
180
181
182
183
184
185
186
187
188
189
190
191
192

193
194

195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212

213
214

215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233

234
235
236
237
238
239
240
241
242

243
244

245
246
247
248
249
250
251
139
140
141
142
143
144
145


146
147
148
149
150

151
152

153
154
155
156
157
158
159
160


161
162
163
164
165

166
167

168
169
170
171
172
173
174


175
176
177
178
179
180
181
182
183
184
185
186
187
188
189

190
191

192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209

210
211

212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230

231
232
233
234
235
236
237
238
239

240
241

242
243
244
245
246
247
248
249







-
-
+
+



-
+

-
+







-
-
+
+



-
+

-
+






-
-
+














-
+

-
+

















-
+

-
+


















-
+








-
+

-
+







"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: src/mkversion.c.
To run the VERSION.h generator, first compile the src/mkversion.c
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:

<blockquote><pre>
<pre>
mkversion.exe manifest.uuid manifest VERSION &gt;VERSION.h
</pre></blockquote>
</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: src/mkbuiltin.c.
The builtin_data.h file contains C-langauge byte-array definitions for
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:

<blockquote><pre>
<pre>
mkbuiltin.exe diff.tcl <i>OtherFiles...</i> &gt;builtin_data.h
</pre></blockquote>
</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.

<a name="preprocessing"></a>
<h1>4.0 Preprocessing</h1>
<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:

<blockquote><pre>
<pre>
./mkindex src.c >page_index.h
</pre></blockquote>
</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:

<blockquote><pre>
<pre>
./translate src.c >src_.c
</pre></blockquote>
</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 [../src/makeheaders.html | src/makeheaders.html].
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:

<blockquote><pre>
<pre>
makeheaders src_.c:src.h sqlite3.h th.h VERSION.h
</pre></blockquote>
</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
292
293
294
295
296
297
298

299
300









301















302

303
304
305
290
291
292
293
294
295
296
297


298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322

323
324
325
326







+
-
-
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+




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>
Fossil includes a copy of [https://github.com/richgel999/miniz | miniz]
which can be used as an alternative to zlib.

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>7.0 See Also</h1>
<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 &bsol;
      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&nbsp;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.

Changes to 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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

19
20
21
22

23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104

105
106
107
108
109
110
111
112
113
114
115
116
117
118
119

120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142






143
144
145
146
147
148


149
150
151
152
153
154
155
156
157


158
159
160
161

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 Bots}
  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 {Fossil As Blockchain}
  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.wiki {How To Configure A Fossil Server}
  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}
  tickets.wiki {The Fossil Ticket System}
  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 &mdash; $prefix" $file 0]
#  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 &mdash; $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 {
<center>
<form action='$ROOT/docsrch' method='GET'>
<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>
</center>
<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='faq.wiki'>FAQ</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='http://www.fossil-scm.org/schimpf-book/home'>Jim Schimpf's
book</a>
<li> <a href='$ROOT/help'>List of commands, web-pages, and settings</a>
<li> <a href='hacker-howto.wiki'>Hacker How-To</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>
<a name="pindex"></a>
<h2 id="pindex">Other Documents:</h2>
<h2>Permuted Index:</h2>
<ul>}
foreach entry $permindex {
  foreach {title file bold} $entry break
  if {$bold} {set title <b>$title</b>}
#  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
1
2

3
4
5
6

7
8
9
10
11

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

29
30
31
32
33
34
35
36
37
38
39
40
41
42


43
44
45
46
47
48

49
50
51
52
53
54



55
56
57

58
59
60
61
62
63
64
65
66
67
68
69
70



71
72
73
74
75
76
77
78
79
80
81
82


83
84
85
86
87
88
89
90
91
92




93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115


116
117
118
119
120
121
122
123
124
125
126
127
128
129

130
131
132
133

134
135
136
137
138
139
140


141
142
143
144

145


146
147
148
149
150
151
152
153


-
+



-
+




-
+
















-
+













-
-
+
+




-
+





-
-
-
+
+
+
-













-
-
-
+
+
+









-
-
+
+








-
-
-
-
+
+
+
+



















-
-
+
+












-
+



-
+






-
-
+
+


-
+
-
-
+







<title>How To Create A New Fossil Repository</title>

<p> The [/doc/tip/www/quickstart.wiki|quickstart guide] explains how
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.</p>
sharing it over the web.

The first thing we need to do is create a fossil repository file:

<verbatim>
stephan@ludo:~/fossil$ fossil new demo.fossil
$ 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>
stephan@ludo:~/fossil$ fossil ui demo.fossil
$ 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.

<blockquote>
Tip: it is not strictly required to configure a repository
<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.
</blockquote>
</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>
stephan@ludo:~/fossil$ mkdir demo
stephan@ludo:~/fossil$ cd demo
stephan@ludo:~/fossil/demo$ fossil open ../demo.fossil
$ mkdir demo
$ cd demo
$ fossil open ../demo.fossil
stephan@ludo:~/fossil/demo$
</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>
stephan@ludo:~/fossil/demo$ cp ../csnip/*.{c,h} .
stephan@ludo:~/fossil/demo$ ls
clob.c  clob.h  clobz.c  _FOSSIL_  mkdep.c  test-clob.c
$ 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>
stephan@ludo:~/fossil/demo$ fossil add *.{c,h}
stephan@ludo:~/fossil/demo$ fossil commit -m "egg"
$ 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>
stephan@ludo:~/fossil/demo$ cd ~/fossil
stephan@ludo:~/fossil$ mkdir demo2
stephan@ludo:~/fossil$ cd demo2
stephan@ludo:~/fossil/demo2$ fossil open ../demo.fossil
$ 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 copy the fossil repository file to your web server (it doesn't
matter where, really).
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>
~/www/cgi-bin> chmod +x myrepo.cgi
$ chmod +x ~/www/cgi-bin/myrepo.cgi
</verbatim>

Now simply point your browser to
<tt>http://my.domain/cgi-bin/myrepo.cgi</tt> and you should
<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>
stephan@ludo:~/fossil$ fossil clone \
  http://MyAccountName:MyAccountPassword@my.domain/cgi-bin/myrepo.cgi
$ fossil clone \
  https://MyAccountName:MyAccountPassword@my.domain/cgi-bin/myrepo.cgi
</verbatim>

Note that you should pass your fossil login name and password (as set
If you do not provide your password in the URL, fossil will
via local server mode) during the clone - that ensures that fossil
won't ask you for it on each commit!
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

Changes to www/password.wiki.

1
2
3
4
5
6
7
8
9
1

2
3
4
5
6
7
8

-







<title>Fossil Password Management</title>
<h1 align="center">Password Management</h1>

Fossil handles user authentication using passwords.
Passwords are unique to each repository.  Passwords are not part of the
persistent state of a project.  Passwords are not versioned and
are not transmitted from one repository to another during a sync.
Passwords are local configuration information that can (and usually does)
vary from one repository to the next within the same project.
20
21
22
23
24
25
26
27

28
29

30
31
32
33
34
35




36
37
38
39
40
41
42
43

44
45

46
47
48
49
50
51
52
53

54
55

56
57
58
59
60
61
62
19
20
21
22
23
24
25

26
27

28
29





30
31
32
33
34
35
36
37
38
39
40

41
42

43
44
45
46
47
48
49
50

51
52

53
54
55
56
57
58
59
60







-
+

-
+

-
-
-
-
-
+
+
+
+







-
+

-
+







-
+

-
+








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

<blockquote>
<pre>
CE59BB9F186226D80E49D1FA2DB29F935CCA0333/alice/asdfg
</blockquote>
</pre>

That hash value is "f1b699cc9af3eeb98e5de244ca7802ae38e77bae".  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.
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:

<blockquote><pre>
<pre>
fossil test-hash-passwords <i>REPOSITORY-NAME</i>
</pre></blockquote>
</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:

<blockquote><pre>
<pre>
UPDATE user SET pw='asdfg' WHERE login='alice';
</pre></blockquote>
</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",
71
72
73
74
75
76
77
78

79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102

103
104
105
106
107
108
109
69
70
71
72
73
74
75

76
77
78
79














80
81
82
83
84
85

86
87
88
89
90
91
92
93







-
+



-
-
-
-
-
-
-
-
-
-
-
-
-
-






-
+







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
for the cookie, encoded as a Julian day number.  On all subsequent
HTTP requests, the cookie value is matched against the USER table to
enable access to the repository.

A login cookie will only work if the IP address matches.  This feature
is designed to make it more difficult for an attacker to sniff the cookie
and take over the connection.  A cookie-sniffing attack will only work
if the attacker is able to send and receive from the same IP address as
the original login.  However, we found that doing an exact IP match
caused problems for some users who are behind proxy firewalls where the proxy
might use a different IP address for each query.  To work around this
problem, newer versions of fossil only check the first 16 bits of the
32-bit IP address.  This makes a cookie sniffing attack easier since now
the attacker only has to send and receive from any IP address in a range
of IPs that are similar to the initial login.  But that is seen as an
acceptable compromise in exchange for ease of use.  If higher security
is really needed, then HTTPS can be used instead of HTTP.

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
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
130
131
132
133
134
135
136
137

138
139

140
141
142
143
144
145
146
147
148
149

150
151

152
153
154
155
114
115
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".

<blockquote><pre>
<pre>
http://<span style="color:blue">login:password</span>@servername.org/path
</pre></blockquote>
</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.

<blockquote><pre>
<pre>
http://<span style="color:blue">login:*password</span>@servername.org/path
</pre></blockquote>
</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.

Changes to www/permutedindex.html.

1
2
3
4

5
6
7
8
9
10
11

12

13
14

15
16
17
18






19
20

21
22
23
24
25
26
27




28
29
30
31
32
33
34
35
36


37
38
39
40
41
42


43
44
45
46



47
48
49
50
51
52





53
54
55
56
57

58
59
60
61
62


63
64
65
66

67
68
69


70
71
72
73
74
75
76
77
78
79
80










81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117


















118
119

120
121
122

123
124
125
126
127
128
129
130
131





132
133
134
135
136
137
138
139









140
141

142
143

144
145
146
147
148




149
150
151


152
153
154
155
156
157
158
159
160
161
162
163
164
165

166
167
168

169
170
171

172
173
174


175
176
177
178
179
180
181
182
183

184
185
186


187
188
189
190
191
192
193





194
195

196
197
198
199
200
201
202
203
204
205
206
207
208

209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230















231
232
233
234
235
236
237
238
239
240





241
242
243
244
245
246
247
248


249
250
251
252



253
254
255
256
1
2


3
4
5
6

7
8
9
10

11
12
13
14




15
16
17
18
19
20
21

22

23





24
25
26
27









28
29






30
31




32
33
34






35
36
37
38
39





40





41
42




43



44
45











46
47
48
49
50
51
52
53
54
55





































56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73


74



75









76
77
78
79
80








81
82
83
84
85
86
87
88
89


90


91





92
93
94
95



96
97














98



99



100



101
102









103



104
105







106
107
108
109
110


111













112






















113
114
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'>

<center>
<form action='$ROOT/docsrch' method='GET'>
<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>
</center>
<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='faq.wiki'>FAQ</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='http://www.fossil-scm.org/schimpf-book/home'>Jim Schimpf's
book</a>
<li> <a href='$ROOT/help'>List of commands, web-pages, and settings</a>
<li> <a href='hacker-howto.wiki'>Hacker How-To</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>
<a name="pindex"></a>
<h2 id="pindex">Other Documents:</h2>
<h2>Permuted Index:</h2>
<ul>
<li><a href="fiveminutes.wiki">5 Minutes as a Single User &mdash; Up and Running in</a></li>
<li><a href="fossil-from-msvc.wiki">2010 IDE &mdash; Integrating Fossil in the Microsoft Express</a></li>
<li><a href="tech_overview.wiki"><b>A Technical Overview Of The Design And Implementation Of Fossil</b></a></li>
<li><a href="adding_code.wiki"><b>Adding New Features To Fossil</b></a></li>
<li><a href="copyright-release.html">Agreement &mdash; Contributor License</a></li>
<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="alerts.md">Alerts And Notifications &mdash; Email</a></li>
<li><a href="delta_encoder_algorithm.wiki">Algorithm &mdash; Fossil Delta Encoding</a></li>
<li><a href="blame.wiki">Algorithm Of Fossil &mdash; The Annotate/Blame</a></li>
<li><a href="blame.wiki">Annotate/Blame Algorithm Of Fossil &mdash; The</a></li>
<li><a href="customskin.md">Appearance of Web Pages &mdash; Theming: Customizing The</a></li>
<li><a href="faq.wiki">Asked Questions &mdash; Frequently</a></li>
<li><a href="password.wiki">Authentication &mdash; Password Management And</a></li>
<li><a href="backoffice.md">Backoffice mechanism of Fossil &mdash; The</a></li>
<li><a href="whyusefossil.wiki"><b>Benefits Of Version Control</b></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="hashpolicy.wiki">Between SHA1 and SHA3-256 &mdash; Hash Policy: Choosing</a></li>
<li><a href="blockchain.md">Blockchain &mdash; Fossil As</a></li>
<li><a href="antibot.wiki">Bots &mdash; Defense against Spiders and</a></li>
<li><a href="private.wiki">Branches &mdash; Creating, Syncing, and Deleting Private</a></li>
<li><a href="branching.wiki"><b>Branching, Forking, Merging, and Tagging</b></a></li>
<li><a href="bugtheory.wiki"><b>Bug Tracking In Fossil</b></a></li>
<li><a href="branching.wiki">Branching, Forking, Merging, and Tagging</a></li>
<li><a href="bugtheory.wiki">Bug Tracking In Fossil</a></li>
<li><a href="makefile.wiki">Build Process &mdash; The Fossil</a></li>
<li><a href="aboutcgi.wiki">CGI Works In Fossil &mdash; How</a></li>
<li><a href="changes.wiki">Changelog &mdash; Fossil</a></li>
<li><a href="checkin_names.wiki"><b>Check-in And Version Names</b></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"><b>Check-in Checklist</b></a></li>
<li><a href="checkin.wiki">Checklist &mdash; Check-in</a></li>
<li><a href="../test/release-checklist.wiki">Checklist &mdash; Pre-Release Testing</a></li>
<li><a href="foss-cklist.wiki"><b>Checklist For Successful Open-Source Projects</b></a></li>
<li><a href="selfcheck.wiki">Checks &mdash; Fossil Repository Integrity Self</a></li>
<li><a href="childprojects.wiki"><b>Child Projects</b></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="hashpolicy.wiki">Choosing Between SHA1 and SHA3-256 &mdash; Hash Policy:</a></li>
<li><a href="contribute.wiki">Code or Documentation To The Fossil Project &mdash; Contributing</a></li>
<li><a href="style.wiki">Code Style Guidelines &mdash; Source</a></li>
<li><a href="../../../help">Commands and Webpages &mdash; Lists of</a></li>
<li><a href="build.wiki"><b>Compiling and Installing Fossil</b></a></li>
<li><a href="build.wiki">Compiling and Installing Fossil</a></li>
<li><a href="concepts.wiki">Concepts &mdash; Fossil Core</a></li>
<li><a href="server.wiki">Configure A Fossil Server &mdash; How To</a></li>
<li><a href="shunning.wiki">Content From Fossil &mdash; Shunning: Deleting</a></li>
<li><a href="contribute.wiki"><b>Contributing Code or Documentation To The Fossil Project</b></a></li>
<li><a href="copyright-release.html"><b>Contributor License Agreement</b></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="whyusefossil.wiki">Control &mdash; Benefits Of Version</a></li>
<li><a href="concepts.wiki">Core Concepts &mdash; Fossil</a></li>
<li><a href="newrepo.wiki">Create A New Fossil Repository &mdash; How To</a></li>
<li><a href="private.wiki"><b>Creating, Syncing, and Deleting Private Branches</b></a></li>
<li><a href="private.wiki">Creating, Syncing, and Deleting Private Branches</a></li>
<li><a href="qandc.wiki">Criticisms &mdash; Questions And</a></li>
<li><a href="customskin.md">Customizing The Appearance of Web Pages &mdash; Theming:</a></li>
<li><a href="custom_ticket.wiki"><b>Customizing The Ticket System</b></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="customgraph.md">Customizing the Timeline Graph &mdash; Theming:</a></li>
<li><a href="tech_overview.wiki">Databases Used By Fossil &mdash; SQLite</a></li>
<li><a href="antibot.wiki"><b>Defense against Spiders and Bots</b></a></li>
<li><a href="shunning.wiki">Deleting Content From Fossil &mdash; Shunning:</a></li>
<li><a href="private.wiki">Deleting Private Branches &mdash; Creating, Syncing, and</a></li>
<li><a href="delta_encoder_algorithm.wiki">Delta Encoding Algorithm &mdash; Fossil</a></li>
<li><a href="delta_format.wiki">Delta Format &mdash; Fossil</a></li>
<li><a href="tech_overview.wiki">Design And Implementation Of Fossil &mdash; A Technical Overview Of The</a></li>
<li><a href="theory1.wiki">Design Of The Fossil DVCS &mdash; Thoughts On The</a></li>
<li><a href="embeddeddoc.wiki">Documentation &mdash; Embedded Project</a></li>
<li><a href="contribute.wiki">Documentation To The Fossil Project &mdash; Contributing Code or</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="aboutdownload.wiki">Download Page Works &mdash; How The</a></li>
<li><a href="theory1.wiki">DVCS &mdash; Thoughts On The Design Of The Fossil</a></li>
<li><a href="quotes.wiki">DVCSes in General &mdash; Quotes: What People Are Saying About Fossil, Git, and</a></li>
<li><a href="alerts.md"><b>Email Alerts And Notifications</b></a></li>
<li><a href="embeddeddoc.wiki"><b>Embedded Project Documentation</b></a></li>
<li><a href="delta_encoder_algorithm.wiki">Encoding Algorithm &mdash; Fossil Delta</a></li>
<li><a href="encryptedrepos.wiki">Encrypted Repositories &mdash; How To Use</a></li>
<li><a href="env-opts.md"><b>Environment Variables and Global Options</b></a></li>
<li><a href="event.wiki"><b>Events</b></a></li>
<li><a href="webpage-ex.md">Examples &mdash; Webpage</a></li>
<li><a href="inout.wiki">Export To And From Git &mdash; Import And</a></li>
<li><a href="fossil-from-msvc.wiki">Express 2010 IDE &mdash; Integrating Fossil in the Microsoft</a></li>
<li><a href="adding_code.wiki">Features To Fossil &mdash; Adding New</a></li>
<li><a href="fileformat.wiki">File Format &mdash; Fossil</a></li>
<li><a href="globs.md"><b>File Name Glob Patterns</b></a></li>
<li><a href="unvers.wiki">Files &mdash; Unversioned</a></li>
<li><a href="branching.wiki">Forking, Merging, and Tagging &mdash; Branching,</a></li>
<li><a href="delta_format.wiki">Format &mdash; Fossil Delta</a></li>
<li><a href="fileformat.wiki">Format &mdash; Fossil File</a></li>
<li><a href="../../../md_rules">Formatting Rules &mdash; Markdown</a></li>
<li><a href="../../../wiki_rules">Formatting Rules &mdash; Wiki</a></li>
<li><a href="forum.wiki">Forums &mdash; Fossil</a></li>
<li><a href="blockchain.md"><b>Fossil As Blockchain</b></a></li>
<li><a href="changes.wiki"><b>Fossil Changelog</b></a></li>
<li><a href="concepts.wiki"><b>Fossil Core Concepts</b></a></li>
<li><a href="delta_encoder_algorithm.wiki"><b>Fossil Delta Encoding Algorithm</b></a></li>
<li><a href="delta_format.wiki"><b>Fossil Delta Format</b></a></li>
<li><a href="fileformat.wiki"><b>Fossil File Format</b></a></li>
<li><a href="forum.wiki"><b>Fossil Forums</b></a></li>
<li><a href="quickstart.wiki"><b>Fossil Quick Start Guide</b></a></li>
<li><a href="selfcheck.wiki"><b>Fossil Repository Integrity Self Checks</b></a></li>
<li><a href="selfhost.wiki"><b>Fossil Self Hosting Repositories</b></a></li>
<li><a href="settings.wiki"><b>Fossil Settings</b></a></li>
<li><a href="hints.wiki"><b>Fossil Tips And Usage Hints</b></a></li>
<li><a href="fossil-v-git.wiki"><b>Fossil Versus Git</b></a></li>
<li><a href="quotes.wiki">Fossil, Git, and DVCSes in General &mdash; Quotes: What People Are Saying About</a></li>
<li><a href="faq.wiki"><b>Frequently Asked Questions</b></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="quotes.wiki">General &mdash; Quotes: What People Are Saying About Fossil, Git, and DVCSes in</a></li>
<li><a href="fossil-v-git.wiki">Git &mdash; Fossil Versus</a></li>
<li><a href="gitusers.md">Git to Fossil Translation Guide</a></li>
<li><a href="inout.wiki">Git &mdash; Import And Export To And From</a></li>
<li><a href="quotes.wiki">Git, and DVCSes in General &mdash; Quotes: What People Are Saying About Fossil,</a></li>
<li><a href="globs.md">Glob Patterns &mdash; File Name</a></li>
<li><a href="glossary.md">Glossary</a></li>
<li><a href="env-opts.md">Global Options &mdash; Environment Variables and</a></li>
<li><a href="customgraph.md">Graph &mdash; Theming: Customizing the Timeline</a></li>
<li><a href="quickstart.wiki">Guide &mdash; Fossil Quick Start</a></li>
<li><a href="style.wiki">Guidelines &mdash; Source Code Style</a></li>
<li><a href="hacker-howto.wiki"><b>Hacker How-To</b></a></li>
<li><a href="adding_code.wiki"><b>Hacking Fossil</b></a></li>
<li><a href="hashpolicy.wiki"><b>Hash Policy: Choosing Between SHA1 and SHA3-256</b></a></li>
<li><a href="hints.wiki">Hints &mdash; Fossil Tips And Usage</a></li>
<li><a href="index.wiki"><b>Home Page</b></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="selfhost.wiki">Hosting Repositories &mdash; Fossil Self</a></li>
<li><a href="aboutcgi.wiki"><b>How CGI Works In Fossil</b></a></li>
<li><a href="aboutdownload.wiki"><b>How The Download Page Works</b></a></li>
<li><a href="server.wiki"><b>How To Configure A Fossil Server</b></a></li>
<li><a href="newrepo.wiki"><b>How To Create A New Fossil Repository</b></a></li>
<li><a href="encryptedrepos.wiki"><b>How To Use Encrypted Repositories</b></a></li>
<li><a href="hacker-howto.wiki">How-To &mdash; Hacker</a></li>
<li><a href="fossil-from-msvc.wiki">IDE &mdash; Integrating Fossil in the Microsoft Express 2010</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="tech_overview.wiki">Implementation Of Fossil &mdash; A Technical Overview Of The Design And</a></li>
<li><a href="inout.wiki"><b>Import And Export To And From Git</b></a></li>
<li><a href="interwiki.md">Interwiki Links</a></li>
<li><a href="build.wiki">Installing Fossil &mdash; Compiling and</a></li>
<li><a href="fossil-from-msvc.wiki"><b>Integrating Fossil in the Microsoft Express 2010 IDE</b></a></li>
<li><a href="fossil-is-not-relational.md">Introduction to the (Non-relational) Fossil Data Model</a></li>
<li><a href="selfcheck.wiki">Integrity Self Checks &mdash; Fossil Repository</a></li>
<li><a href="webui.wiki">Interface &mdash; The Fossil Web</a></li>
<li><a href="th1.md">Language &mdash; The TH1 Scripting</a></li>
<li><a href="copyright-release.html">License Agreement &mdash; Contributor</a></li>
<li><a href="../../../help"><b>Lists of Commands and Webpages</b></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="password.wiki">Management And Authentication &mdash; Password</a></li>
<li><a href="../../../sitemap">Map &mdash; Site</a></li>
<li><a href="../../../md_rules"><b>Markdown Formatting Rules</b></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="backoffice.md">mechanism of Fossil &mdash; The Backoffice</a></li>
<li><a href="branching.wiki">Merging, and Tagging &mdash; Branching, Forking,</a></li>
<li><a href="fossil-from-msvc.wiki">Microsoft Express 2010 IDE &mdash; Integrating Fossil in the</a></li>
<li><a href="fiveminutes.wiki">Minutes as a Single User &mdash; Up and Running in 5</a></li>
<li><a href="globs.md">Name Glob Patterns &mdash; File</a></li>
<li><a href="checkin_names.wiki">Names &mdash; Check-in And Version</a></li>
<li><a href="adding_code.wiki">New Features To Fossil &mdash; Adding</a></li>
<li><a href="newrepo.wiki">New Fossil Repository &mdash; How To Create A</a></li>
<li><a href="alerts.md">Notifications &mdash; Email Alerts And</a></li>
<li><a href="foss-cklist.wiki">Open-Source Projects &mdash; Checklist For Successful</a></li>
<li><a href="pop.wiki">Operation &mdash; Principles Of</a></li>
<li><a href="env-opts.md">Options &mdash; Environment Variables and Global</a></li>
<li><a href="tech_overview.wiki">Overview Of The Design And Implementation Of Fossil &mdash; A Technical</a></li>
<li><a href="index.wiki">Page &mdash; Home</a></li>
<li><a href="containers.md">OCI Containers</a></li>
<li><a href="aboutdownload.wiki">Page Works &mdash; How The Download</a></li>
<li><a href="customskin.md">Pages &mdash; Theming: Customizing The Appearance of Web</a></li>
<li><a href="password.wiki"><b>Password Management And Authentication</b></a></li>
<li><a href="password.wiki">Password Management And Authentication</a></li>
<li><a href="globs.md">Patterns &mdash; File Name Glob</a></li>
<li><a href="quotes.wiki">People Are Saying About Fossil, Git, and DVCSes in General &mdash; Quotes: What</a></li>
<li><a href="stats.wiki"><b>Performance Statistics</b></a></li>
<li><a href="stats.wiki">Performance Statistics</a></li>
<li><a href="hashpolicy.wiki">Policy: Choosing Between SHA1 and SHA3-256 &mdash; Hash</a></li>
<li><a href="../test/release-checklist.wiki"><b>Pre-Release Testing Checklist</b></a></li>
<li><a href="pop.wiki"><b>Principles Of Operation</b></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="private.wiki">Private Branches &mdash; Creating, Syncing, and Deleting</a></li>
<li><a href="makefile.wiki">Process &mdash; The Fossil Build</a></li>
<li><a href="contribute.wiki">Project &mdash; Contributing Code or Documentation To The Fossil</a></li>
<li><a href="embeddeddoc.wiki">Project Documentation &mdash; Embedded</a></li>
<li><a href="foss-cklist.wiki">Projects &mdash; Checklist For Successful Open-Source</a></li>
<li><a href="childprojects.wiki">Projects &mdash; Child</a></li>
<li><a href="sync.wiki">Protocol &mdash; The Fossil Sync</a></li>
<li><a href="faq.wiki">Questions &mdash; Frequently Asked</a></li>
<li><a href="qandc.wiki"><b>Questions And Criticisms</b></a></li>
<li><a href="qandc.wiki">Questions And Criticisms</a></li>
<li><a href="quickstart.wiki">Quick Start Guide &mdash; Fossil</a></li>
<li><a href="quotes.wiki"><b>Quotes: What People Are Saying About Fossil, Git, and DVCSes in General</b></a></li>
<li><a href="selfhost.wiki">Repositories &mdash; Fossil Self Hosting</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="encryptedrepos.wiki">Repositories &mdash; How To Use Encrypted</a></li>
<li><a href="newrepo.wiki">Repository &mdash; How To Create A New Fossil</a></li>
<li><a href="selfcheck.wiki">Repository Integrity Self Checks &mdash; Fossil</a></li>
<li><a href="reviews.wiki"><b>Reviews</b></a></li>
<li><a href="../../../md_rules">Rules &mdash; Markdown Formatting</a></li>
<li><a href="../../../wiki_rules">Rules &mdash; Wiki Formatting</a></li>
<li><a href="fiveminutes.wiki">Running in 5 Minutes as a Single User &mdash; Up and</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="quotes.wiki">Saying About Fossil, Git, and DVCSes in General &mdash; Quotes: What People Are</a></li>
<li><a href="th1.md">Scripting Language &mdash; The TH1</a></li>
<li><a href="tech_overview.wiki">SQLite Databases Used By Fossil</a></li>
<li><a href="selfcheck.wiki">Self Checks &mdash; Fossil Repository Integrity</a></li>
<li><a href="selfhost.wiki">Self Hosting Repositories &mdash; Fossil</a></li>
<li><a href="server.wiki">Server &mdash; How To Configure A Fossil</a></li>
<li><a href="settings.wiki">Settings &mdash; Fossil</a></li>
<li><a href="hashpolicy.wiki">SHA1 and SHA3-256 &mdash; Hash Policy: Choosing Between</a></li>
<li><a href="hashpolicy.wiki">SHA3-256 &mdash; Hash Policy: Choosing Between SHA1 and</a></li>
<li><a href="shunning.wiki"><b>Shunning: Deleting Content From Fossil</b></a></li>
<li><a href="fiveminutes.wiki">Single User &mdash; Up and Running in 5 Minutes as a</a></li>
<li><a href="../../../sitemap"><b>Site Map</b></a></li>
<li><a href="style.wiki"><b>Source Code Style Guidelines</b></a></li>
<li><a href="antibot.wiki">Spiders and Bots &mdash; Defense against</a></li>
<li><a href="tech_overview.wiki"><b>SQLite Databases Used By Fossil</b></a></li>
<li><a href="ssl.wiki">SSL with Fossil &mdash; Using</a></li>
<li><a href="ssl-server.md">SSL/TLS Server Mode</a></li>
<li><a href="quickstart.wiki">Start Guide &mdash; Fossil Quick</a></li>
<li><a href="stats.wiki">Statistics &mdash; Performance</a></li>
<li><a href="style.wiki">Style Guidelines &mdash; Source Code</a></li>
<li><a href="foss-cklist.wiki">Successful Open-Source Projects &mdash; Checklist For</a></li>
<li><a href="sync.wiki">Sync Protocol &mdash; The Fossil</a></li>
<li><a href="private.wiki">Syncing, and Deleting Private Branches &mdash; Creating,</a></li>
<li><a href="custom_ticket.wiki">System &mdash; Customizing The Ticket</a></li>
<li><a href="tickets.wiki">System &mdash; The Fossil Ticket</a></li>
<li><a href="branching.wiki">Tagging &mdash; Branching, Forking, Merging, and</a></li>
<li><a href="tech_overview.wiki">Technical Overview Of The Design And Implementation Of Fossil &mdash; A</a></li>
<li><a href="../test/release-checklist.wiki">Testing Checklist &mdash; Pre-Release</a></li>
<li><a href="th1.md">TH1 Scripting Language &mdash; The</a></li>
<li><a href="backoffice.md"><b>The "Backoffice" mechanism of Fossil</b></a></li>
<li><a href="blame.wiki"><b>The Annotate/Blame Algorithm Of Fossil</b></a></li>
<li><a href="makefile.wiki"><b>The Fossil Build Process</b></a></li>
<li><a href="sync.wiki"><b>The Fossil Sync Protocol</b></a></li>
<li><a href="tickets.wiki"><b>The Fossil Ticket System</b></a></li>
<li><a href="webui.wiki"><b>The Fossil Web Interface</b></a></li>
<li><a href="th1.md"><b>The TH1 Scripting Language</b></a></li>
<li><a href="customskin.md"><b>Theming: Customizing The Appearance of Web Pages</b></a></li>
<li><a href="customgraph.md"><b>Theming: Customizing the Timeline Graph</b></a></li>
<li><a href="theory1.wiki"><b>Thoughts On The Design Of The Fossil DVCS</b></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="custom_ticket.wiki">Ticket System &mdash; Customizing The</a></li>
<li><a href="tickets.wiki">Ticket System &mdash; The Fossil</a></li>
<li><a href="customgraph.md">Timeline Graph &mdash; Theming: Customizing the</a></li>
<li><a href="hints.wiki">Tips And Usage Hints &mdash; Fossil</a></li>
<li><a href="bugtheory.wiki">Tracking In Fossil &mdash; Bug</a></li>
<li><a href="unvers.wiki"><b>Unversioned Files</b></a></li>
<li><a href="fiveminutes.wiki"><b>Up and Running in 5 Minutes as a Single User</b></a></li>
<li><a href="hints.wiki">Usage Hints &mdash; Fossil Tips And</a></li>
<li><a href="fiveminutes.wiki">User &mdash; Up and Running in 5 Minutes as a Single</a></li>
<li><a href="ssl.wiki"><b>Using SSL with Fossil</b></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="env-opts.md">Variables and Global Options &mdash; Environment</a></li>
<li><a href="whyusefossil.wiki">Version Control &mdash; Benefits Of</a></li>
<li><a href="checkin_names.wiki">Version Names &mdash; Check-in And</a></li>
<li><a href="fossil-v-git.wiki">Versus Git &mdash; Fossil</a></li>
<li><a href="webui.wiki">Web Interface &mdash; The Fossil</a></li>
<li><a href="customskin.md">Web Pages &mdash; Theming: Customizing The Appearance of</a></li>
<li><a href="webpage-ex.md"><b>Webpage Examples</b></a></li>
<li><a href="../../../help">Webpages &mdash; Lists of Commands and</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="quotes.wiki">What People Are Saying About Fossil, Git, and DVCSes in General &mdash; Quotes:</a></li>
<li><a href="whyusefossil.wiki"><b>Why You Should Use Fossil</b></a></li>
<li><a href="../../../wiki_rules"><b>Wiki Formatting Rules</b></a></li>
<li><a href="wikitheory.wiki"><b>Wiki In Fossil</b></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>
<li><a href="aboutdownload.wiki">Works &mdash; How The Download Page</a></li>
<li><a href="aboutcgi.wiki">Works In Fossil &mdash; How CGI</a></li>
<li><a href="whyusefossil.wiki">You Should Use Fossil &mdash; Why</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
&#96;&#96;&#96; ... &#96;&#96;&#96; or between
&#126;&#126;&#126; ... &#126;&#126;&#126; using three or
more &#96; or &#126; characters.  The fenced code block normally
displays its content verbatim, but if an "info string"  of "pikchr"
follows the opening &#96;&#96;&#96; or &#126;&#126;&#126;, 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**  &rarr;  The diagram is indented from the left margin.

  *  **center**  &rarr;  The diagram is centered

  *  **toggle**  &rarr;  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** &rarr;  The diagram is shown at the left margin and
     text fills in around the diagram.

  *  **float-right** &rarr;  The diagram is shown at the right margin and
     text fills in around the diagram.

  *  **source** &rarr;  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
73
74
75
76



































































1

2

3
4

5




































































6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
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>
<h1 align="center">Principles Of Operation</h1>

<p>
This page attempts to define the foundational principals upon
which Fossil is built.
</p>

<ul>
<li><p>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.</p></li>

<li><p>A project resides in one or more repositories.  Each
repository is administered and operates independently
of the others.</p></li>

<li><p>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.</p></li>

<li><p>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.</p></li>

<li><p>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.</p></li>

<li><p>It is theoretically possible for two artifacts with different
content to share the same hash.  But finding two such
artifacts is so incredibly difficult and unlikely that we
consider it to be an impossibility.</p></li>

<li><p>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.</p></li>

<li><p>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.</p></li>

<li><p>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.</p></li>

<li><p>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.</p></li>

<li><p>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.</p></li>
  *  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.

Changes to 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
1
2
3
4
5
6
7
8
9
10
11
12

13
14

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

30
31
32
33

34
35


36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79

80
81

82
83
84







85
86
87
88
89
90
91
92
93
94
95
96
97
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:

<blockquote><pre>
<pre>
fossil commit --private
</pre></blockquote>
</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:

<blockquote><pre>
<pre>
fossil update trunk
fossil merge private
fossil commit
</pre></blockquote>
</pre>

The private branch remains private.  (There is no way to convert a private
branch into a public branch.)  But all of the changes associated with
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:

<blockquote><pre>
<verbatim>
fossil clone --private http://user@linux.localnetwork:8080/ mac-clone.fossil
</pre></blockquote>
</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.  Furthermore,
you have to enable the "Private" capability (the "x" capability) in order
to enable private branch syncing.  The Admin ("a") and Superuser ("s") do
<u>not</u> imply Private ("x"); you'll have to set "x" in addition to
"a" or "s".  This is a little extra work, but there is a purpose here:
the interface is designed to make it very difficult to accidentally
sync a private branch to a public server.  It is highly recommended that
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:

<blockquote><pre>
<pre>
fossil scrub --private
</pre></blockquote>
</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
155

156
157

158
159
1


2



3
4
5

6
7
8
9
10


11
12
13
14

15
16


17
18
19
20
21
22
23
24
25
26
27
28
29

30
31
32
33

34
35
36
37
38
39
40
41
42
43
44
45
46

47
48
49
50


51
52
53
54

55
56
57
58
59
60
61
62
63

64
65
66
67
68


69
70
71

72
73

74
75
76
77


78
79
80
81
82
83
84


85
86
87
88

89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107



108
109
110
111
112
113
114

115
116
117
118

119
120
121
122
123

124
125
126
127
128
129

130
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>
<nowiki>
<h1 align="center">Questions And Criticisms</h1>

<p>This page is a collection of real questions and criticisms that have been
raised against fossil together with responses from the program's author.</p>

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
<p>Note: See also the <a href="faq.wiki">Frequently Asked Questions</a>.</p>
[/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>

<blockquote>
  <p>I wrote fossil because none of the
<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.</p>
  don't meet mine, and so fossil is necessary for me.

  <p>Features provided by fossil that one does not get with other
  DVCSes include:</p>
  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>
</blockquote>
</div>

<b>Why should I use this rather than Trac?</b>

<blockquote>
<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>
</blockquote>
</div>

<b>Love the concept here. Anyone using this for real work yet?</b>

<blockquote>
Fossil is <a href="http://www.fossil-scm.org/">self-hosting</a>.
<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 http://www.fossil-scm.org/
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.
</blockquote>
</div>

<b>Fossil looks like the bug tracker that would be in your
Linksys Router's administration screen.</b>

<blockquote>
<p>I take a pragmatic approach to software: form follows function.
<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.</p>
enduring, and simple DVCS than one that looks pretty.

<p>On the other hand, if you have patches that improve the appearance
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.</p>
</blockquote>
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>

<blockquote>
<p>Fossil already <u>does</u> push and pull bugs along with the files
<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:</p>
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>

<p>These points are reiterated in the opening paragraphs of
the <a href="bugtheory.wiki">Bug-Tracking In Fossil</a> document.</p>
</blockquote>
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>

<blockquote>
<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.
</blockquote>
</div>

<b>The idea of storing a repository in a binary blob like an
SQLite database terrifies me.</b>

<blockquote>
<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.
</blockquote>
</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>

<blockquote>
<p>I have no doubt that Trac has many features that fossil lacks.  But that
<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.</p>
fossil supports disconnected operation.

<p>As for bloat:  Fossil is a single self-contained executable.
As for bloat:  Fossil is a single self-contained executable.
You do not need any other packages
(diff, patch, merge, cvs, svn, rcs, git, python, perl, tcl, apache,
sqlite, and so forth)
in order to run fossil.  Fossil runs just fine in a chroot jail all
by itself.  And the self-contained fossil
executable is much less than 1MB in size.  (Update 2015-01-12: Fossil has
grown in the years since the previous sentence was written but is still
much less than 2MB according to "size" when compiled using -Os on x64 Linux.)
Fossil is the very opposite of bloat.</p>
Fossil is the very opposite of bloat.
</blockquote>

</div>

</nowiki>

Changes to www/quickstart.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
1

2


3
4
5

6
7







8
9
10
11
12
13
14
15
16


17
18
19
20
21
22
23





24
25
26
27
28
29







30
31
32
33
34
35
36
37
38
39
40


41
42
43

44
45


46
47
48



49
50
51
52
53
54
55

56
57





58
59
60
61
62
63

64
65
66
67





68
69
70
71
72
73
74
75
76
77
78
79







80
81
82
83
84
85
86
87
88


89
90
91




92
93


94
95

96
97


98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122




123
124



125
126

127

128
129




130
131
132
133
134
135
136
137






138
139
140
141
142
143
144
145


146

147





148
149
150
151
152
153









154
155
156
157
158
159
160
161
162
163
164
165
166




167
168
169
170
171


172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203




204
205
206
207





208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223

224
225
226


227
228
229



230


231

232
233
234
235
236
237


238
239
240
241
242
243
244
245
246
247



248
249


250

251


252








253
254
255
256
257
258
259
260
261
262
263
264
265
266


267
268
269
270
271
272
273
274
275




276
277
278
279
280
281

282
283
284


285
286
287
288
289
290



291
292
293
294


295
296
297
298
299
300
301
302

303



304
305
306


307
308
309
310
311
312

313
314
315
316
317
318
319




320
321
322
323
324
325
326



327
328
329
330
331
332
333
334
335
336
337
338
339
340
341



342
343
344
345



346
347
348
349




350
351
352
353
354



355
356
357
358



359
360
361
362







363
364
365
366
367
368
369
370






371
372
373
374
375
376
377



378
379
380
381




382
383
384
385
386

387
388






389
390
391
392
393
394
395





396
397
398
399
400
401






402
403
404
405
406
407
408






409
410
411
412
413
414
415







416
417
418
419
420
421
422
423






424
425
426
427
428
429
430


431
432
433



434
435
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>
<h1 align="center">Fossil Quick Start</h1>

<p>This is a guide to get you started using fossil quickly
and painlessly.</p>
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>Installing</h2>
<h2 id="install">Installing</h2>

    <p>Fossil is a single self-contained C program.  You need to
    either download a
    <a href="https://www.fossil-scm.org/fossil/uv/download.html">precompiled
    binary</a>
    or <a href="build.wiki">compile it yourself</a> from sources.
    Install fossil by putting the fossil binary
    someplace on your $PATH.</p>
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:
<a name="fslclone"></a>
<h2>General Work Flow</h2>

<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>

    <p>Fossil works with repository files (a database with the project's
    complete history) and with checked-out local trees (the working directory
    you use to do your work).
    (See [./whyusefossil.wiki#definitions | definitions] for more background.)
    The workflow looks like this:</p>
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>
<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.

    <p>The following sections will give you a brief overview of these
    operations.</p>
The following sections give a brief overview of these
operations.

<h2>Starting A New Project</h2>
<h2 id="new">Starting A New Project</h2>

    <p>To start a new project with fossil, create a new empty repository
    this way: ([/help/init | more info]) </p>
To start a new project with fossil create a new empty repository
this way: ([/help/init | more info])

    <blockquote>
    <b>fossil init </b><i> repository-filename</i>
    </blockquote>
<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>Cloning An Existing Repository</h2>
<h2 id="clone">Cloning An Existing Repository</h2>

    <p>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".</p>
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".

    <p>Clone a remote repository as follows: ([/help/clone | more info])</p>
Clone a remote repository as follows: ([/help/clone | more info])

<pre><b>fossil clone</b> <i>URL repository-filename</i>
</pre>
    <blockquote>
    <b>fossil clone</b> <i>URL  repository-filename</i>
    </blockquote>

    <p>The <i>URL</i> specifies the fossil repository

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
    you want to clone.  The <i>repository-filename</i> is the new local
    filename into which the cloned repository will be written.  For
    example:

    <blockquote>
    <b>fossil clone http://www.fossil-scm.org/ myclone.fossil</b>
    </blockquote>
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>

    <p>If the remote repository requires a login, include a
    userid in the URL like this:
If the remote repository requires a login, include a
userid in the URL like this:

    <blockquote>
    <b>fossil clone http://</b><i>userid</i><b>@www.fossil-scm.org/ myclone.fossil</b>
    </blockquote>

<pre><b>fossil clone https://</b><i>remoteuserid</i><b>@www.example.org/ myclone.fossil</b></pre>

    <p>You will be prompted separately for the password.
     Use "%HH" escapes for special characters in the userid.
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.
     Examples: "%40" in place of "@" and "%2F" in place of "/".
For example "/" would be replaced by "%2F" meaning that a userid of "Projects/Budget" would become "Projects%2FBudget")

    <p>If you are behind a restrictive firewall, you might need
    to <a href="#proxy">specify an HTTP proxy</a>.</p>
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.

    <p>A Fossil repository is a single disk file.  Instead of cloning,
    you can just make a copy of the repository file (for example, using
    "scp").  Note, however, that the repository file contains auxiliary
    information above and beyond the versioned files, including some
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
    sensitive information such as password hashes and email addresses.  If you
    want to share Fossil repositories directly, consider running the
    [/help/scrub|fossil scrub] command to remove sensitive information
[https://www.mercurial-scm.org/|Mercurial].
Fossil can also import [https://subversion.apache.org/|Subversion projects] directly.
    before transmitting the file.

<h2>Importing From Another Version Control System</h2>
<h2 id="checkout">Checking Out A Local Tree</h2>

    <p>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.
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:
<h2>Checking Out A Local Tree</h2>

    <p>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])</p>

<pre><b>fossil open ../myclone.fossil
    BUILD.txt
    COPYRIGHT-BSD2.txt
    README.md

</tt></b></pre>

    <blockquote>
    <b>fossil open </b><i> repository-filename</i>
(or "fossil open ..\myclone.fossil" on Windows).
    </blockquote>

    <p>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:</p>
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:

    <blockquote>
    <b>[/help/info | fossil info]</b><br>
    <b>[/help/status | fossil status]</b><br>
    <b>[/help/changes | fossil changes]</b><br>
    <b>[/help/diff | fossil diff]</b><br>
    <b>[/help/timeline | fossil timeline]</b><br>
    <b>[/help/ls | fossil ls]</b><br>
    <b>[/help/branch | fossil branch]</b><br>
    </blockquote>
<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.

    <p>Note that Fossil allows you to make multiple check-outs in
    separate directories from the same repository.  This enables you,
    for example, to do builds from multiple branches or versions at
    the same time without having to generate extra clones.</p>
Note that Fossil allows you to make multiple check-outs in
separate directories from the same repository.  This enables you,
for example, to do builds from multiple branches or versions at
the same time without having to generate extra clones.

    <p>To switch a checkout between different versions and branches,
    use:</p>
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":

    <blockquote>
    <b>[/help/update | fossil update]</b><br>
    <b>[/help/checkout | fossil checkout]</b><br>
    </blockquote>
<pre><b>fossil changes
EDITED     README.md
</b></pre>

    <p>[/help/update | update] honors the "autosync" option and
    does a "soft" switch, merging any local changes into the target
    version, whereas [/help/checkout | checkout] does not
    automatically sync and does a "hard" switch, overwriting local
    changes if told to do so.</p>
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
<h2>Configuring Your Local Repository</h2>
the repository, being what you get when you "fossil open" a repository
without specifying a version, populating the working directory.

    <p>When you create a new repository, either by cloning an existing
    project or create a new project of your own, you usually want to do some
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:
    local configuration.  This is easily accomplished using the web-server
    that is built into fossil.  Start the fossil webserver like this:
    ([/help/ui | more info])</p>

    <blockquote>
    <b>fossil ui </b><i> repository-filename</i>
<pre><b>fossil timeline
    </blockquote>
=== 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) 


    <p>You can omit the <i>repository-filename</i> from the command above
    if you are inside a checked-out local tree.</p>
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>

    <p>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
"current" is an alias for the checkout version, so the command
"fossil diff --from ad75dfa4a0 --to ab975c6632" gives identical results.
    how to start your web browser.  In that case, first tell fossil
    where to find your web browser using a command like this:</p>

    <blockquote>
To commit your changes to a local-only repository:
    <b>fossil setting web-browser </b><i>  path-to-web-browser</i>
    </blockquote>

    <p>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.</p>

    <p>When you are finished configuring, just press Control-C or use
    the <b>kill</b> command to shut down the mini-server.</p>

<h2>Making Changes</h2>
<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.
    <p>To add new files to your project, or remove old files, use these
    commands:</p>

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>
    <blockquote>
    <b>[/help/add | fossil add]</b> <i>file...</i><br>
    <b>[/help/rm | fossil rm]</b> <i>file...</i><br>
    <b>[/help/addremove | fossil addremove]</b> <i>file...</i><br>

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.

    </blockquote>
The following are all equivalent ways of identifying a Fossil file,
checkin or branch artifact:

    <p>You can also edit files freely.  Once you are ready to commit
    your changes, type:</p>
<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>

    <blockquote>
    <b>[/help/commit | fossil commit]</b>
    </blockquote>
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.

    <p>You will be prompted for check-in comments using whatever editor
    is specified by your VISUAL or EDITOR environment variable.</p>
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
    In the default configuration, the [/help/commit|commit]
local configuration.  This is easily accomplished using the web-server
    command will also automatically [/help/push|push] your changes, but that
    feature can be disabled.  (More information about
    [./concepts.wiki#workflow|autosync] and how to disable it.)
that is built into fossil.  Start the fossil web server like this:
([/help/ui | more info])

    Remember that your coworkers can not see your changes until you
    commit and push them.</p>
<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.
<h2>Sharing Changes</h2>

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:

    <p>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:</p>
<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.
    <blockquote>
    <b>[/help/push | fossil push]</b> <i>URL</i>
    </blockquote>

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>

    <p>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.</p>
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.

    <p>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]:</p>
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]:

    <blockquote>
    <b>[/help/pull | fossil pull]</b> <i>URL</i><br>
    <b>[/help/sync | fossil sync]</b> <i>URL</i>
    </blockquote>
<pre>
<b>[/help/pull | fossil pull]</b> <i>URL</i>
<b>[/help/sync | fossil sync]</b> <i>URL</i>
</pre>

    <p>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]:</p>
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]:

    <blockquote>
    <b>[/help/update | fossil update]</b> <i>VERSION</i>
    </blockquote>
<pre>
<b>[/help/update | fossil update]</b> <i>VERSION</i>
</pre>

    <p>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.</p>
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.

    <p>The default behavior is for [./concepts.wiki#workflow|autosync] to
    be turned on.  That means that a [/help/pull|pull] automatically occurs
    when you run [/help/update|update] and a [/help/push|push] happens
    automatically after you [/help/commit|commit].  So in normal practice,
    the push, pull, and sync commands are rarely used.  But it is important
    to know about them, all the same.</p>
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.

    <blockquote>
    <b>[/help/checkout | fossil checkout]</b> <i>VERSION</i>
    </blockquote>
<pre>
<b>[/help/checkout | fossil checkout]</b> <i>VERSION</i>
</pre>

    <p>Is similar to update except that it does not honor the autosync
    setting, nor does it merge in local changes - it prefers to overwrite
    them and fails if local changes exist unless the <tt>--force</tt>
    flag is used.</p>
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>Branching And Merging</h2>
<h2 id="branch" name="merge">Branching And Merging</h2>

    <p>Use the --branch option to the [/help/commit | commit] command
    to start a new branch.  Note that in Fossil, branches are normally
    created when you commit, not before you start editing.  You can
    use the [/help/branch | branch new] command to create a new branch
    before you start editing, if you want, but most people just wait
    until they are ready to commit.
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:</p>
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:

    <blockquote>
    <b>fossil [/help/update|update] trunk</b><br>
    <b>fossil [/help/merge|merge] featureX</b><br>
    <i># make sure the merge didn't break anything...</i><br>
    <b>fossil [/help/commit|commit]
    </blockquote>
<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>

    <p>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 cherrypick individual
    changes, or to back out individual changes, if you don't want to
    do a full merge.</p>
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 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.
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.

    <p>If a merge or update doesn't work out (perhaps something breaks or
    there are many merge conflicts) then you back up using:</p>
If a merge or update doesn't work out (perhaps something breaks or
there are many merge conflicts) then you back up using:

    <blockquote>
    <b>[/help/undo | fossil undo]</b>
    </blockquote>
<pre>
<b>[/help/undo | fossil undo]</b>
</pre>

    <p>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.</p>
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>Setting Up A Server</h2>
<h2 id="server">Setting Up A Server</h2>

    <p>Fossil can act as a stand-alone web server using one of these
    commands:</p>
Fossil can act as a stand-alone web server using one of these
commands:

    <blockquote>
    <b>[/help/server | fossil server]</b> <i>repository-filename</i><br>
    <b>[/help/ui | fossil ui]</b> <i>repository-filename</i>
    </blockquote>
<pre>
<b>[/help/server | fossil server]</b> <i>repository-filename</i>
<b>[/help/ui | fossil ui]</b> <i>repository-filename</i>
</pre>

    <p>The <i>repository-filename</i> can be omitted when these commands
    are run from within an open check-out, which a particularly useful
    shortcut for the <b>fossil ui</b> command.
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.

    <p>The <b>ui</b> command is intended for accessing the web interface
    from a local desktop.  The <b>ui</b> command binds to the loopback IP
    address only (and thus makes the web interface visible only on the
    local machine) and it automatically start your web browser pointing at the
    server.  For cross-machine collaboration, use the <b>server</b> command,
    which binds on all IP addresses and does not try to start a web browser.</p>
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].

    <p>Servers are also easily configured as:
    <ul>
    <li>[./server.wiki#inetd|inetd/xinetd]
    <li>[./server.wiki#cgi|CGI]
    <li>[./server.wiki#scgi|SCGI]
    </ul>
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].

    <p>The [./selfhost.wiki | self-hosting fossil repositories] use
    CGI.
The [./selfhost.wiki | self-hosting fossil repositories] use
CGI.

<a name="proxy"></a>
<h2>HTTP Proxies</h2>

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>

    <p>If you are behind a restrictive firewall that requires you to use
    an HTTP proxy to reach the internet, then you can configure the proxy
    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>.</p>
If you are behind a restrictive firewall that requires you to use
an HTTP proxy to reach the internet, then you can configure the proxy
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>.

    <blockquote>
    <b>fossil clone </b><i>URL</i>  <b>--proxy</b> <i>Proxy-URL</i>
    </blockquote>
<pre>
<b>fossil clone </b><i>URL</i> <b>--proxy</b> <i>Proxy-URL</i>
</pre>

    <p>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:</p>
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:

    <blockquote>
    <b>fossil setting proxy </b><i>Proxy-URL</i>
    </blockquote>
<pre>
<b>fossil setting proxy </b><i>Proxy-URL</i>
</pre>

    <p>Or, you can set the "<b>http_proxy</b>" environment variable:</p>
Or, you can set the "<b>http_proxy</b>" environment variable:

    <blockquote>
    <b>export http_proxy=</b><i>Proxy-URL</i>
<pre>
<b>export http_proxy=</b><i>Proxy-URL</i>
    </blockquote>
</pre>

    <p>To stop using the proxy, do:</p>
To stop using the proxy, do:

    <blockquote>
    <b>fossil setting proxy off</b>
    </blockquote>
<pre>
<b>fossil setting proxy off</b>
</pre>

    <p>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 an 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-workers repository on your LAN, you might type:</p>
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:

    <blockquote>
    <b>fossil sync http://192.168.1.36:8080/ --proxy off</b>
    </blockquote>
<pre>
<b>fossil sync http://192.168.1.36:8080/ --proxy off</b>
</pre>

<h2>More Hints</h2>
<h2 id="links">Other Resources</h2>

    <p>A [/help | complete list of commands] is available, as is the
    [./hints.wiki|helpful hints] document.  See the
    [./permutedindex.html#pindex|permuted index] for additional
<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>
    documentation.

</ul>
    <p>Explore and have fun!</p>

Changes to 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
172
173

174

175






176
1
2
3
4
5
6

7
8
9
10
11
12

13
14

15
16
17
18
19
20
21
22



23
24
25
26
27
28
29
30
31
32
33
34

35
36

37
38
39
40

41
42
43

44
45
46
47
48
49
50
51
52

53
54


55





56
57
58

59
60

61
62
63
64
65


66
67
68

69
70
71
72
73

74
75

76
77
78
79

80
81
82
83

84
85
86
87
88
89
90



91
92
93
94
95
96
97
98
99
100

101
102


103






104
105
106
107
108

109
110

111
112
113
114
115
116
117
118
119

120
121
122

123
124

125
126
127
128
129
130
131

132
133

134
135
136
137
138
139
140
141
142



143
144
145
146
147
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>
<h2>On The Usability Of Git</h2>

<ol>
<li>Git approaches the usability of iptables, which is to say, utterly
unusable unless you have the manpage tattooed on you arm.

<blockquote>
<p class="local-indent">
<i>by mml at [http://news.ycombinator.com/item?id=1433387]</i>
</blockquote>
</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>

<blockquote>
<i>At [http://tartley.com/?p=1267]</i>
</blockquote>
<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.

<blockquote>
<p class="local-indent">
<i>Nick Farina at [http://nfarina.com/post/9868516270/git-is-simpler]</i>
</blockquote>
</p>

<li>Initial revision of "git", The information manager from hell

<blockquote>
<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
</blockquote>
</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>
<blockquote>
<p class="local-indent">
<i>by Donal K. Fellow (dkf) on the Tcl/Tk chatroom, 2013-04-09.</i>
</blockquote>

</p>
<li>Klingon Code Warriors embrace Git; we enjoy arbitrary conflicts. Git is not for the weak and feeble. TODAY IS A GOOD DAY TO CODE.

<blockquote>
<i>teastain at [http://www.reddit.com/r/programming/comments/xpitj/10_things_i_hate_about_git/c5oj4fk]
</blockquote>

<li>&#91;G&#93;it is <i>designed</i> to forget things.

<blockquote>
<p class="local-indent">
<i>[http://www.cs.cmu.edu/~davide/howto/git_lose.html]
</blockquote>
</p>

<li>&#91;I&#93;n nearly 31 years of using a computer i have, in total, lost more data to git
(while following the instructions!!!) than any other single piece of software.

<blockquote>
<i>Stephen Beal on the [http://www.mail-archive.com/fossil-users@lists.fossil-scm.org/msg17181.html|Fossil mailing list]
<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>
</blockquote>
</p>

<li>If programmers _really_ wanted to help scientists, they'd build a version control
system that was more usable than Git.

<blockquote>
<p class="local-indent">
<i>Tweet by Greg Wilson @gvwilson on 2015-02-22 17:47</i>
</blockquote>
</p>

<li><img src='xkcd-git.gif' align='top'>

<blockquote><i>Randall Munroe.  [http://xkcd.com/1597/]</i></blockquote>
<p class="local-indent"><i>Randall Munroe.  [http://xkcd.com/1597/]</i><p>

</ol>

<h2>On The Usability Of Fossil:</h2>
<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.

<blockquote>
<i>rawjeev at [http://stackoverflow.com/questions/156322/what-do-people-think-of-the-fossil-dvcs]</i>
</blockquote>
<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.

<blockquote>
<p class="local-indent">
<i>Joe Prostko at [http://www.mail-archive.com/fossil-users@lists.fossil-scm.org/msg16716.html]
</blockquote>

</p>
<li>Fossil is awesome!!! I have never seen an app like that before,
such simplicity and flexibility!!!

<blockquote>
<i>zengr at [http://stackoverflow.com/questions/138621/best-version-control-for-lone-developer]</i>
</blockquote>

<li>This is my favourite VCS. I can carry it on a USB. And it's a complete system, with it's own
server, ticketing system, Wiki pages, and a very, very helpful timeline visualization. And
the entire program in a single file!

<blockquote>
<p class="local-indent">
<i>thunderbong commenting on hacker news: [https://news.ycombinator.com/item?id=9131619]</i>
</blockquote>
</p>


</ol>


<h2>On Git Versus Fossil</h2>

<ol>
<li value=15>
<li value=14>
After prolonged exposure to fossil, i tend to get the jitters when I work with git...

<blockquote>
<p class="local-indent">
<i>sriku - at [https://news.ycombinator.com/item?id=16104427]</i>
</blockquote>
</p>


<li>
Just want to say thanks for fossil making my life easier....
Also <nowiki>[for]</nowiki> not having a misanthropic command line interface.

<blockquote>
<p class="local-indent">
<i>Joshua Paine at [http://www.mail-archive.com/fossil-users@lists.fossil-scm.org/msg02736.html]</i>
</blockquote>
</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.

<blockquote>
<i>viablepanic at [http://www.reddit.com/r/programming/comments/bxcto/why_not_fossil_scm/]</i>
</blockquote>
<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.

<p>In git (or rather, the git community), the development history is part of
<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.

<blockquote>
<p class="local-indent">
<i>Mike Meyer on the Fossil mailing list, 2011-10-04</i>
</blockquote>
</p>

<li>github is such a pale shadow of what fossil does.

<blockquote>
<p class="local-indent">
<i>dkf on the Tcl chatroom, 2013-12-06</i>
</p>
</blockquote>

<li>&#91;With fossil&#93; 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

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
1
2
3

4
5



6
7
8
9
10
11
12

13
14
15
16
17

18
19
20
21
22

23
24
25
26
27
28
29
30
31
32
33
34

35
36
37
38

39
40
41
42
43
44
45
46



-
+

-
-
-







-
+




-
+




-
+











-
+



-
+







<title>Reviews</title>
<b>External links:</b>

  *  [http://nixtu.blogspot.com/2010/03/fossil-dvcs-on-go-first-impressions.html |
  *  [https://www.nixtu.info/2010/03/fossil-dvcs-on-go-first-impressions.html |
     Fossil DVCS on the Go - First Impressions]
  *  [http://blog.mired.org/2011/02/fossil-sweet-spot-in-vcs-space.html |
     Fossil - a sweet spot in the VCS space] by Mike Meyer.
  *  [http://blog.s11n.net/?p=72|Four reasons to take a closer look at the Fossil SCM] by Stephan Beal

<b>See Also:</b>

  *  [./quotes.wiki | Short Quotes on Fossil, Git, And DVCSes]

<b>Daniel writes on 2009-01-06:</b>

<blockquote>
<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!
</blockquote>
</div>


<b>Joshua Paine on 2010-10-22:</b>

<blockquote>
<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.
</blockquote>
</div>

<b>Stephan Beal writes on 2009-01-11:</b>

<blockquote>
<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:
57
58
59
60
61
62
63
64
65


66
67
68
69
70
71
72
54
55
56
57
58
59
60


61
62
63
64
65
66
67
68
69







-
-
+
+







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 (http://blog.s11n.net/?p=71), in over 15 source trees, and I now
get tripped up when I have to use svn or cvs.
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
136
137
138
139
140
141
142
143

133
134
135
136
137
138
139

140







-
+
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.
</blockquote>
</div>

Changes to 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
1
2
3
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:

<blockquote><pre>
<pre>
fossil server $REPOSITORY --port 9000 --scgi
</pre></blockquote>
</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:

<blockquote><pre>
<pre>
location ~ ^/demo_project/ {
    include scgi_params;
    scgi_pass localhost:9000;
    scgi_param SCRIPT_NAME "/demo_project";
    scgi_param HTTPS "on";
}
</pre></blockquote>
</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
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

19
20
21
22
23
24
25
26
27
28
29
30

31
32
33
34
35
36
37
38

39
40
41
42
43
44

45
46
47
48
49

50
51
52
53
54
55

56
57

58
59
60
61
62
63

64
65

66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85

86
87
88
89
90
91
92
93
94
95
96
97
98

99
100
101
102
103
104

105
106
107
108



109
1
2


3
4
5
6
7
8
9
10
11
12
13
14
15

16
17
18
19
20
21
22
23
24
25
26
27

28
29
30
31
32
33
34
35

36
37
38
39
40
41

42
43
44
45
46

47
48
49
50
51
52

53
54

55
56
57
58
59
60

61
62

63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82

83
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>

<h1 align="center">Fossil Repository Integrity Self-Checks</h1>

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
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
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
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-encoding mechanism designed expressly for fossil.  We want
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
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
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
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
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
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
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
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 see no merit in getting the wrong answer quickly.

Fossil may not be the fastest versioning system, but it is "fast enough".
Fossil runs quickly enough to stay out of the developers way.
Most operations complete in milliseconds, faster that you can press
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
2
3
4
5
6

7
8
9
10
11
12
13
14



15
16
17
18
19
20
21
22

23
24
25
26
27
28
29
30
31
32
33

34
35
36

37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

52
53
54

55
56
57
58

59

60
61
62
63

64
65
66
67



68
69
70
71
72



1
2
3
4
5

6
7
8

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

24
25
26
27
28
29
30
31
32
33
34

35
36
37

38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

53
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://www.fossil-scm.org/]
  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 CGI script on a
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:

<blockquote><pre>
<pre>
#!/usr/bin/fossil
repository: /fossil/fossil.fossil
</pre></blockquote>
</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:

<blockquote><pre>
<pre>
#!/home/hwaci/bin/fossil
repository: /home/hwaci/fossil/fossil.fossil
</pre></blockquote>
</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]
onto a $5/month "droplet" VPS from [https://www.digitalocean.com|Digital Ocean]
from [https://www.digitalocean.com|Digital Ocean]
located in San Francisco.

Server (3) is synchronized with the canonical server (1) by running
the following command via cron:
a command similar to the following via cron:

<blockquote><pre>
/home/hwaci/bin/fossil sync -R /home/hwaci/fossil/fossil.fossil
</pre></blockquote>
<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 as in server (3).
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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377

























































































































































































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
<title>How To Configure A Fossil Server</title>
<h2>Introduction</h2><blockquote>
<p>A server is not necessary to use Fossil, but a server does help in collaborating with
peers.  A Fossil server also works well as a complete website for a project.
For example, the complete [https://www.fossil-scm.org/] website, including the
page you are now reading,
is just a Fossil server displaying the content of the
self-hosting repository for Fossil.</p>
<p>This article is a guide for setting up your own Fossil server.
<p>See "[./aboutcgi.wiki|How CGI Works In Fossil]" for background
information on the underlying CGI technology.
See "[./sync.wiki|The Fossil Sync Protocol]" for information on the
wire protocol used for client/server communication.</p>
</blockquote>
<h2>Overview</h2><blockquote>
There are basically four ways to set up a Fossil server:
<ol>
<li>A stand-alone server
<li>Using inetd or xinetd or stunnel
<li>CGI
<li>SCGI (a.k.a. SimpleCGI)
</ol>
Each of these can serve either a single repository, or a directory hierarchy
containing many repositories with names ending in ".fossil".
</blockquote>
<a name="standalone"></a>
<h2>Standalone server</h2><blockquote>
The easiest way to set up a Fossil server is to use either the
[/help/server|server] or the [/help/ui|ui] commands:
<ul>
<li><b>fossil server</b> <i>REPOSITORY</i>
<li><b>fossil ui</b> <i>REPOSITORY</i>
</ul>
<p>
The <i>REPOSITORY</i> argument is either the name of the repository file, or
a directory containing many repositories.
Both of these commands start a Fossil server, usually on TCP port 8080, though
a higher numbered port might also be used if 8080 is already occupied.  You can
access these using URLs of the form <b>http://localhost:8080/</b>, or if
<i>REPOSITORY</i> is a directory, URLs of the form
<b>http://localhost:8080/</b><i>repo</i><b>/</b> where <i>repo</i> is the base
name of the repository file without the ".fossil" suffix.
The difference between "ui" and "server" is that "ui" will
also start a web browser and point it
to the URL mentioned above, and the "ui" command binds to
the loopback IP address (127.0.0.1) only so that the "ui" command cannot be
used to serve content to a different machine.
</p>
<p>
If one of the commands above is run from within an open checkout,
then the <i>REPOSITORY</i> argument can be omitted and the checkout is used as
the repository.
</p>
<p>
Both commands have additional command-line options that can be used to refine
their behavior.  See the [/help/server|online documentation] for an overview.
</p>
</blockquote>
<a name="inetd"></a>
<h2>Fossil as an inetd/xinetd or stunnel service</h2><blockquote>
<p>
A Fossil server can be launched on-demand by inetd or xinetd using
the [/help/http|fossil http] command. To launch Fossil from inetd, modify
your inetd configuration file (typically "/etc/inetd.conf") to contain a
line something like this:
<blockquote>
<pre>
80 stream tcp nowait.1000 root /usr/bin/fossil /usr/bin/fossil http /home/fossil/repo.fossil
</pre>
</blockquote>
In this example, you are telling "inetd" that when an incoming connection
appears on TCP port "80", that it should launch the binary "/usr/bin/fossil"
program with the arguments shown.
Obviously you will
need to modify the pathnames for your particular setup.
The final argument is either the name of the fossil repository to be served,
or a directory containing multiple repositories.
</p>
<p>
If you use a non-standard TCP port on
systems where the port-specification must be a symbolic name and cannot be
numeric, add the desired name and port to /etc/services.  For example, if
you want your Fossil server running on TCP port 12345 instead of 80, you
will need to add:
<blockquote>
<pre>
fossil          12345/tcp  #fossil server
</pre>
</blockquote>
and use the symbolic name ('fossil' in this example) instead of the numeral ('12345')
in inetd.conf. For details, see the relevant section in your system's documentation, e.g.
the [https://www.freebsd.org/doc/en/books/handbook/network-inetd.html|FreeBSD Handbook] in
case you use FreeBSD.
</p>
<p>
If your system is running xinetd, then the configuration is likely to be
in the file "/etc/xinetd.conf" or in a subfile of "/etc/xinetd.d".
An xinetd configuration file will appear like this:</p>
<blockquote>
<pre>
service http
{
  port = 80
  socket_type = stream
  wait = no
  user = root
  server = /usr/bin/fossil
  server_args = http /home/fossil/repos/
}
</pre>
</blockquote>
<p>
The xinetd example above has Fossil configured to serve multiple
repositories, contained under the "/home/fossil/repos/" directory.
</p>
<p>
In both cases notice that Fossil was launched as root.  This is not required,
but if it is done, then Fossil will automatically put itself into a chroot
jail for the user who owns the fossil repository before reading any information
off of the wire.
</p>
<p>
Inetd or xinetd must be enabled, and must be (re)started whenever their configuration
changes - consult your system's documentation for details.
</p>
<p>
[https://www.stunnel.org/ | Stunnel version 5] is an inetd-like process that
accepts and decodes SSL-encrypted connections.  Fossil can be run directly from
stunnel in a manner similar to inetd and xinetd.  This can be used to provide
a secure link to a Fossil project.  The configuration needed to get stunnel5
to invoke Fossil is very similar to the inetd and xinetd examples shown above.
The relevant parts of an stunnel configuration might look something
like the following:
<blockquote><pre><nowiki>
[https]
accept       = www.ubercool-project.org:443
TIMEOUTclose = 0
exec         = /usr/bin/fossil
execargs     = /usr/bin/fossil http /home/fossil/ubercool.fossil --https
</nowiki></pre></blockquote>
See the stunnel5 documentation for further details about the /etc/stunnel/stunnel.conf
configuration file.  Note that the [/help/http|fossil http] command should include
the --https option to let Fossil know to use "https" instead of "http" as the scheme
on generated hyperlinks.
<p>
Using inetd or xinetd or stunnel is a more complex setup
than the "standalone" server, but it has the
advantage of only using system resources when an actual connection is
attempted.  If no-one ever connects to that port, a Fossil server will
not (automatically) run. It has the disadvantage of requiring "root" access
and therefore may not normally be available to lower-priced "shared" servers
on the internet.
</p>
</blockquote>
<a name="cgi"></a>
<h2>Fossil as CGI</h2><blockquote>
<p>
A Fossil server can also be run from an ordinary web server as a CGI program.
This feature allows Fossil to be seamlessly integrated into a larger website.
CGI is how the [./selfhost.wiki | self-hosting fossil repositories] are
implemented.
</p>
<p>
To run Fossil as CGI, create a CGI script (here called "repo") in the CGI directory
of your web server and having content like this:
<blockquote><pre>
#!/usr/bin/fossil
repository: /home/fossil/repo.fossil
</pre></blockquote>
</p>

<p>
As always, adjust your paths appropriately.
It may be necessary to set permissions properly, or to modify an ".htaccess"
file or make other server-specific changes.  Consult the documentation
for your particular web server. In particular, the following permissions are
<em>normally</em> required (but, again, may be different for a particular
configuration):

<ul>
<li>The Fossil binary (/usr/bin/fossil in the example above)
must be readable/executable, and ALL directories leading up to it
must be readable by the process which executes the CGI.</li>
<li>ALL directories leading to the CGI script must also be readable and the CGI
script itself 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 administrator).</li>
<li>The repository file AND the directory containing it must be writable by the same account
which executes the Fossil binary (again, this might differ from the WWW user). The directory
needs to be writable so that sqlite can write its journal files.</li>
<li>Fossil must be able to create temporary files, the default directory
for which depends on the OS.  When the CGI process is operating within
a chroot, ensure that this directory exists and is readable/writeable
by the user who executes the Fossil binary.</li>
</ul>
</p>

<p>
Once the 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> (assuming the "repo" script is
accessible under "cgi-bin", which would be a typical deployment on Apache
for instance).
</p>
<p>
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:
<blockquote><pre>
#!/usr/bin/fossil
directory: /home/fossil/repos
notfound: http://url-to-go-to-if-repo-not-found/
</pre></blockquote>
</p>
<p>
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).
</p>
<p>
Additional options available to the CGI script are documented in the
source code.  As of 2017-07-02, the available options are described at
[/artifact/9a52a07b?ln=1777-1824|main.c lines 1777 through 1824].
</p>
</blockquote>

<a name="scgi"></a>
<h2>Fossil as SCGI</h2><blockquote>

<p>
The [/help/server|fossil server] command, described above as a way of
starting a stand-alone web server, can also be used for SCGI.  Simply add
the --scgi command-line option and the stand-alone server will interpret
and respond to the SimpleCGI or SCGI protocol rather than raw HTTP.  This can
be used in combination with a webserver (such as [http://nginx.org|Nginx])
that does not support CGI.  A typical Nginx configuration to support SCGI
with Fossil would look something like this:
<blockquote><pre>
location /demo_project/ {
    include scgi_params;
    scgi_pass localhost:9000;
    scgi_param SCRIPT_NAME "/demo_project";
    scgi_param HTTPS "on";
}
</pre></blockquote>
<p>
Note that Fossil requires the SCRIPT_NAME variable
in order to function properly, but Nginx does not provide this
variable by default.
So it is necessary to provide the SCRIPT_NAME parameter in the configuration.
Failure to do this will cause Fossil to return an error.
</p>
<p>
All of the features of the stand-alone server mode described above,
such as the ability to serve a directory full of Fossil repositories
rather than just a single repository, work the same way in SCGI mode.
</p>
<p>
For security, it is probably a good idea to add the --localhost option
to the [/help/server|fossil server] command to prevent Fossil from accepting
off-site connections.  And one might want to specify the listening TCP port
number, rather than letting Fossil choose one for itself, just to avoid
ambiguity.  A typical command to start a Fossil SCGI server
would be something like this:
<blockquote><pre>
fossil server $REPOSITORY --scgi --localhost --port 9000
</pre></blockquote>
</blockquote>

<h2>Securing a repository with SSL</h2><blockquote>
<p>
Using either CGI or SCGI, it is trivial to use SSL to
secure the server.  Simply set up the Fossil CGI scripts etc. as above,
but modify the Apache (or IIS, etc.) server to require SSL (that is, a
URL with "https://") in order to access the CGI script directory.  This
may also be accomplished (on Apache, at least) using appropriate
".htaccess" rules.
</p>
<p>
If you are using "inetd" to serve your repository, then you simply need
to add "/usr/bin/stunnel" (perhaps on a different path, depending on your
setup) before the command line to launch Fossil.
</p>
<p>
At this stage, the standalone server (e.g. "fossil server") does not
support SSL.
</p>
<p>
For more information, see <a href="./ssl.wiki">Using SSL with Fossil</a>.
</p>
</blockquote>

<a name="loadmgmt"></a>
<h2>Managing Server Load</h2><blockquote>
<p>
A Fossil server is very efficient and normally presents a very light
load on the server.
The Fossil [./selfhost.wiki | self-hosting server] is a 1/24th slice VM at
[http://www.linode.com | Linode.com] hosting 65 other repositories in
addition to Fossil (and including some very high-traffic sites such
as [http://www.sqlite.org] and [http://system.data.sqlite.org]) and
it has a typical load of 0.05 to 0.1.  A single HTTP request to Fossil
normally takes less than 10 milliseconds of CPU time to complete.  So
requests can be arriving at a continuous rate of 20 or more per second
and the CPU can still be mostly idle.
<p>
However, there are some Fossil web pages that can consume large
amounts of CPU time, especially on repositories with a large number
of files or with long revision histories.  High CPU usage pages include
[/help?cmd=/zip | /zip], [/help?cmd=/tarball | /tarball],
[/help?cmd=/annotate | /annotate] and others.  On very large repositories,
these commands can take 15 seconds or more of CPU time.
If these kinds of requests arrive too quickly, the load average on the
server can grow dramatically, making the server unresponsive.
<p>
Fossil provides two capabilities to help avoid server overload problems
due to excessive requests to expensive pages:
<ol>
<li><p>An optional cache is available that remembers the 10 most recently
    requested /zip or /tarball pages and returns the precomputed answer
    if the same page is requested again.
<li><p>Page requests can be configured to fail with a
    [http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.4 | "503 Server Overload"]
    HTTP error if an expensive request is received while the host load
    average is too high.
</ol>
Both of these load-control mechanisms are turned off by default, but they
are recommended for high-traffic sites.
<p>
The webpage cache is activated using the [/help?cmd=cache|fossil cache init]
command-line on the server.  Add a -R option to specify the specific repository
for which to enable caching.  If running this command as root, be sure to
"chown" the cache database (which is a separate file in the same directory
and with the same name as the repository but with the suffix changed to ".cache")
to give it write permission for the userid of the webserver.
<p>
To activate the server load control feature
visit the /Admin/Access setup page in the administrative web
interface and in the "<b>Server Load Average Limit</b>" box
enter the load average threshold above which "503 Server
Overload" replies will be issued for expensive requests.  On the
self-host Fossil server, that value is set to 1.5.  But you could easily
set it higher on a multi-core server.
<p>
The maximum load average can also be set on the command line using
commands like this:
<blockquote><pre>
fossil set max-loadavg 1.5
fossil all set max-loadavg 1.5
</pre></blockquote>
The second form is especially useful for changing the maximum load average
simultaneously on a large number of repositories.
<p>
Note that this load-average limiting feature is only available on operating
systems that support the "getloadavg()" API.  Most modern Unix systems have
this interface, but Windows does not, so the feature will not work on Windows.
Note also that Linux implements "getloadavg()" by accessing the "/proc/loadavg"
file in the "proc" virtual filesystem.  If you are running a Fossil instance
inside a chroot() jail on Linux, you will need to make the "/proc" file
system available inside that jail in order for this feature to work.  On
the [./selfhost.wiki|self-hosting Fossil repositories], this was accomplished
by adding a line to the "/etc/fstab" file that looks like:
<blockquote><pre>
chroot_jail_proc /home/www/proc proc ro 0 0
</pre></blockquote>
The /home/www/proc pathname should be adjusted so that the "/proc" component is
in the root of the chroot jail, of course.
<p>
To see if the load-average limiter is functional, visit the [/test_env] page
of the server to view the current load average.  If the value for the load
average is greater than zero, that means that it is possible to activate
the load-average limiter on that repository.  If the load average shows
exactly "0.0", then that means that Fossil is unable to find the load average
(either because it is in a chroot() jail without /proc access, or because
it is running on a system that does not support "getloadavg()") and so the
load-average limiter will not function.

</blockquote>

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>
      &lt;Directory ...&gt;
         SetEnv ap_trust_cgilike_cl "yes"
      &lt;/Directory&gt;
      </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 (&ge; 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 &le; 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&nbsp;GiB of RAM
    during feature installation and crashing. I had to upgrade the VM’s
    RAM to 2&nbsp;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 &lt;script&gt;...&lt;/script&gt; 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 &lt;div&gt;
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.

Changes to 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
1
2
3
4
5
6
7
8
9
10
11
12

13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

29
30
31
32
33
34
35
36




37
38
39
40
41
42
43
44
45
46
47












-
+















-
+







-
-
-
-
+
+
+
+







<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>Repository settings</h3>
<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>"Versionable" settings</h3>
<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>allow-symlinks</tt>,
<tt>binary-glob</tt>, <tt>crlf-glob</tt>, <tt>crnl-glob</tt>,
<tt>empty-dirs</tt>, <tt>encoding-glob</tt>, <tt>ignore-glob</tt>,
<tt>keep-glob</tt> and <tt>manifest</tt>. The most important is
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>

Changes to www/shunning.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
1

2
3
4

5



6
7
8

9
10

11
12


13
14
15
16
17


18
19

20


21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
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>Deleting Content From Fossil</title>
<h1 align="center">Deleting Content From Fossil</h1>

<h2>Good Reasons for Removing Content from a Fossil Repository</h2>

Fossil is designed to keep all historical content forever.  Users
Fossil is designed to keep all historical content forever. Fossil
of Fossil are discouraged from "deleting" content simply because it
has become obsolete.  Old content is part of the historical record
(part of the "fossil record") and should be maintained indefinitely.
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.
Such is the design intent of Fossil.

Nevertheless, there may occasionally arise legitimate reasons for
deleting content.  Such reasons might include:
deleting content.  Such reasons include:

  *  Spammers have inserted inappropriate content into a wiki page
     or ticket that needs to be removed.
  *  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 copyright
     may have been accidentally committed and needs to be backed
  *  A file that contains trade secrets or that is under someone else's
     copyright was accidentally committed and needs to be backed out.
     out.

  *  A malformed control artifact may have been inserted and is
     disrupting the operation of Fossil.
  *  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

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

Changes to 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





































1
2

3
4

5
6
7
8
9
10
11
12
13
14
15

16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84

85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104

105
106

107
108
109
110
111
112
113
114
115
116

117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165

166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184

185
186
187
188
189
190
191
192
193
194
195
196
197
198
199

200
201

202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240

241
242
243

244
245
246
247
248
249
250
251
252
253
254
255

256
257
258
259
260
261
262
263
264
265

266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
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>SSL and Fossil</title>
<title>Securing a Repository with TLS</title>

<h2>Using SSL with Fossil</h2>
<h2>Using TLS-Encrypted Communications with Fossil</h2>

If you are storing sensitive information in your repository, you should use SSL to encrypt all communications. This will protect the credentials used to access the server, as well preventing eavesdropping of the contents of your repository.
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.
To host a repository with SSL, you need to use an web server which supports SSL in front of the Fossil server. You can host it using the CGI option or by proxying Fossil's built in HTTP server.

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>

Your fossil client must be built with SSL support. The <tt>configure</tt> script will attempt to find OpenSSL on your system, but if necessary, you can specify the location with the <tt>--with-openssl</tt> option. Type <tt>./configure --help</tt> for details.
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:
Make sure the URL you clone from uses the <tt>https:</tt> scheme to ensure you're using SSL. If your server is configured to serve the repository from http as well as https, it's easy to accidentally use unencrypted HTTP if you forget the all important 's'.

<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!


<h2>Certificates</h2>
<h3 id="certs">Certificates</h3>

To verify the identify of a server, SSL uses certificates. Fossil needs to know which certificates you trust.
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, 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" to remember your decision.
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.
If you are using a certificate signed by a certificate authority, you need to specify the certificates you trust with the <tt>ssl-ca-location</tt> setting. Set this globally with the <tt>-global</tt> option for convenience.

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:
This should be set to the location of a file containing all the PEM encoded certificates you trust. You can obtain a certificate using a web browser, for example, Firefox, or just refer to your system's trusted CA roots which are usually stored somewhere in <tt>/etc</tt>.

<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.


<h2>Client side certificates</h2>
<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.
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>
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.


<h2 id="enforcing">Enforcing TLS Access</h2>
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.

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.
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.

As of Fossil 2.8, there is a setting in the Fossil UI under Admin &rarr;
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.
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.

<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
2
3
4
5
6
7
8
9
10
11
12
13

14
15
16
17
18
19
20
1

2
3
4
5
6
7
8
9
10
11

12
13
14
15
16
17
18
19

-










-
+







<title>Fossil Performance</title>
<h1 align="center">Performance Statistics</h1>

The questions will inevitably arise:  How does Fossil perform?
Does it use a lot of disk space or bandwidth?  Is it scalable?

In an attempt to answers these questions, this report looks at several
projects that use fossil for configuration management and examines how
well they are working.  The following table is a summary of the results.
(Last updated on 2018-06-04.)
Explanation and analysis follows the table.

<table border=1>
<table>
<tr>
<th>Project</th>
<th>Number Of Artifacts</th>
<th>Number Of Check-ins</th>
<th>Project&nbsp;Duration<br>(as of 2018-06-04)</th>
<th>Uncompressed Size</th>
<th>Repository Size</th>
123
124
125
126
127
128
129
130

131
132
133
134
135
136
137
122
123
124
125
126
127
128

129
130
131
132
133
134
135
136







-
+







"GB" means gigabytes (10<sup><small>9</small></sup> bytes)
not <a href="http://en.wikipedia.org/wiki/Gibibyte">gibibytes</a>
(2<sup><small>30</small></sup> bytes).  Similarly, "MB" and "KB"
means megabytes and kilobytes, not mebibytes and kibibytes.

<h2>Analysis And Supplemental Data</h2>

Perhaps the two most interesting datapoints in the above table are SQLite
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

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
1
2


3
4
5
6
7


8
9
10
11
12

13
14

15
16
17

18
19
20
21
22

23
24

25
26
27

28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

53
54

55
56
57

58
59





60
61
62
63
64
65
66
67
68
69

70
71
72
73

74
75
76

77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98

99
100
101
102
103
104
105
106
107
108






109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141

142
143

144
145
146
147
148

149
150

151
152

153
154


155

156


157
158
159


160

161

162
163


164
165
166

167
168
169
170
171
172
173

174

175

176
177
178
179
180
181

182
183

184
185

186
187
188
189
190
191
192
193
194

195

196


197
198
199

200
201

202
203

204
205

206
207

208
209

210
211

212
213
214
215

216
217

218
219


220
221
222


223

224

225
226
227
228

229
230

231
232
233
234
235

236
237



238
239
240
241

242
243

244
245
246
247
248
249

250

251
252

253
254


255
256
257
258


259
260
261

262
263

264
265
266
267
268

269
270

271
272
273
274
275
276
277


278
279
280
281

282
283
284

285
286

287
288

289
290
291

292
293

294
295
296
297

298
299




300
301
302
303
304

305
306
307
308
309
310
311
312
313
314

315
316


317
318
319

320
321

322
323
324

325

326
327


328

329


330
331
332

333
334

335
336
337


338

339

340
341
342
343
344
345
346
347
348
349
350
351

352
353
354
355
356
357

358
359
360

361
362
363
364

365
366

367
368
369
370

371
372


373
374
375

376
377

378
379
380
381

382
383

384
385

386
387

388
389

390
391

392
393
394
395

396
397


398
399
400

401
402
403
404

405
406
407
408
409

410
411
412
413

414
415
416
417
418

419
420
421

422
423

424
425

426
427

428
429

430
431

432
433
434
435

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


-
-
+
+



-
-
+
+
+


-
+

-
+


-
+




-
+

-
+


-
+







+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+

-
+


-
+

-
-
-
-
-
+
+
+
+
+
+
+



-
+

+
+
-
+


-
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+

+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
-
+

-
+




-
+

-
+

-
+

-
-
+
-

-
-
+
+

-
-
+
-

-
+

-
-
+
+

-
+




+

-
+
-

-
+





-
+

-
+

-
+






+

-
+
-

-
-
+
+

-
+

-
+

-
+

-
+

-
+

-
+

-
+



-
+

-
+

-
-
+
+

-
-
+
-

-
+



-
+

-
+




-
+

-
-
-
+
+
+

-
+

-
+





-

-
+

-
+

-
-
+
+
+

-
-
+
+

-
+

-
+




-
+

-
+






-
-
+
+
+

-
+


-
+

-
+

-
+


-
+

-
+



-
+

-
-
-
-
+
+
+
+

-
+









-
+

-
-
+
+

-
+

-
+


-

-
+

-
-
+
-

-
-
+
+

-
+

-
+


-
-
+
-

-
+











-
+





-
+


-
+



-
+

-
+



-
+

-
-
+
+

-
+

-
+



-
+

-
+

-
+

-
+

-
+

-
+



-
+

-
-
+
+

-
+



-
+




-
+



-
+




-
+


-
+

-
+

-
+

-
+

-
+

-
+



-
+




-
+





-
+

-
+

-
+

-
+

-
+

-
+



-
+

-
+


+
+
+



-
+



-
+

-
+

-
+






-
+







-
-
+
+





-
+

-
+

-
+

-
+

-
+

-
+


-
+
+
+
+
+



-
+




-
+

-
+

-
+




-
+

-
+



-
+

-
+

-
+

-
+





-
+

-
+

-
+








-
+


-
+

-
+

-
+







<title>The Fossil Sync Protocol</title>

<p>This document describes the wire protocol used to synchronize
content between two Fossil repositories.</p>
This document describes the wire protocol used to synchronize
content between two Fossil repositories.

<h2>1.0 Overview</h2>

<p>The global state of a fossil repository consists of an unordered
collection of artifacts.  Each artifact is identified by a cryptographic
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
servers so that all servers have copies of all artifacts.  Because
repositories so that all repositories have copies of all artifacts.  Because
artifacts are unordered, the order in which artifacts are received
at a server is inconsequential.  It is assumed that the hash names
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
hash values for available artifacts, then sharing the content of artifacts
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 hundred.</p>
shared to a few dozen.

<p>Each repository also has local state.  The local state determines
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 using transferred during a sync.  Except,
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>2.0 Transport</h2>
<h2 id="transport">2.0 Transport</h2>

<p>All communication between client and server is via HTTP requests.
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.</p>
request.

<p>The server might be running as an independent server
using the <b>server</b> command, or it might be launched from
inetd or xinetd using the <b>http</b> command.  Or the server might
be launched from CGI.
(See "[./server.wiki|How To Configure A Fossil Server]" for details.)
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.</p>
the client is the issuer of the requests.

A single [/help?cmd=push|push], 
[/help?cmd=pull|pull], or [/help?cmd=sync|sync]
<p>A single push, pull, or sync might involve multiple HTTP requests.
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.</p>
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>2.0.1 Encrypted Transport</h4>
<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.
<p>In the current implementation of Fossil, the server only
understands HTTP requests.  The client can send either
clear-text HTTP requests or encrypted HTTPS requests.  But when
HTTPS requests are sent, they first must be decrypted by a webserver
or proxy before being passed to the Fossil server.  This limitation
may be relaxed in a future release.</p>

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>2.1 Server Identification</h3>
<h3 id="srv-id">2.1 Server Identification</h3>

<p>The server is identified by a URL argument that accompanies the
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.)</p>
the same server.)

<p>The client modifies the URL by appending the method name "<b>/xfer</b>"
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</p>
line is

<blockquote>
http://fossil-scm.hwaci.com/fossil
<pre>https://fossil-scm.org/fossil</pre>
</blockquote>

<p>Then the URL that is really used to do the synchronization will
be:</p>
Then the URL that is really used to do the synchronization will
be:

<blockquote>
http://fossil-scm.hwaci.com/fossil/xfer
<pre>https://fossil-scm.org/fossil/xfer</pre>
</blockquote>

<h3>2.2 HTTP Request Format</h3>
<h3 id="req-format">2.2 HTTP Request Format</h3>

<p>The client always sends a POST request to the server.  The
general format of the POST request is as follows:</p>
The client always sends a POST request to the server.  The
general format of the POST request is as follows:

<blockquote><pre>
<pre>
POST /fossil/xfer HTTP/1.0
Host: fossil-scm.hwaci.com:80
Content-Type: application/x-fossil
Content-Length: 4216
</pre>

<i>content...</i>
<i><pre>content...</pre></i>
</pre></blockquote>

<p>In the example above, the pathname given after the POST keyword
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.</p>
uncompressed.

<p>A typical reply from the server might look something like this:</p>
A typical reply from the server might look something like this:

<blockquote><pre>
<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>content...</i>
<i><pre>content...</pre></i>
</pre></blockquote>

<p>The content type of the reply is always the same as the content type
of the request.</p>
The content type of the reply is always the same as the content type
of the request.

<h2>3.0 Fossil Synchronization Content</h2>
<h2 id="content">3.0 Fossil Synchronization Content</h2>

<p>A synchronization request between a client and server consists of
A synchronization request between a client and server consists of
one or more HTTP requests as described in the previous section.  This
section details the "x-fossil" content type.</p>
section details the "x-fossil" content type.

<h3>3.1 Line-oriented Format</h3>
<h3 id="lines">3.1 Line-oriented Format</h3>

<p>The x-fossil content type consists of zero or more "cards".  Cards
The x-fossil content type consists of zero or more "cards".  Cards
are separated by the newline character ("\n").  Leading and trailing
whitespace on a card is ignored.  Blank cards are ignored.</p>
whitespace on a card is ignored.  Blank cards are ignored.

<p>Each card is divided into zero or more space separated tokens.
Each card is divided into zero or more space separated tokens.
The first token on each card is the operator.  Subsequent tokens
are arguments.  The set of operators understood by servers is slightly
different from the operators understood by clients, though the two
are very similar.</p>
are very similar.

<h3>3.2 Login Cards</h3>
<h3 id="login">3.2 Login Cards</h3>

<p>Every message from client to server begins with one or more login
cards.  Each login card has the following format:</p>
Every message from client to server begins with one or more login
cards.  Each login card has the following format:

<blockquote>
<b>login</b>  <i>userid  nonce  signature</i>
<pre><b>login</b>  <i>userid  nonce  signature</i></pre>
</blockquote>

<p>The userid is the name of the user that is requesting service
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.</p>
the concatenation of the nonce and the users password.

<p>For each login card, the server looks up the user and verifies
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.</p>
specified user.

<p>Privileges are cumulative.  There can be multiple successful
login cards.  The session privileges are the bit-wise OR of the
privileges of each individual login.</p>
Privileges are cumulative.  There can be multiple successful
login cards.  The session privilege is the union of all
privileges from all login cards.

<h3>3.3 File Cards</h3>
<h3 id="file">3.3 File Cards</h3>

<p>Artifacts are transferred using either "file" cards, or "cfile"
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".
</p>

<h4>3.3.1 Ordinary File Cards</h4>
<h4 id="ordinary-fc">3.3.1 Ordinary File Cards</h4>

<p>For sync protocols, artifacts are transferred using "file"
For sync protocols, artifacts are transferred using "file"
cards.  File cards come in two different formats depending
on whether the artifact is sent directly or as a delta from some
other artifact.</p>
on whether the artifact is sent directly or as a 
[./delta_format.wiki|delta] from some
other artifact.

<blockquote>
<b>file</b> <i>artifact-id size</i> <b>\n</b> <i>content</i><br>
<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>
</blockquote>
</pre>

<p>File cards are followed by in-line "payload" data.
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.
</p>


<p>The first argument of a file card is the ID of the artifact that
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 and second argument is
the ID of another artifact that is the source of the delta.</p>
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.

<p>File cards are sent in both directions: client to server and
File cards are sent in both directions: client to server and
server to client.  A delta might be sent before the source of
the delta, so both client and server should remember deltas
and be able to apply them when their source arrives.</p>
and be able to apply them when their source arrives.

<h4>3.3.2 Compressed File Cards</h4>
<h4 id="compressed-fc">3.3.2 Compressed File Cards</h4>

<p>A client that sends a clone protocol version "3" or greater will
A client that sends a clone protocol version "3" or greater will
receive artifacts as "cfile" cards while cloning.  This card was
introduced to improve the speed of the transfer of content by sending the
compressed artifact directly from the server database to the client.</p>
compressed artifact directly from the server database to the client.

<p>Compressed File cards are similar to File cards, sharing the same
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.</p>
some other artifact.

<blockquote>
<b>cfile</b> <i>artifact-id usize csize</i> <b>\n</b> <i>content</i><br>
<b>cfile</b> <i>artifact-id delta-artifact-id usize csize</i> <b>\n</b> <i>content</i><br>
</blockquote>
<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>

<p>The first argument of the cfile card is the ID of the artifact that
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.</p>
delta artifact.

<p>Unlike file cards, cfile cards are only sent in one direction during a
clone from server to client for clone protocol version "3" or greater.</p>
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>3.3.3 Private artifacts</h4>
<h4 id="private">3.3.3 Private artifacts</h4>

<p>"Private" content consist of artifacts that are not normally synced.
"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.
</p>

<p>Private content is marked by a "private" card:
Private content is marked by a "private" card:

<blockquote>
<b>private</b>
<pre><b>private</b></pre>
</blockquote>

<p>The private card has no arguments and must directly precede a
file card that contains the private content.</p>
The private card has no arguments and must directly precede a
file card that contains the private content.

<h4>3.3.4 Unversioned File Cards</h4>
<h4 id="uv-fc">3.3.4 Unversioned File Cards</h4>

<p>Unversioned content is sent in both directions (client to server and
Unversioned content is sent in both directions (client to server and
server to client) using "uvfile" cards in the following format:

<blockquote>
<b>uvfile</b> <i>name mtime hash size flags</i> <b>\n</b> <i>content</i>
<pre><b>uvfile</b> <i>name mtime hash size flags</i> <b>\n</b> <i>content</i></pre>
</blockquote>

<p>The <i>name</i> field is the name of the unversioned file.  The
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.

<p>The receiver should only accept the uvfile card if the hash and
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.

<p>A server should only accept uvfile cards if the login user has
A server will only accept uvfile cards if the login user has
the "y" write-unversioned permission.

<p>Servers send uvfile cards in response to uvgimme cards received from
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>3.4 Push and Pull Cards</h3>
<h3 id="push" name="pull">3.4 Push and Pull Cards</h3>

<p>Among the first cards in a client-to-server message are
Among the first cards in a client-to-server message are
the push and pull cards.  The push card tells the server that
the client is pushing content.  The pull card tells the server
that the client wants to pull content.  In the event of a sync,
both cards are sent.  The format is as follows:</p>
both cards are sent.  The format is as follows:

<blockquote>
<b>push</b> <i>servercode projectcode</i><br>
<pre>
<b>push</b> <i>servercode projectcode</i>
<b>pull</b> <i>servercode projectcode</i>
</blockquote>
</pre>

<p>The <i>servercode</i> argument is the repository ID for the
The <i>servercode</i> argument is the repository ID for the
client.  The <i>projectcode</i> is the identifier
of the software project that the client repository contains.
The projectcode for the client and server must match in order
for the transaction to proceed.</p>
for the transaction to proceed.

<p>The server will also send a push card back to the client
The server will also send a push card back to the client
during a clone.  This is how the client determines what project
code to put in the new repository it is constructing.</p>
code to put in the new repository it is constructing.

<p>The <i>servercode</i> argument is currently unused.
The <i>servercode</i> argument is currently unused.

<h3>3.5 Clone Cards</h3>
<h3 id="clones">3.5 Clone Cards</h3>

<p>A clone card works like a pull card in that it is sent from
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.</p>
two-argument format.

<blockquote>
<b>clone</b><br>
<pre>
<b>clone</b>
<b>clone</b> <i>protocol-version sequence-number</i>
</blockquote>
</pre>

<h4>3.5.1 Protocol 3</h4>

<p>The latest clients send a two-argument clone message with a
The latest clients send a two-argument clone message with a
protocol version of "3".   (Future versions of Fossil might use larger
protocol version numbers.)  Version "3" of the protocol enhanced version
"2" by introducing the "cfile" card which is intended to speed up clone
operations.  Instead of sending "file" cards, the server will send "cfile"
cards</p>
cards

<h4>3.5.2 Protocol 2</h4>

<p>The sequence-number sent is the number
The sequence-number sent is the number
of artifacts received so far.  For the first clone message, the
sequence number is 0.  The server will respond by sending file
cards for some number of artifacts up to the maximum message size.

<p>The server will also send a single "clone_seqno" card to the client
The server will also send a single "clone_seqno" card to the client
so that the client can know where the server left off.

<blockquote>
<pre>
<b>clone_seqno</b>  <i>sequence-number</i>
</blockquote>
</pre>

<p>The clone message in subsequent HTTP requests for the same clone
The clone message in subsequent HTTP requests for the same clone
operation will use the sequence-number from the
clone_seqno of the previous reply.</p>
clone_seqno of the previous reply.

<p>In response to an initial clone message, the server also sends the client
In response to an initial clone message, the server also sends the client
a push message so that the client can discover the projectcode for
this project.</p>
this project.

<h4>3.5.3 Legacy Protocol</h4>

<p>Older clients send a clone card with no argument.  The server responds
Older clients send a clone card with no argument.  The server responds
to a blank clone card by sending an "igot" card for every artifact in the
repository.  The client will then issue "gimme" cards to pull down all the
content it needs.

<p>The legacy protocol works well for smaller repositories (50MB with 50,000
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>3.6 Igot Cards</h3>
<h3 id="igot">3.6 Igot Cards</h3>

<p>An igot card can be sent from either client to server or from
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:</p>
of a particular artifact.  The format is:

<blockquote>
<pre>
<b>igot</b> <i>artifact-id</i> ?<i>flag</i>?
</blockquote>
</pre>

<p>The first argument of the igot card is the ID of the artifact that
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.</p>
using a gimme card in either the reply or in the next message.

<p>If the second argument exists and is "1", then the artifact
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>

<p>Zero or more "uvigot" cards are sent from server to client when
Zero or more "uvigot" cards are sent from server to client when
synchronizing unversioned content.  The format of a uvigot card is
as follows:

<blockquote>
<pre>
<b>uvigot</b> <i>name mtime hash size</i>
</blockquote>
</pre>

<p>The <i>name</i> argument is the name of an unversioned file.
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.

<p>When the server sees a "pragma uv-hash" card for which the hash
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.

<p>When a client receives a "uvigot" card, it checks to see if the
file needs to be transfered from client to server or from server to client.
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>3.7 Gimme Cards</h3>
<h3 id="gimme">3.7 Gimme Cards</h3>

<p>A gimme card is sent from either client to server or from server
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:</p>
artifact back to the sender.  The format of a gimme card is this:

<blockquote>
<pre>
<b>gimme</b> <i>artifact-id</i>
</blockquote>
</pre>

<p>The argument to the gimme card is the ID of the artifact that
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.</p>
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>

<p>Sync synchronizing unversioned content, the client may send "uvgimme"
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:

<blockquote>
<pre>
<b>uvgimme</b> <i>name</i>
</blockquote>
</pre>

<p>The <i>name</i> is the name of the unversioned file found on the
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>3.8 Cookie Cards</h3>
<h3 id="cookie">3.8 Cookie Cards</h3>

<p>A cookie card can be used by a server to record a small amount
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.</p>
is its payload.

<blockquote>
<pre>
<b>cookie</b> <i>payload</i>
</blockquote>
</pre>

<p>The client is not required to return the cookie to the server on
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.)</p>
its servercode as part of the cookie.)

<h3>3.9 Request-Configuration Cards</h3>
<h3 id="reqconfig">3.9 Request-Configuration Cards</h3>

<p>A request-configuration or "reqconfig" card is sent from client to
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.

<p>The reqconfig card is normally sent in response to the
The reqconfig card is normally sent in response to the
"fossil configuration pull" command.  The format is as follows:

<blockquote>
<pre>
<b>reqconfig</b> <i>configuration-name</i>
</blockquote>
</pre>

<p>As of 2018-06-04, the configuration-name must be one of the
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
537
538
539
540
541
542
543
544

545
546
547
548
549
550
551
609
610
611
612
613
614
615

616
617
618
619
620
621
622
623







-
+







<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> allow-symlinks
<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
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
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







-
+





-
+






-
+




-
+


-
+

-
+




-
+

-
+

-
+



-
+


-
+

-
+


-
+

-
+

-
+




-
+



-
+


-
-
+



-
-
+



-
+

-
-
+











-
+

-
+




-
-
+


-
+


-
+

-
-
+


-
+


-
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


-
+

-
-
+
+

-
+

-
+


-
+

-
+

-
+

-
+






-
+

-
+


-
+

-
+

-
+

-
+

-
-
+
+

-
+

-
+






-
+

-
+

-
+

-
+




-
+

-
+




-
+

-
+



-
+

-
+

-
+






-
+

-
+

-
+

-
+

-
+







<li> @reportfmt
<li> @user
<li> @concealed
<li> @shun
</ul></td></tr>
</table>

<p>New configuration-names are likely to be added in future releases of
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.

<p>The configuration-names that begin with an alphabetic character refer
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.

<p>If the configuration-name begins with "@", that refers to a class of
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.

<p>The @user and @concealed configuration items contain sensitive information
The @user and @concealed configuration items contain sensitive information
and are ignored for clients without sufficient privilege.

<h3>3.10 Configuration Cards</h3>
<h3 id="config">3.10 Configuration Cards</h3>

<p>A "config" card is used to send configuration information from client
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:

<blockquote>
<pre>
<b>config</b> <i>configuration-name size</i> <b>\n</b> <i>content</i>
</blockquote>
</pre>

<p>The server will only accept a config card if the user has
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.

<p>The content of the configuration item is used to overwrite the
The content of the configuration item is used to overwrite the
corresponding configuration data in the receiver.

<h3>3.11 Pragma Cards</h3>
<h3 id="pragma">3.11 Pragma Cards</h3>

<p>The client may try to influence the behavior of the server by
The client may try to influence the behavior of the server by
issuing a pragma card:

<blockquote>
<pre>
<b>pragma</i> <i>name value...</i>
</blockquote>
</pre>

<p>The "pragma" card has at least one argument which is the pragma name.
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.

<p>New pragma names may be added to the protocol from time to time
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.

<p>The following are the known pragma names as of 2016-08-03:
The following are the known pragma names as of 2019-06-30:

<ol>
<li><p><b>send-private</b>
<p>The send-private pragma instructs the server to send all of its
<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><p><b>send-catalog</b>
<p>The send-catalog pragma instructs the server to transmit igot
<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.</p>
the send-catalog pragma to be transmitted.

<li><p><b>uv-hash</b> <i>HASH</i>
<p>The uv-hash pragma is sent from client to server to provoke a
<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.</p>
cards on the next HTTP request.

<p>If a client sends a uv-hash pragma and does not receive back
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><p><b>uv-pull-only</b></i>
<p>A server sends the uv-pull-only pragma to the client in response
<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 transfered
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.</p>
permission.

<li><p><b>uv-push-ok</b></i>
<p>A server sends the uv-push-ok pragma to the client in response
<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 only be transfered
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.</p>
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>3.12 Comment Cards</h3>
<h3 id="comment">3.12 Comment Cards</h3>

<p>Any card that begins with "#" (ASCII 0x23) is a comment card and
is silently ignored.</p>
Any card that begins with "#" (ASCII 0x23) is a comment card and
is silently ignored.

<h3>3.13 Message and Error Cards</h3>
<h3 id="error">3.13 Message and Error Cards</h3>

<p>If the server discovers anything wrong with a request, it generates
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:</p>
operation.  An error card looks like this:

<blockquote>
<pre>
<b>error</b> <i>error-message</i>
</blockquote>
</pre>

<p>The error message is English text that is encoded in order to
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.</p>
the error message.

<p>The server can also send a message card that also prints a
The server can also send a message card that also prints a
message on the client console, but which is not an error:

<blockquote>
<pre>
<b>message</b> <i>message-text</i>
</blockquote>
</pre>

<p>The message-text uses the same format as an error message.
The message-text uses the same format as an error message.

<h3>3.14 Unknown Cards</h3>
<h3 id="unknown">3.14 Unknown Cards</h3>

<p>If either the client or the server sees a card that is not
described above, then it generates an error and aborts.</p>
If either the client or the server sees a card that is not
described above, then it generates an error and aborts.

<h2>4.0 Phantoms And Clusters</h2>
<h2 id="phantoms" name="clusters">4.0 Phantoms And Clusters</h2>

<p>When a repository knows that an artifact exists and knows the ID of
When a repository knows that an artifact exists and knows the ID of
that artifact, but it does not know the artifact content, then it stores that
artifact as a "phantom".  A repository will typically create a phantom when
it receives an igot card for an artifact that it does not hold or when it
receives a file card that references a delta source that it does not
hold.  When a server is generating its reply or when a client is
generating a new request, it will usually send gimme cards for every
phantom that it holds.</p>
phantom that it holds.

<p>A cluster is a special artifact that tells of the existence of other
A cluster is a special artifact that tells of the existence of other
artifacts.  Any artifact in the repository that follows the syntactic rules
of a cluster is considered a cluster.</p>
of a cluster is considered a cluster.

<p>A cluster is line oriented.  Each line of 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.</p>
strict lexicographical order.

<p>A cluster consists of one or more "M" cards followed by a single
A cluster consists of one or more "M" cards followed by a single
"Z" card.  Each M card holds an argument which is an artifact ID for an
artifact in the repository.  The Z card has a single argument which is the
lower-case hexadecimal representation of the MD5 checksum of all
preceding M cards up to and included the newline character that
occurred just before the Z that starts the Z card.</p>
occurred just before the Z that starts the Z card.

<p>Any artifact that does not match the specifications of a cluster
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.</p>
be in strict lexicographical order.

<h3>4.1 The Unclustered Table</h3>
<h3 id="unclustered">4.1 The Unclustered Table</h3>

<p>Every repository maintains a table named "<b>unclustered</b>"
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.</p>
of clusters to find every artifact in the repository.

<h2>5.0 Synchronization Strategies</h2>
<h2 id="strategies">5.0 Synchronization Strategies</h2>

<h3>5.1 Pull</h3>
<h3 id="pull-strategy">5.1 Pull</h3>

<p>A typical pull operation proceeds as shown below.  Details
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:</p>
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
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
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>

<p>These ten steps represent a single HTTP round-trip request.
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.</p>
client does to interpret the reply.

<p>During a pull, the client will keep sending HTTP requests
until it holds all artifacts that exist on the server.</p>
During a pull, the client will keep sending HTTP requests
until it holds all artifacts that exist on the server.

<p>Note that the server tries
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.</p>
described in step (6) if the reply becomes too large.

<p>Step (5) is the only way in which new clusters can be created.
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.</p>
number of clusters might be generated.

<h3>5.2 Push</h3>
<h3 id="push-stragegy">5.2 Push</h3>

<p>A typical push operation proceeds roughly as shown below.  As
with a pull, the actual implementation may vary slightly.</p>
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
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
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







-
+



-
+

-
+

-
+



-
+

-
+

-
+





-
+



-
+


-
+







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>

<p>As with a pull, the steps of a push operation repeat until the
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.</p>
size of the request reaches 1MB.

<h3>5.3 Sync</h3>
<h3 id="sync-strategy">5.3 Sync</h3>

<p>A sync is just a pull and a push that happen at the same time.
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.</p>
are combined with step (9) of a push.

<h3>5.4 Unversioned File Sync</h3>
<h3 id="uv-strategy">5.4 Unversioned File Sync</h3>

<p>"Unversioned files" are files held in the repository
"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.

<p>Unversioned files are identified by name and timestamp (mtime).
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.

<p>Unversioned files are synchronized using the
Unversioned files are synchronized using the
[/help?cmd=unversioned|fossil unversioned sync] command.

<p>A schematic of an unversioned file synchronization is as follows:
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
897
898
899
900
901
902
903
904

905
906
907
908

909
910

911
912
913
914
915
916
917
988
989
990
991
992
993
994

995
996
997
998

999
1000

1001
1002
1003
1004
1005
1006
1007
1008







-
+



-
+

-
+







    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>

<p>The last two steps might be repeated multiple
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>6.0 Summary</h2>
<h2 id="summary">6.0 Summary</h2>

<p>Here are the key points of the synchronization protocol:</p>
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.
944
945
946
947
948
949
950



































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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
<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.

Changes to www/tech_overview.wiki.

1

2
3
4
5
6
7
8
9
10
11

1



2
3
4
5
6
7
8
-
+
-
-
-







<title>Technical Overview</title>
<title>A Technical Overview of Fossil's Design & Implementation</title>
<h2 align="center">
A Technical Overview<br>Of The Design And Implementation<br>Of Fossil
</h2>

<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.
51
52
53
54
55
56
57
58

59
60
61
62
63
64
65
66
67
68
69
70
71
72







73
74

75
76
77

78
79
80
81

82
83
84
85
86
87
88
89

90
91
92
93

94
95
96
97
98
99
100
101
102
103
104

105
106
107
108
109
110

111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128


129
130
131
132
133




























134


135
136












137



138
139
140
141
142
143

144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162








163
164
165
166
167
168
169
170
171

172
173
174
175
176
177
178
179
180

181
182
183
184
185
186
187
48
49
50
51
52
53
54

55
56
57
58
59
60
61
62
63
64
65
66



67
68
69
70
71
72
73


74
75
76

77




78
79
80
81
82
83
84
85

86




87
88
89
90
91
92
93
94
95
96
97

98

99
100
101


102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122





123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153


154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174

175
176
177
178
179
180
181
182
183
184
185
186
187
188






189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204

205
206
207
208
209
210
211
212
213

214
215
216
217
218
219
220
221







-
+











-
-
-
+
+
+
+
+
+
+
-
-
+


-
+
-
-
-
-
+







-
+
-
-
-
-
+










-
+
-



-
-
+


















+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+





-
+













-
-
-
-
-
-
+
+
+
+
+
+
+
+








-
+








-
+







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/all | fossil settings] command only opens the configuration database
[/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 border="1" width="80%" cellpadding="0" align="center">
<tr>
<td width="33%" valign="top">
<table align="center">
<tr valign="bottom">
<th style="text-align:center">Configuration&nbsp;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">
<h3 align="center">Configuration Database<br>"~/.fossil"</h3>
<ul>
<td><ul>
<li>Global [/help/settings |settings]
<li>List of active repositories used by the [/help/all | all] command
</ul>
</ul></td>
</td>
<td width="34%" valign="top">
<h3 align="center">Repository Database<br>"<i>project</i>.fossil"</h3>
<ul>
<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>
</ul></td>
</td>
<td width="33%" valign="top">
<h3 align="center">Checkout Database<br>"_FOSSIL_"</h3>
<ul>
<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>
</ul></td>
</td>
</tr>
</table>

<a name='configdb'></a>
<h3>2.1 The Configuration Database</h3>
<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 ".fossil" and is
located in the user's home directory.  On Windows, the configuration
database is named "_fossil" (using an underscore as the first character
instead of a dot) and is located in the directory specified by the
LOCALAPPDATA, APPDATA, or HOMEPATH environment variables, in that order.
On Unix systems, the configuration database is named by the following
algorithm:

<table>
<tr><td>1. if environment variable FOSSIL_HOME exists
     <td>&nbsp;&rarr;&nbsp;<td>$FOSSIL_HOME/.fossil
<tr><td>2. if file ~/.fossil exists
     <td>&nbsp;&rarr;<td>~/.fossil
<tr><td>3. if environment variable XDG_CONFIG_HOME exists
    <td>&nbsp;&rarr;<td>$XDG_CONFIG_HOME/fossil.db
<tr><td>4. if the directory ~/.config exists
    <td>&nbsp;&rarr;<td>~/.config/fossil.db
<tr><td>5. Otherwise<td>&nbsp;&rarr;<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:
You can override this default location by defining the environment
variable FOSSIL_HOME pointing to an appropriate (writable) directory.

  *  %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 then
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, at the time of this writing,
the total size of all artifacts is over 2.0 GB but thanks to the
combined zlib and delta compression, that content only takes up
32 MB of space in the repository database, for a compression ratio
of about 64:1.  The average size of a content BLOB in the database
is around 500 bytes.
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 undeltaed artifacts can be extracted
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 undeleted and uncompressed
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.
284
285
286
287
288
289
290
291
292

293
294
295
296
297
298
299
318
319
320
321
322
323
324


325
326
327
328
329
330
331
332







-
-
+







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].

<a name='localdb'></a>
<h3>2.3 Checkout Databases</h3>
<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:

312
313
314
315
316
317
318
319

320
321
322
323
324
325
326
345
346
347
348
349
350
351

352
353
354
355
356
357
358
359







-
+







  *  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 filesystem.  (In the latter case,
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

Changes to www/th1-hooks.md.

34
35
36
37
38
39
40
41

42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

57
58
59
60
61
62
63
64
65
66
67
68
69
70
71

72
73
74
75
76
77
78
79
80

81
82
83
84
85
86
87
88
89
90
91
92
93
94

95
96
97
98
99
100
101
102
103
104
105
106
107
108

109
110
111
112
113
114
115
116
117
118
119
120
121
122

123
124
125
126
127
128
129
130
131
132
133
134
34
35
36
37
38
39
40

41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

56
57
58
59
60
61
62
63
64
65
66
67
68
69
70

71
72
73
74
75
76
77
78
79

80
81
82
83
84
85
86
87
88
89
90
91
92
93

94
95
96
97
98
99
100
101
102
103
104
105
106
107

108
109
110
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 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 name="cmdReturnCodes"></a>TH1 Hook Related Return Codes for Commands
<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 name="webReturnCodes"></a>TH1 Hook Related Return Codes for Web Pages
<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 name="triggerReturnCodes"></a>Triggering TH1 Return Codes from a Script
<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 name="command_hook"></a>TH1 command_hook Procedure
<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 name="command_notify"></a>TH1 command_notify Procedure
<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 name="webpage_hook"></a>TH1 webpage_hook Procedure
<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 name="webpage_notify"></a>TH1 webpage_notify Procedure
<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.

Changes to www/th1.md.

10
11
12
13
14
15
16
17
18


19
20
21
22
23
24
25
10
11
12
13
14
15
16


17
18
19
20
21
22
23
24
25







-
-
+
+







TH1 began as a minimalist re-implementation of the Tcl scripting language.
There was a need to test the SQLite library on Symbian phones, but at that
time all of the test cases for SQLite were written in Tcl and Tcl could not
be easily compiled on the SymbianOS.  So TH1 was developed as a cut-down
version of Tcl that would facilitate running the SQLite test scripts on
SymbianOS.

The testing of SQLite on SymbianOS was eventually accomplished by other
means.  But Fossil was first being designed at about the same time.
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.
38
39
40
41
42
43
44
45
46
47




48
49
50
51
52
53
54
55
56
57
58
59
60
61
62





63
64
65
66
67
68
69

70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92






93
94
95
96
97
98
99
100
101
102
103
104
105



106
107
108
109
110
111
112
113
114
115

116
117
118
119
120
121
122
123
124
125
126
127

128
129
130
131

132
133
134
135
136
137
138
139
140
141
142
143
144

145
146
147
148
149
150
151
38
39
40
41
42
43
44



45
46
47
48
49
50
51
52
53
54
55
56
57
58





59
60
61
62
63
64
65
66
67
68
69

70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87






88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103



104
105
106
107
108
109
110
111
112
113
114
115

116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155







-
-
-
+
+
+
+










-
-
-
-
-
+
+
+
+
+






-
+

















-
-
-
-
-
-
+
+
+
+
+
+










-
-
-
+
+
+









-
+












+




+













+







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.
Or, all text without double-quotes is a single token even if it includes
whitespace and semicolons.  Or, all text without nested {...} pairs is a
single token.
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"
        }
    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"`.
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 {$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]
    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 when TH1 is modeled has a very rich
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?

162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190





























191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217






























218
219
220
221
222
223
224
225
226
227

228


229
230
231
232
233
234
235

236


237
238
239
240
241
242
243

244
245
246
247
248
249
250
251






































































252

253
254
255
256
257
258
259
260
261
262

263
264
265
266
267
268
269
270
271
272






















273

274
275
276
277
278
279
280
281

282
283
284
285
286
287
288







289

290
291
292
293
294
295
296
297
298
299
300


















301

302
303
304
305
306

307
308
309

310
311
312
313
314
315
316

317
318
319
320
321
322
323
324

325
326
327
328
329
330
331
332

333
334
335
336
337
338
339
166
167
168
169
170
171
172






















173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201



























202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240

241
242
243
244
245
246
247
248
249
250

251
252
253
254
255
256
257
258
259
260

261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339

340
341
342
343
344
345
346
347
348
349

350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382

383
384
385
386
387
388
389
390

391
392
393
394
395
396
397
398
399
400
401
402
403
404
405

406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435

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







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+









-
+

+
+






-
+

+
+






-
+








+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+









-
+










+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+







-
+







+
+
+
+
+
+
+
-
+











+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+




-
+


-
+






-
+







-
+







-
+








TH1 Extended Commands
---------------------

There are many new commands added to TH1 and used to access the special
features of Fossil.  The following is a summary of the extended commands:

  *  anoncap
  *  anycap
  *  artifact
  *  checkout
  *  combobox
  *  date
  *  decorate
  *  dir
  *  enable\_output
  *  encode64
  *  getParameter
  *  glob\_match
  *  globalState
  *  hascap
  *  hasfeature
  *  html
  *  htmlize
  *  http
  *  httpize
  *  insertCsrf
  *  linecount
  *  markdown
  *  [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)
  *  puts
  *  query
  *  randhex
  *  redirect
  *  regexp
  *  reinitialize
  *  render
  *  repository
  *  searchable
  *  setParameter
  *  setting
  *  stime
  *  styleHeader
  *  styleFooter
  *  styleScript
  *  tclEval
  *  tclExpr
  *  tclInvoke
  *  tclIsSafe
  *  tclMakeSafe
  *  tclReady
  *  trace
  *  unversioned content
  *  unversioned list
  *  utime
  *  verifyCsrf
  *  wiki
  *  [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 name="anoncap"></a>TH1 anoncap Command
<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 name="anycap"></a>TH1 anycap Command
<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 name="artifact"></a>TH1 artifact Command
<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 name="checkout"></a>TH1 checkout Command
<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 name="combobox"></a>TH1 combobox Command
<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 name="date"></a>TH1 date Command
<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 name="decorate"></a>TH1 decorate Command
<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 name="dir"></a>TH1 dir Command
<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 name="enable_output"></a>TH1 enable\_output Command
<a id="enable_output"></a>TH1 enable\_output Command
------------------------------------------------------

  *  enable\_output BOOLEAN

Enable or disable sending output when the combobox, puts, or wiki
Enable or disable sending output when the combobox, copybtn, puts, or wiki
commands are used.

<a name="encode64"></a>TH1 encode64 Command
<a id="encode64"></a>TH1 encode64 Command
-------------------------------------------

  *  encode64 STRING

Encode the specified string using Base64 and return the result.

<a name="getParameter"></a>TH1 getParameter Command
<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 name="glob_match"></a>TH1 glob\_match Command
<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 name="globalState"></a>TH1 globalState Command
<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:

347
348
349
350
351
352
353
354

355


356
357
358
359
360
361
362

363
364
365
366
367
368
369
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







-
+

+
+






-
+







  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 name="hascap"></a>TH1 hascap Command
<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 name="hasfeature"></a>TH1 hasfeature Command
<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:

382
383
384
385
386
387
388
389

390
391
392
393
394
395
396

397
398
399
400
401
402
403
404

405
406
407
408
409
410
411
412
413
414
415
416
417

418
419
420
421
422
423
424
425

426
427
428
429
430
431
432
433

434
435
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
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







-
+






-
+







-
+












-
+







-
+







-
+







-
+








+
+
+
+
+
+
+
-
+




-
+
+

-
+











-
+







-
+











-
+








-
+






-
+






-
+








-
+



















-
+



-
+




-
+






-
+






-
+







-
+






-
+






-
+






+
+
+
+
+
+
+
-
+










-
+











-
+










-
+









-
+











-
+







-
+






-
+








-
+







-
+







-
+










+
+
+
+
+
+
+
+
-
+

















-
+









-
+








  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 name="html"></a>TH1 html Command
<a id="html"></a>TH1 html Command
-----------------------------------

  *  html STRING

Outputs the STRING escaped for HTML.

<a name="htmlize"></a>TH1 htmlize Command
<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 name="http"></a>TH1 http Command
<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 name="httpize"></a>TH1 httpize Command
<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 name="insertCsrf"></a>TH1 insertCsrf Command
<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 name="linecount"></a>TH1 linecount Command
<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 name="markdown"></a>TH1 markdown Command
<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 name="puts"></a>TH1 puts Command
<a id="puts"></a>TH1 puts Command
-----------------------------------

  *  puts STRING

Outputs the STRING unchanged.
Outputs the STRING unchanged, where "unchanged" might, depending on
the context, mean "with some characters escaped for HTML."

<a name="query"></a>TH1 query Command
<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 name="randhex"></a>TH1 randhex Command
<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 name="redirect"></a>TH1 redirect Command
<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 name="regexp"></a>TH1 regexp Command
<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 name="reinitialize"></a>TH1 reinitialize Command
<a id="reinitialize"></a>TH1 reinitialize Command
---------------------------------------------------

  *  reinitialize ?FLAGS?

Reinitializes the TH1 interpreter using the specified flags.

<a name="render"></a>TH1 render Command
<a id="render"></a>TH1 render Command
---------------------------------------

  *  render STRING

Renders the TH1 template and writes the results.

<a name="repository"></a>TH1 repository Command
<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 name="searchable"></a>TH1 searchable Command
<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]} {...}
    if {[searchable c d t w]} {...}

But to see if ANY document class is searchable:

        if {[searchable cdtw]} {...}
    if {[searchable cdtw]} {...}

This command is useful for enabling or disabling a "Search" entry on the
menu bar.

<a name="setParameter"></a>TH1 setParameter Command
<a id="setParameter"></a>TH1 setParameter Command
---------------------------------------------------

  *  setParameter NAME VALUE

Sets the value of the specified query parameter.

<a name="setting"></a>TH1 setting Command
<a id="setting"></a>TH1 setting Command
-----------------------------------------

  *  setting name

Gets and returns the value of the specified setting.

<a name="stime"></a>TH1 stime Command
<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 name="styleHeader"></a>TH1 styleHeader Command
<a id="styleHeader"></a>TH1 styleHeader Command
-------------------------------------------------

  *  styleHeader TITLE

Render the configured style header for the selected skin.

<a name="styleFooter"></a>TH1 styleFooter Command
<a id="styleFooter"></a>TH1 styleFooter Command
-------------------------------------------------

  *  styleFooter

Render the configured style footer for the selected skin.

<a name="styleScript"></a>TH1 styleScript Command
<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 name="tclEval"></a>TH1 tclEval Command
<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 name="tclExpr"></a>TH1 tclExpr Command
<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 name="tclInvoke"></a>TH1 tclInvoke Command
<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 name="tclIsSafe"></a>TH1 tclIsSafe Command
<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 name="tclMakeSafe"></a>TH1 tclMakeSafe Command
<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 name="tclReady"></a>TH1 tclReady Command
<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 name="trace"></a>TH1 trace Command
<a id="trace"></a>TH1 trace Command
-------------------------------------

  *  trace STRING

Generates a TH1 trace message if TH1 tracing is enabled.

<a name="unversioned_content"></a>TH1 unversioned content Command
<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 name="unversioned_list"></a>TH1 unversioned list Command
<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 name="utime"></a>TH1 utime Command
<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 name="verifyCsrf"></a>TH1 verifyCsrf Command
<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 name="wiki"></a>TH1 wiki Command
<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 name="th1Eval"></a>Tcl th1Eval Command
<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 name="th1Expr"></a>Tcl th1Expr Command
<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
2
3
4
5
6
7
8
9
1

2
3
4
5
6
7
8

-







<title>Thoughts On The Design Of The Fossil DVCS</title>
<h1 align="center">Thoughts On The Design Of The Fossil DVCS</h1>

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?

Changes to www/tickets.wiki.

8
9
10
11
12
13
14
15

16
17
18
19
20
21
22

23
24
25
26
27
28
29
30
31
32

33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

53
54
55
56
57
58
59
8
9
10
11
12
13
14

15
16
17
18
19
20
21

22
23
24
25
26
27
28
29
30
31

32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

52
53
54
55
56
57
58
59







-
+






-
+









-
+



















-
+







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 timestamp for when the change occurred
<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 timestamp order.
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 realtime.  To facility reporting and display
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:

<blockquote><verbatim>
<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
76
77
78
79
80
81
82
83

84
85
86
87
88
89
90
76
77
78
79
80
81
82

83
84
85
86
87
88
89
90







-
+







  -- 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></blockquote>
</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.

130
131
132
133
134
135
136
137
138


139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159

160
161
162
163
164
165
166
130
131
132
133
134
135
136


137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158

159
160
161
162
163
164
165
166







-
-
+
+




















-
+








<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 timestamp on the ticket
change artifact, expressed as a julian day number.  If the ticket
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 timestamp order.  As each ticket change artifact is
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
192
193
194
195
196
197
198
199

192
193
194
195
196
197
198

199







-
+
support the "new-style" tickets.

The TICKETCHNG table was added to support new-style tickets.  In the new
style, comment text is stored with the "icomment" (for "Incremental Comment")
key and appears separately, and with its on mimetype, in multiple rows
of the TICKETCHNG table.  It then falls to the TH1 script code on the
View Ticket Page to query the TICKETCHNG table and extract and format
the various comments in timestamp order.
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).

Changes to www/uitest.html.

10
11
12
13
14
15
16
17

18
19
20
21
22
23
24
10
11
12
13
14
15
16

17
18
19
20
21
22
23
24







-
+







//////////////////////////////////////////////////////////////////////////
{
 url: "timeline",
 desc:
   "Simple timeline of most recent check-ins. Verify that all submenus work."
},
{
 url: "timeline?n=125",
 url: "timeline?n1=125",
 desc:
   "Timeline with 125 entries.  Verify that submenus preserve the entry count."
},
{
 url: "wiki",
 desc:
   "The wiki homepage"

Changes to 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
1

2
3


4
5
6



7
8
9
10
11
12



13
14
15
16
17
18
19
20
21

22
23
24

25
26
27
28
29
30
31
32
33
34


35
36

37

38
39
40
41

42
43
44



45
46
47
48
49
50
51
52

53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75

76
77
78
79
80
81

82
83
84
85
86

87
88
89



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>
<h1 align="center">Unversioned Content</h1>

"Unversioned content" or "unversioned files" are
files stored in a Fossil repository without history.
Only the newest version of each unversioned file is retained.
files stored in a Fossil repository without history, meaning
it retains the newest version of each such file, and that alone.

Though history is omitted, unversioned content is synced between
respositories.  In the event of a conflict during a sync, the most recent
version of each unversioned file is retained and older versions are discarded.
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.
The [https://www.fossil-scm.org/fossil/uv/download.html|download] page of
the self-hosting Fossil repository is stored as unversioned
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:  "http://domain/cgi-script/<b>uv</b>/<i>FILENAME</i>".
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 mimetype is inferred from the filename suffix.
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 is synced between respositories, though not by default.
Special commands or command-line options are required.
Unversioned content does not sync between repositories by default.
One must request it via commands such as:
Unversioned content can be synced using the following commands:

<blockquote><pre>
<pre>
fossil sync <b>-u</b>
fossil clone <b>-u</b> <i>URL local-repo-name</i>
fossil unversioned sync
</pre></blockquote>
</pre>

The [/help?cmd=sync|fossil sync] and [/help?cmd=clone|fossil clone]
commands will synchronize unversioned content if and only if the
"-u" (or "--unversioned") command-line option is supplied.  The
[/help?cmd=unversioned|fossil unversioned sync] command will synchronize the
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:

<blockquote><pre>
<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 hash of uncompressed content
  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></blockquote>
</pre>

Fossil does not create the table ahead of need.
If there are no unversioned files in the repository, then the
"unversioned" table does not necessarily exist.
A simple way to purge all unversioned content from a repository
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:

<blockquote><pre>
<pre>
fossil sql "DROP TABLE unversioned; VACUUM;"
</pre></blockquote>
</pre>

No delta compression is performed on unversioned files, since there is no
Lacking history for unversioned files, Fossil does not attempt delta
history to delta against.
compression on them.

Unversioned content is exchanged between servers as whole, uncompressed
files (though the content does get compressed when the overall HTTP message
payload is compressed).  SHA1 hash exchanges are used to avoid sending
content over the wire unnecessarily.  See the
[./sync.wiki|synchronization protocol documentation] for further
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 &amp; 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.

Changes to 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

128
129
130
131

132
1
2
3
4
5
6








7


8

9
10
11
12

13
14
15
16

17
18
19
20
21

22
23
24
25
26
27
28
29

30
31
32
33

34
35
36
37
38

39
40
41
42
43

44
45
46
47

48
49
50
51

52
53
54
55
56

57
58
59
60

61
62
63
64

65
66
67
68
69
70
71
72
73

74
75
76
77

78
79
80
81

82
83
84
85
86

87
88
89
90

91
92
93
94

95
96
97
98

99
100
101
102

103
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.
<style>
.exbtn {
  border: 1px solid #000;
  margin: 1ex;
  border-radius: 1ex;
  padding: 0 1ex;
  background-color: #eee;
}

</style>

  *  <a target='_blank' class='exbtn'
     href='$ROOT/timeline?y=ci&n=100'>Example</a>
     href='$ROOT/timeline?y=ci&n1=100'>(Example)</a> &rarr;
     100 most recent check-ins.

  *  <a target='_blank' class='exbtn'
     href='$ROOT/finfo?name=src/file.c'>Example</a>
     href='$ROOT/finfo?name=src/file.c'>(Example)</a> &rarr;
     All changes to the <b>src/file.c</b> source file.

  *  <a target='_blank' class='exbtn'
     href='$ROOT/timeline?n=200&uf=0c3c2d086a'>Example</a>
     href='$ROOT/timeline?n1=200&uf=0c3c2d086a'>(Example)</a> &rarr;
     All check-ins using a particular version of the <b>src/file.c</b>
     source file.

  *  <a target='_blank' class='exbtn'
     href='$ROOT/timeline?n=11&y=ci&c=2014-01-01'>Example</a>
     href='$ROOT/timeline?n1=11&y=ci&c=2014-01-01'>(Example)</a> &rarr;
     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> &rarr;
     All check-ins derived from the most recent release.

  *  <a target='_blank' class='exbtn'
     href='$ROOT/timeline?n=11&y=ci&c=2014-01-01&v=1'>Example</a>
     href='$ROOT/timeline?n1=11&y=ci&c=2014-01-01&v=1'>(Example)</a> &rarr;
     The previous example augmented with file changes.

  *  <a target='_blank' class='exbtn'
     href='$ROOT/timeline?n=25&y=ci&a=1970-01-01'>Example</a>
     href='$ROOT/timeline?n1=25&y=ci&a=1970-01-01'>(Example)</a> &rarr;
     First 25 check-ins after 1970-01-01.  (The first 25 check-ins of
     the project.)

  *  <a target='_blank' class='exbtn'
     href='$ROOT/timeline?n=200&r=svn-import'>Example</a>
     href='$ROOT/timeline?n1=200&r=svn-import'>(Example)</a> &rarr;
     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?n=200&t=svn-import'>Example</a>
     href='$ROOT/timeline?n1=200&t=svn-import'>(Example)</a> &rarr;
     All check-ins of the "svn-import" branch only.

  *  <a target='_blank' class='exbtn'
     href='$ROOT/timeline?n=100&y=ci&ubg'>Example</a>
     href='$ROOT/timeline?n1=100&y=ci&ubg'>(Example)</a> &rarr;
     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>
     href='$ROOT/timeline?from=version-1.27&to=version-1.28'>(Example)</a> &rarr;
     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>
     href='$ROOT/timeline?namechng'>(Example)</a> &rarr;
     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>
     href='$ROOT/timeline?u=drh&c=2014-01-08&y=ci'>(Example)</a> &rarr;
     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>
     href='$ROOT/timeline?from=version-1.34&to=version-1.35&chng=src/timeline.c,src/doc.c'>(Example)</a> &rarr;
     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>&rarr;</b></big> (Hint:  In the pages above, click the graph nodes
     for any two check-ins or files to see a diff.)
     <big><b>&larr;</b></big>

  *  <a target='_blank' class='exbtn'
     href='$ROOT/search?s=interesting+pages'>Example</a>
     href='$ROOT/search?s=interesting+pages'>(Example)</a> &rarr;
     Full-text search for "interesting pages".

  *  <a target='_blank' class='exbtn'
     href='$ROOT/tree?ci=daff9d20621&type=tree'>Example</a>
     href='$ROOT/tree?ci=daff9d20621&type=tree'>(Example)</a> &rarr;
     All files for a particular check-in (daff9d20621480)

  *  <a target='_blank' class='exbtn'
     href='$ROOT/tree?ci=trunk&type=tree&mtime=1'>Example</a>
     href='$ROOT/tree?ci=trunk&type=tree&mtime=1'>(Example)</a> &rarr;
     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>
     href='$ROOT/fileage?name=svn-import'>(Example)</a> &rarr;
     Age of all files in the latest checking for branch "svn-import".

  *  <a target='_blank' class='exbtn'
     href='$ROOT/brlist'>Example</a>
     href='$ROOT/brlist'>(Example)</a> &rarr;
     Table of branches.  (Click on column headers to sort.)

  *  <a target='_blank' class='exbtn'
     href='$ROOT/stat'>Example</a>
     href='$ROOT/stat'>(Example)</a> &rarr;
     Overall repository status.

  *  <a target='_blank' class='exbtn'
     href='$ROOT/reports?type=ci&view=byuser'>Example</a>
     href='$ROOT/reports?type=ci&view=byuser'>(Example)</a> &rarr;
     Number of check-ins per committer.

  *  <a target='_blank' class='exbtn'
     href='$ROOT/reports?view=byfile'>Example</a>
     href='$ROOT/reports?view=byfile'>(Example)</a> &rarr;
     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>
       (Example)</a> &rarr;
     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>
     href='$ROOT/taglist'>(Example)</a> &rarr;
     List of tags on check-ins.

  *  <a target='_blank' class='exbtn'
     href='$ROOT/bigbloblist'>Example</a>
     href='$ROOT/bigbloblist'>(Example)</a> &rarr;
     The largest objects in the repository.

  *  <a target='_blank' class='exbtn'
     href='$ROOT/hash-collisions'>Example</a>
     href='$ROOT/hash-collisions'>(Example)</a> &rarr;
     Hash prefix collisions

  *  <a target='_blank' class='exbtn'
     href='$ROOT/sitemap'>Example</a>
     href='$ROOT/sitemap'>(Example)</a> &rarr;
     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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19










+
+







<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
28
29
30
31
32
33
34
35

36
37
38

39
40
41
42
43
44
45
46
47
48

49
50
51
52
53

54
55
56

57
58
59
60
61

62
63
64
65
66
67
68
30
31
32
33
34
35
36

37
38
39

40
41
42
43
44
45
46
47
48
49

50
51
52
53
54

55
56
57

58
59
60
61
62

63
64
65
66
67
68
69
70







-
+


-
+









-
+




-
+


-
+




-
+








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.

<blockquote>
<div class="indent">
<b>Key point:</b> <i>The Fossil website is just a running instance
of Fossil!
</blockquote>
</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>Drop-Dead Simple Startup</h2>
<h2>Very Simple Startup</h2>

To start using the built-in Fossil web interface on an existing Fossil
repository, simply type this:

   <b>fossil ui existing-repository.fossil</b>
<pre>fossil ui existing-repository.fossil</pre>

Substitute the name of your repository, of course.
The "ui" command will start a webserver running (it figures out an
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:

  <b>fossil ui</b>
<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.
95
96
97
98
99
100
101
102
103
104



105
106
107
108
109
110
111
112

113
114
115
116
117
118
119
97
98
99
100
101
102
103



104
105
106
107
108
109
110
111
112
113

114
115
116
117
118
119
120
121







-
-
-
+
+
+







-
+







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 uses simple and easy-to-remember
[/wiki_rules | wiki formatting rules] so you won't have to spend a lot
of time learning a new markup language.  And, as with tickets, all of
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 subteams within your project team.
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].
127
128
129
130
131
132
133
134










135
136
137
138
139
140
141
142
143
144
145
146
147
148
149


150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169


170
171
172
173
174
129
130
131
132
133
134
135

136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157



158
159

160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175



176
177

178
179
180
181







-
+
+
+
+
+
+
+
+
+
+












-
-
-
+
+
-
















-
-
-
+
+
-




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.
"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>#!/usr/local/bin/fossil
repository: /home/www/sample-project.fossil</verbatim>
  </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>80 stream tcp nowait.1000 root /usr/local/bin/fossil \
/usr/local/bin/fossil http /home/www/sample-project.fossil</verbatim>
  </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

Changes to www/whyusefossil.wiki.

1
2
3
4




5
6
7
8


9
10
11
12
13
14
15




1
2
3
4

5


6
7
8
9
10
11
12
13
14
-
-
-
-
+
+
+
+
-

-
-
+
+







<title>Why Use Fossil</title>
<h1 align='center'>Why You Should Use Fossil</h1>
<p align='center'><b>Or, if not Fossil, at least some kind of modern
version control<br>such as Git, Mercurial, or Subversion.</b></p>
<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>
<p align='center'>(Presented in outline form, for people in a hurry)</p>

<ol>
<li><p><b>Benefits of Version Control</b>
<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>
35
36
37
38
39
40
41
42
43
44



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78


79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281






















































































































































282
34
35
36
37
38
39
40



41
42
43


































44
45











































































































































































































46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196







-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

     <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>
<a name='definitions'></a>
<li><p><b>Definitions</b></p>
  <ul>

<h5 id="definitions">II. Definitions</h5>

  <li><p><b>Project</b> &rarr;
       a collection of computer files that serve some common
      purpose.  Often the project is a software application and the
      individual files are source code together with makefiles, scripts, and
      "README.txt" files.  Other examples of projects include books or
      manuals in which each chapter or section is held in a separate file.
     <ul>
     <li><p>Projects change and evolve.  The whole purpose of version
         control is to track and manage that evolution.
     <li><p>Most projects contain many files, but it is possible
         to have a project consisting of just a single file.
     <li><p>Fossil requires that
         all the files for a project must be collected into a single
         directory hierarchy - a single folder possibly with layers
         of subfolders.  Fossil is not a good choice for managing a
         project that has files scattered hither and yon all over
         the disk.  In other words, Fossil only works for projects
         where the files are laid out such that they can be archived
         into a ZIP file or tarball.
     </ul>
  <li><p><b>Repository</b> &rarr;
      (also called "repo") a single file that contains
      all historical versions of all files in a project.  A repo is similar
      to a ZIP archive in that it is a single file that stores compressed
      versions of many other files.  Files can be extracted from the
      repo and new files can be added to the repo, just as with a ZIP
      archive.  But a repo has other capabilities above and beyond
      what a ZIP archive can do.
      <ul>
      <li><p>Fossil does not care what you name your repository files,
          though names ending with ".fossil" are recommended.
      <li><p>A single project typically has multiple, redundant repositories on
      separate machines.
      <li><p>All repositories stay synchronized with one another by exchanging
<div class="indent">Moved to [./glossary.md | a separate document].</div>

          information via HTTP or SSH.
      <li><p>All repos for a single project redundantly store all information
          about that project.  So if any one repo is lost due to a disk
          crash, all content is preserved in the surviving repos.
      <li><p>The usual arrangement is one repository per user.  And since
          most users these days have their own computer, that means one
          repository per computer.  But this is not a requirement.  It is
          ok to have multiple copies of the same repository on the same
          computer.
      <li><p>Fossil works fine with just a single copy of the repository.
          But in that case there is no redundancy.  If that one repository
          file is lost due to a hardware malfunction, then there is no way
          to recover the project.
      <li><p>Best practice is to keep all repositories for a user in a single
          folder.  Folders such as "~/Fossils" or "%USERPROFILE%\Fossils"
          are recommended.  Fossil itself does not care where the repositories
          are stored.  Nor does Fossil require repositories to be
          kept in the same folder.  But it is easier to organize your work
          if all repositories are kept in the same place.
      </ul>
  <li><p><b>Check-out</b> &rarr;
      a set of files that have been extracted from a
      repository and that represent a particular version or snapshot of
      the project.
      <ul>
      <li><p>Check-outs must be on the same computer as the repository from
          which they are extracted.  This is just like with a ZIP archive:
          one must have the ZIP archive file on the local machine before
          extracting files from ZIP archive.
      <li><p>There can be multiple check-outs (in different folders) from
          the same repository.
      <li><p>The repository must be on the same computer as the check-out, but
          the relative locations of the repo and the check-out are arbitrary.
          The repository may be located inside the folder holding the
          check-out, but it certainly does not have to be and usually is
          not.
      <li><p>A special file exists in every check-out that tells Fossil from
      which repository the check-out was extracted, and which version of
      the project the check-out represents.  This is the ".fslckout" file
      on unix systems or the "_FOSSIL_" file on Windows.
      </ul>
  <li><p><b>Check-in</b> &rarr;
      another name for a particular version of the project.
      A check-in is a collection of files inside of a repository that
      represent a snapshot of the project for an instant in time.
      Check-ins exist only inside of the repository.  This contrasts with
      a check-out which is a collection of files outside of the repository.
      <ul>
      <li><p>Every check-out knows the check-in from which it was derived.
          But check-outs might have been edited and so might not exactly
          match their associated check-in.
      <li><p>Check-ins are immutable.  They can never be changed.  But
          check-outs are collections of ordinary files on disk.  The
          files of a check-out can be edited just like any other file.
      <li><p>A check-in can be thought of as an historical snapshot of a
          check-out.
      <li><p>"Check-in", "version", "snapshot", and "revision" are synonyms.
      <li><p> When used as a noun, the word "commit" is another synonym
          for "check-in".  When used as a verb, the word "commit"
          means to create a new check-in.
      </ul>
  </ul>
<li><p><b>Basic Fossil commands</b>
  <ul>
  <li><p><b>clone</b> &rarr;
      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> &rarr;
      Create a new check-out from a repository on the local machine.
  <li><p><b>update</b> &rarr;
      Modify an existing check-out so that it is derived from a
      different version of the same project.
  <li><p><b>commit</b> &rarr;
      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> &rarr;
      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> &rarr;
      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> &rarr;
      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> &rarr;
      Do both a "push" and a "pull" at the same time.
  <li><p><b>add</b> &rarr;
      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> &rarr;
      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>
<li><p><b>The history of a project is a Directed Acyclic Graph (DAG)</b>
  <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 X is derived from check-in Y 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> &rarr;
      combines the work of multiple check-ins into
      a single check-out.  That check-out can then be committed to create
      a new 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".
          Conceptually, the primary parent shows the main line of
          development.  Content from the secondary 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> &rarr;
      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.
       <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>
<li><p><b>Why version control is important (reprise)</b>
  <ol type="A">
  <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>
<h5>III. Basic Fossil commands</h5>

 <ul>
 <li><p><b>clone</b> &rarr;
     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> &rarr;
     Create a new check-out from a repository on the local machine.
 <li><p><b>update</b> &rarr;
     Modify an existing check-out so that it is derived from a
     different version of the same project.
 <li><p><b>commit</b> &rarr;
     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> &rarr;
     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> &rarr;
     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> &rarr;
     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> &rarr;
     Do both a "push" and a "pull" at the same time.
 <li><p><b>add</b> &rarr;
     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> &rarr;
     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> &rarr;
     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> &rarr;
     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
1
2
3
4
5
6
7
8

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

33
34
35
36
37
38
39
40








-
+
+



+
+

















-
+







<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.
   *  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 timestamp.
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.
54
55
56
57
58
59
60
61

62
63
64
65
66

























57
58
59
60
61
62
63

64
65




66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90







-
+

-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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</h2>
<h2>Bug-reports and check-in comments and Forum messages</h2>

The comments on check-ins 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.
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>
&nbsp;&nbsp;(1) read the_file content into a buffer<br>
&nbsp;&nbsp;(2) rename the_file the_file.bak<br>
&nbsp;&nbsp;(3) write buffer content to new file, the_file<br>
&nbsp;&nbsp;(4) del the_file.bak (or leave it)<br>
&nbsp;&nbsp;(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.