Fossil

Check-in [270d2fb9]
Login

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

Overview
Comment:Merged trunk
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | commonmark-markdown
Files: files | file ages | folders
SHA3-256:270d2fb95ce3ba86fcf5427bcfaa463cdeb79a4e63784faf6f3e37eaf23bf564
User & Date: mjanssen 2018-02-12 08:50:09
Context
2018-06-06
10:39
Merged trunk Leaf check-in: a253fba0 user: mjanssen tags: commonmark-markdown
2018-02-12
08:50
Merged trunk check-in: 270d2fb9 user: mjanssen tags: commonmark-markdown
2018-02-10
16:24
Add the cgi_csrf_safe() routine as a supplimental defense against cross-site request forgery attacks. check-in: 047802a3 user: drh tags: trunk
2017-10-04
12:07
Don't duplicate title in body check-in: 5ab4db5a user: mpcjanssen tags: commonmark-markdown
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to Makefile.in.

34
35
36
37
38
39
40

41
42
43
44
45
46
47
48
49
50
51
52
53





















TCC = @CC@

#### Tcl shell for use in running the fossil testsuite.  If you do not
#    care about testing the end result, this can be blank.
#
TCLSH = tclsh


LIB =	@LDFLAGS@ @EXTRA_LDFLAGS@ @LIBS@
BCCFLAGS =	@CPPFLAGS@ @CFLAGS@
TCCFLAGS =	@EXTRA_CFLAGS@ @CPPFLAGS@ @CFLAGS@ -DHAVE_AUTOCONFIG_H -D_HAVE_SQLITE_CONFIG_H
INSTALLDIR = $(DESTDIR)@prefix@/bin
USE_SYSTEM_SQLITE = @USE_SYSTEM_SQLITE@
USE_LINENOISE = @USE_LINENOISE@
USE_SEE = @USE_SEE@
FOSSIL_ENABLE_MINIZ = @FOSSIL_ENABLE_MINIZ@

include $(SRCDIR)/main.mk

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




























>













>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
TCC = @CC@

#### Tcl shell for use in running the fossil testsuite.  If you do not
#    care about testing the end result, this can be blank.
#
TCLSH = tclsh

CFLAGS = @CFLAGS@
LIB =	@LDFLAGS@ @EXTRA_LDFLAGS@ @LIBS@
BCCFLAGS =	@CPPFLAGS@ @CFLAGS@
TCCFLAGS =	@EXTRA_CFLAGS@ @CPPFLAGS@ @CFLAGS@ -DHAVE_AUTOCONFIG_H -D_HAVE_SQLITE_CONFIG_H
INSTALLDIR = $(DESTDIR)@prefix@/bin
USE_SYSTEM_SQLITE = @USE_SYSTEM_SQLITE@
USE_LINENOISE = @USE_LINENOISE@
USE_SEE = @USE_SEE@
FOSSIL_ENABLE_MINIZ = @FOSSIL_ENABLE_MINIZ@

include $(SRCDIR)/main.mk

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

reconfig:
	@AUTOREMAKE@

# 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

Changes to VERSION.

1
2.4
|
1
2.5

Changes to ajax/js/whajaj.js.

145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
    "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 paramater 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







|







145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
    "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

Changes to auto.def.

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

25
26
27
28
29
30
31
..
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
..
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
...
130
131
132
133
134
135
136
137
138
139





140
141
142
143
144
145
146
...
357
358
359
360
361
362
363



364
365
366
367
368
369
370
...
462
463
464
465
466
467
468

469
470
471
472
473
474
475
    with-zlib:path|auto|tree
                         => {Look for zlib 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=0  => {Enable legacy behavior for mv/rm (skip checkout files)}
    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-see=0           => {Enable the SQLite Encryption Extension (SEE)}
    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}

    json=0               => {Build with fossil JSON API enabled}
}

# sqlite wants these types if possible
cc-with {-includes {stdint.h inttypes.h}} {
    cc-check-types uint32_t uint16_t int16_t uint8_t
}
................................................................................

# Use pread/pwrite system calls in place of seek + read/write if possible
define USE_PREAD [cc-check-functions pread]

# Find tclsh for the test suite. Can't yet use jimsh for this.
cc-check-progs tclsh

define EXTRA_CFLAGS ""
define EXTRA_LDFLAGS ""
define USE_SYSTEM_SQLITE 0
define USE_LINENOISE 0
define FOSSIL_ENABLE_MINIZ 0
define USE_SEE 0

# This procedure is a customized version of "cc-check-function-in-lib",
................................................................................
    # 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_prepare_v3() can be found as well. If we can find open() but
      # not prepare_v3(), 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_prepare_v3 sqlite3 $extralibs]} {
          user-error "system sqlite3 too old (require >= 3.20.0)"
        }

        # Success. Update symbols and return.
        #
        define USE_SYSTEM_SQLITE 1
        define-append LIBS -lsqlite3
        define-append LIBS $extralibs
................................................................................

if {[string match *-solaris* [get-define host]]} {
    define-append EXTRA_CFLAGS {-D_XOPEN_SOURCE=500 -D__EXTENSIONS__}
}

if {[opt-bool fossil-debug]} {
    define-append EXTRA_CFLAGS -DFOSSIL_DEBUG
    define CFLAGS {-g -O0}
    msg-result "Debugging support enabled"
}






if {[opt-bool with-see]} {
    define-append EXTRA_CFLAGS -DUSE_SEE
    define USE_SEE 1
    msg-result "Enabling encryption support"
}

................................................................................
                /usr /usr/local /usr/share /opt/local]
            set msg "on your system"
        }
    } else {
        array set tclconfig [parse-tclconfig-sh $tclpath]
        set msg "at $tclpath"
    }



    if {![info exists tclconfig(TCL_INCLUDE_SPEC)]} {
        user-error "Cannot find Tcl $msg"
    }
    set tclstubs [opt-bool with-tcl-stubs]
    if {$tclprivatestubs} {
        define FOSSIL_ENABLE_TCL_PRIVATE_STUBS
        define USE_TCL_STUBS
................................................................................
    if {[is_mingw]} {
        define-append LIBS -lwsock32
    }
}
cc-check-functions utime
cc-check-functions usleep
cc-check-functions strchrnul


# Check for getloadavg(), and if it doesn't exist, define FOSSIL_OMIT_LOAD_AVERAGE
if {![cc-check-functions getloadavg]} {
  define FOSSIL_OMIT_LOAD_AVERAGE 1
  msg-result "Load average support unavailable"
}








|








>







 







|







 







|
|



|
|







 







|


>
>
>
>
>







 







>
>
>







 







>







9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
..
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
..
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
...
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
...
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
...
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
    with-zlib:path|auto|tree
                         => {Look for zlib 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=0  => {Enable legacy behavior for mv/rm (skip checkout files)}
    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-see=0           => {Enable the SQLite Encryption Extension (SEE)}
    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}
}

# sqlite wants these types if possible
cc-with {-includes {stdint.h inttypes.h}} {
    cc-check-types uint32_t uint16_t int16_t uint8_t
}
................................................................................

# Use pread/pwrite system calls in place of seek + read/write if possible
define USE_PREAD [cc-check-functions pread]

# Find tclsh for the test suite. Can't yet use jimsh for this.
cc-check-progs tclsh

define EXTRA_CFLAGS "-Wall"
define EXTRA_LDFLAGS ""
define USE_SYSTEM_SQLITE 0
define USE_LINENOISE 0
define FOSSIL_ENABLE_MINIZ 0
define USE_SEE 0

# This procedure is a customized version of "cc-check-function-in-lib",
................................................................................
    # 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_vtab_collation() can be found as well. If we can find open() but
      # not vtab_collation(), 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_vtab_collation sqlite3 $extralibs]} {
          user-error "system sqlite3 too old (require >= 3.22.0)"
        }

        # Success. Update symbols and return.
        #
        define USE_SYSTEM_SQLITE 1
        define-append LIBS -lsqlite3
        define-append LIBS $extralibs
................................................................................

if {[string match *-solaris* [get-define host]]} {
    define-append EXTRA_CFLAGS {-D_XOPEN_SOURCE=500 -D__EXTENSIONS__}
}

if {[opt-bool fossil-debug]} {
    define-append EXTRA_CFLAGS -DFOSSIL_DEBUG
    define CFLAGS {-g -O0 -Wall}
    msg-result "Debugging support enabled"
}

if {[opt-bool no-opt]} {
    define CFLAGS {-g -O0 -Wall}
    msg-result "Builting without compiler optimization"
}

if {[opt-bool with-see]} {
    define-append EXTRA_CFLAGS -DUSE_SEE
    define USE_SEE 1
    msg-result "Enabling encryption support"
}

................................................................................
                /usr /usr/local /usr/share /opt/local]
            set msg "on your system"
        }
    } else {
        array set tclconfig [parse-tclconfig-sh $tclpath]
        set msg "at $tclpath"
    }
    if {[opt-bool static]} {
        set tclconfig(TCL_LD_FLAGS) { }
    }
    if {![info exists tclconfig(TCL_INCLUDE_SPEC)]} {
        user-error "Cannot find Tcl $msg"
    }
    set tclstubs [opt-bool with-tcl-stubs]
    if {$tclprivatestubs} {
        define FOSSIL_ENABLE_TCL_PRIVATE_STUBS
        define USE_TCL_STUBS
................................................................................
    if {[is_mingw]} {
        define-append LIBS -lwsock32
    }
}
cc-check-functions utime
cc-check-functions usleep
cc-check-functions strchrnul
cc-check-functions pledge

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

Changes to autosetup/system.tcl.

203
204
205
206
207
208
209
210
211
212

213
214
215
216
217
218
219
					set cond [expr {!$cond}]
				}
			}
			continue
		}
		lappend result $line
	}
	writefile $out [string map $mapping [join $result \n]]\n

	msg-result "Created [relative-path $out] from [relative-path $template]"

}

# build/host tuples and cross-compilation prefix
set build [opt-val build]
define build_alias $build
if {$build eq ""} {
	define build [config_guess]







|
<
|
>







203
204
205
206
207
208
209
210

211
212
213
214
215
216
217
218
219
					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]"
    }
}

# build/host tuples and cross-compilation prefix
set build [opt-val build]
define build_alias $build
if {$build eq ""} {
	define build [config_guess]

Added skins/ardoise/README.md.































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
## Ardoise theme

A black and grey skin ("Ardoise" is the french word for slate).

The skin includes custom icons for the file browser and the WYSIWYG editor, which are embedded directly in the css as base64 blobs. For convenience, they are also provided as standalone files in the images subdirectory.

This skin was contributed by Antoine Chavasse.

This theme is loosely based upon, and still contains some elements from the Blitz theme by James Moger.

This theme embeds & uses a modified copy of [Normalize 3.0.2](https://necolas.github.io/normalize.css/) which is distributed under an [MIT license](https://github.com/necolas/normalize.css/blob/master/LICENSE.md).

This theme embeds & uses a modified copy of [Skeleton](http://getskeleton.com) which is distributed under an [MIT license](https://github.com/dhg/Skeleton/blob/master/LICENSE.md).

The sass version of Skeleton used in this project was made by [Seth Coelen](https://github.com/whatsnewsaes).

Added skins/ardoise/css.txt.























































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
@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 {
  border: 0
}
ol,
p,
ul {
  margin-top: 0
}
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
main,
menu,
nav,
pre > code,
section,
summary {
  display: block
}
ul.browser li.dir,
ul.browser li.file {
  background-position: 0 center;
  padding-left: 22px;
  padding-top: 2px
}
.container,
.filetree a,
.filetree li,
.filetree ul ul,
.mainmenu ul,
sub,
sup {
  position: relative
}
.filetree .dir > div.filetreeline > a,
ul.browser li.dir {
  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
}
audio:not([controls]) {
  display: none;
  height: 0
}
.filetree li.last>ul:before,
.filetree ul.collapsed,
[hidden],
template {
  display: none
}
a {
  background-color: transparent;
  color: #ff8000;
  text-decoration: unset
}
a:active,
a:hover,
pre.udiff:focus,
table.sbsdiffcols:focus {
  outline: 0
}
abbr[title] {
  border-bottom: 1px dotted
}
b,
optgroup,
strong,
td.usetupEditLabel {
  font-weight: 700
}
mark {
  background: #ff0
}
small {
  font-size: 80%
}
sub,
sup {
  font-size: 75%;
  line-height: 0;
  vertical-align: baseline
}
sup {
  top: -.5em
}
sub {
  bottom: -.25em
}
svg:not(:root) {
  overflow: hidden
}
figure {
  margin: 1em 40px
}
hr {
  height: 0;
  margin-top: 3rem;
  margin-bottom: 3.5rem;
  border-width: 0;
  border-top: 1px solid #626262
}
pre {
  overflow: auto
}
code,
kbd,
pre,
samp {
  font-family: monospace,monospace;
  font-size: 1em
}
button,
input,
optgroup,
select,
textarea {
  color: inherit;
  font: inherit;
  margin: 0
}
body,
h5 {
  line-height: 1.5
}
button {
  overflow: visible
}
button,
select {
  text-transform: none
}
button,
html input[type=button],
input[type=reset],
input[type=submit] {
  -webkit-appearance: button;
  cursor: pointer
}
button[disabled],
html input[disabled] {
  cursor: default
}
button::-moz-focus-inner,
input::-moz-focus-inner {
  border: 0;
  padding: 0
}
input {
  line-height: normal
}
input[type=checkbox],
input[type=radio] {
  box-sizing: border-box;
  padding: 0;
  display: inline
}
input[type=number]::-webkit-inner-spin-button,
input[type=number]::-webkit-outer-spin-button {
  height: auto
}
input[type=search] {
  -webkit-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
}
table {
  border-spacing: 0;
  width: 100%
}
html {
  font-size: 62.5%
}
body {
  margin: 0;
  font-size: 1.4em;
  font-weight: 400;
  font-family: HelveticaNeue,"Helvetica Neue",Helvetica,Arial,sans-serif;
  color: #ddd;
  background-color: #303536
}
a:hover {
  color: #e67300
}
.full-width,
.u-full-width {
  width: 100%;
  box-sizing: border-box
}
.max-full-width,
.u-max-full-width {
  max-width: 100%;
  box-sizing: border-box
}
.pull-right,
.u-pull-right {
  float: right
}
.pull-left,
.u-pull-left {
  float: left
}
h1,
h2,
h3,
h4,
h5,
h6 {
  margin: 1rem 0;
  font-weight: 700
}
h1 {
  font-size: 3rem;
  line-height: 1.2
}
h2 {
  font-size: 2.6rem;
  line-height: 1.25
}
h3 {
  font-size: 2.4rem;
  line-height: 1.3
}
h4 {
  font-size: 2rem;
  line-height: 1.35
}
h5 {
  font-size: 1.6rem
}
h6 {
  font-size: 1.4rem;
  line-height: 1.6
}
h1 small,
h2 small,
h3 small,
h4 small,
h5 small,
h6 small {
  font-size: .75em;
  font-weight: 400;
  color: #ccc
}
p {
  display: flow-root
}
.container {
  width: 100%;
  max-width: 1200px;
  margin: 0 auto;
  box-sizing: border-box
}
.column,
.columns {
  width: 100%;
  float: left;
  box-sizing: border-box
}
@media (min-width:400px) {
  .container {
    width: 95%;
    padding: 0
  }
}
.button,
button,
input[type=button],
input[type=reset],
input[type=submit] {
  padding: 0 30px;
  font-size: 11px;
  line-height: 32px;
  letter-spacing: .1rem;
  text-transform: uppercase;
  height: 32px;
  font-weight: 600;
  display: inline-block;
  box-sizing: border-box;
  text-decoration: none;
  text-align: center;
  white-space: nowrap;
  cursor: pointer
}
@media (min-width:550px) {
  .container {
    width: 95%
  }
  .column,
  .columns {
    margin-left: 4%
  }
  .column:first-child,
  .columns:first-child {
    margin-left: 0
  }
  .one.column,
  .one.columns {
    width: 4.66667%
  }
  .two.columns {
    width: 13.33333%
  }
  .three.columns {
    width: 22%
  }
  .four.columns,
  .one-third.column {
    width: 30.66667%
  }
  .five.columns {
    width: 39.33333%
  }
  .one-half.column,
  .six.columns {
    width: 48%
  }
  .seven.columns {
    width: 56.66667%
  }
  .eight.columns,
  .two-thirds.column {
    width: 65.33333%
  }
  .nine.columns {
    width: 74%
  }
  .ten.columns {
    width: 82.66667%
  }
  .eleven.columns {
    width: 91.33333%
  }
  .twelve.columns {
    width: 100%;
    margin-left: 0
  }
  .offset-by-one.column,
  .offset-by-one.columns {
    margin-left: 8.66667%
  }
  .offset-by-two.column,
  .offset-by-two.columns {
    margin-left: 17.33333%
  }
  .offset-by-three.column,
  .offset-by-three.columns {
    margin-left: 26%
  }
  .offset-by-four.column,
  .offset-by-four.columns,
  .offset-by-one-third.column,
  .offset-by-one-third.columns {
    margin-left: 34.66667%
  }
  .offset-by-five.column,
  .offset-by-five.columns {
    margin-left: 43.33333%
  }
  .offset-by-one-half.column,
  .offset-by-six.column,
  .offset-by-six.columns {
    margin-left: 52%
  }
  .offset-by-seven.column,
  .offset-by-seven.columns {
    margin-left: 60.66667%
  }
  .offset-by-eight.column,
  .offset-by-eight.columns,
  .offset-by-two-thirds.column,
  .offset-by-two-thirds.columns {
    margin-left: 69.33333%
  }
  .offset-by-nine.column,
  .offset-by-nine.columns {
    margin-left: 78%
  }
  .offset-by-ten.column,
  .offset-by-ten.columns {
    margin-left: 86.66667%
  }
  .offset-by-eleven.column,
  .offset-by-eleven.columns {
    margin-left: 95.33333%
  }
}
.button,
button {
  color: #aaa;
  background-color: #444;
  border-radius: 5px;
  border: 0
}
input[type=button],
input[type=reset],
input[type=submit] {
  color: #ddd;
  background-color: #446979;
  border: 0;
  border-radius: 5px
}
.button:hover,
button:hover {
  color: #444;
  background-color: #aaa;
  outline: 0
}
input[type=button]:hover,
input[type=reset]:hover,
input[type=submit]:hover {
  color: #446979;
  background-color: #ddd;
  outline: 0
}
.button:focus,
button:focus,
input[type=button]:focus,
input[type=reset]:focus,
input[type=submit]:focus {
  color: #333;
  border-color: #888;
  outline: 0
}
.button.button-primary,
.button.button-primary:focus,
.button.button-primary:hover,
button.button-primary,
button.button-primary:focus,
button.button-primary:hover,
input[type=button].button-primary,
input[type=button].button-primary:focus,
input[type=button].button-primary:hover,
input[type=reset].button-primary,
input[type=reset].button-primary:focus,
input[type=reset].button-primary:hover,
input[type=submit].button-primary,
input[type=submit].button-primary:focus,
input[type=submit].button-primary:hover {
  color: #303536;
  background-color: #ff8000;
  border-color: #ff8000
}
input[type=email],
input[type=number],
input[type=password],
input[type=search],
input[type=tel],
input[type=text],
input[type=url] {
  box-shadow: none;
  box-sizing: border-box;
  -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
}
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,
input[type=search]:focus,
input[type=tel]:focus,
input[type=text]:focus,
input[type=url]:focus,
select:focus,
textarea:focus {
  border: 1px solid #ff8000;
  outline: 0
}
label,
legend {
  margin-bottom: .5rem;
  font-weight: 600
}
fieldset {
  padding: 0;
  border-width: 0
}
label > .label-body {
  display: inline-block;
  margin-left: .5rem;
  font-weight: 400
}
ul {
  list-style: square
}
ol {
  list-style: decimal
}
ol,
ul {
  padding-left: 3rem
}
li {
  margin-bottom: 0
}
ol ol,
ol ul,
ul ol,
ul ul {
  margin: 1rem 0 1rem 2rem
}
code {
  padding: .2rem .5rem;
  margin: 0 .2rem;
  font-size: 90%;
  white-space: nowrap;
  background: #000;
  border: 2px solid #bbb;
  border-radius: 5px
}
pre > code {
  padding: 1rem 1.5rem;
  white-space: pre
}
td,
th {
  padding: 1px 5px;
  text-align: left
}
td:first-child,
th:first-child {
  padding-left: 0
}
.button,
button {
  margin-bottom: 1rem
}
fieldset,
input,
select,
textarea {
  margin-bottom: .5rem
}
blockquote,
dl,
figure,
ol,
p,
pre,
table,
ul {
  margin-bottom: 1.5rem
}
.header {
  color: #888;
  font-weight: 400;
  padding-top: 10px;
  border-width: 0
}
.filetree li > ul:before,
.filetree li li:before {
  border-left: 2px solid #888;
  content: '';
  position: absolute
}
.filetree>ul,
.header .logo,
.header .logo h1 {
  display: inline-block
}
.header .login {
  padding-top: 2px;
  text-align: right
}
.header .login .button {
  margin: 0
}
.header h1 {
  margin: 0;
  color: #888;
  display: inline-block
}
.header .title h1 {
  padding-bottom: 10px
}
.header .login,
.header h1 small,
.header h2 small {
  color: #777
}
.middle {
  background-color: #1d2021;
  padding-bottom: 20px;
  max-width: 100%;
  box-sizing: border-box
}
.content {
  padding-top: 8px;
  padding-left: 8px;
  padding-right: 8px
}
.content a {
  color: #8cf
}
.content a:hover,
.submenu a:hover,
.submenu label:hover {
  color: #fff
}
.artifact_content hr:first-of-type {
  margin: 0;
  border: 0
}
.artifact_content blockquote:first-of-type {
  padding: 1px 20px;
  margin: 0 0 20px;
  background: #000;
  border-radius: 5px
}
.footer {
  padding: 10px 0 60px;
  border-top: 0;
  color: #888
}
.footer a {
  color: #527b8f;
  background-repeat: no-repeat;
  background-position: center top 10px
}
.footer a:hover {
  color: #eef8ff
}
.mainmenu {
  background-color: #161819;
  border-top-right-radius: 15px;
  border-top-left-radius: 15px;
  clear: both
}
.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
}
.submenu {
  padding: 4px 0;
  background-color: #000;
  border-bottom-right-radius: 15px;
  border-bottom-left-radius: 15px;
  line-height: 2.5
}
.section,
.sortable thead,
.userTable thead {
  background-color: #404040
}
.submenu input,
.submenu select {
  margin: 0 0 0 5px
}
.submenu a,
.submenu label {
  display: inline;
  font-weight: 400;
  color: #5e9ab6;
  padding: 25px 15px;
  text-decoration: none;
  border-radius: 5px
}
.section {
  font-weight: 700;
  padding: 9px 10px 10px;
  margin: 10px 0;
  border-radius: 5px
}
.sectionmenu {
  border-top: 0;
  margin-top: -10px;
  margin-bottom: 10px;
  padding: 5px;
  text-align: center;
  background: #000;
  border-bottom-right-radius: 15px;
  border-bottom-left-radius: 15px
}
.sectionmenu a {
  display: inline-block;
  margin-top: 5px;
  margin-right: 1em
}
ul.browser {
  list-style: none;
  line-height: 1.6
}
ul.browser li.dir {
  background-repeat: no-repeat
}
.filetree a,
ul.browser li.file {
  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
}
div.filetreeline {
  display: table;
  width: 100%;
  white-space: nowrap
}
.filetree {
  margin: 1em 0;
  line-height: 1.6
}
.filetree ul {
  margin: 0;
  padding: 0;
  list-style: none
}
.filetree ul ul {
  margin: 0 0 0 21px
}
.filetree li {
  margin: 0;
  padding: 0
}
.filetree li li:before {
  top: -.8em;
  left: -14px;
  width: 16px;
  height: 1.5em;
  border-bottom: 2px solid #888
}
.filetree li > ul:before {
  top: -1.5em;
  bottom: 0;
  left: -35px
}
.filetree a {
  z-index: 1;
  display: table-cell;
  min-height: 16px;
  padding-left: 22px;
  background-position: center left
}
div.filetreeage {
  display: table-cell;
  padding-left: 10em;
  text-align: right
}
.fileage tr:first-child {
  background-color: #404040!important
}
.fileage tr:nth-child(odd),
.sortable tbody tr:nth-child(even),
.userTable tbody tr:nth-child(even) {
  background-color: #2c2c2c
}
.fileage tr:nth-child(even):hover,
.fileage tr:nth-child(odd):hover,
.sortable thead:hover {
  background-color: #555
}
.fileage tr:nth-child(even),
.sortable tbody tr:nth-child(odd),
.userTable tbody tr:nth-child(odd) {
  background-color: #181818
}
.fileage td,
.sortable td,
.userTable td {
  vertical-align: top;
  text-align: left;
  padding-top: 3px;
  border-left: 1px solid #333
}
.fileage td:first-child,
.sortable td:first-child,
.userTable td:first-child {
  border-left: transparent
}
table.label-value th {
  vertical-align: middle
}
.brlist table td {
  padding: 5px
}
.sortable,
.userTable {
  border-color: transparent;
  width: 75%
}
td.timelineTime,
tr.timelineBottom td {
  border-bottom: 0
}
.sortable tbody tr:nth-child(even):hover,
.sortable tbody tr:nth-child(odd):hover,
.userTable tbody tr:nth-child(even):hover,
.userTable tbody tr:nth-child(odd):hover {
  background-color: #444
}
div.timelineDate {
  font-weight: 700;
  white-space: nowrap
}
td.timelineTime {
  vertical-align: top;
  text-align: right;
  white-space: nowrap;
  padding-top: .75em
}
td.timelineGraph {
  width: 20px;
  text-align: left;
  vertical-align: top;
  border-bottom: 0
}
a.timelineHistLink {
  text-transform: lowercase
}
span.timelineComment {
  padding: 0 5px
}
.report th,
span.timelineEllipsis {
  cursor: pointer
}
table.timelineTable {
  border-spacing: 2px 3px
}
.timelineModernCell, .timelineColumnarCell, .timelineDetailCell, .timelineCompactCell, .timelineVerboseCell {
  vertical-align: top;
  text-align: left;
  padding: .75em;
  border-radius: 5px;
  background: #000
}
.timelineSelected > .timelineColumnarCell,
.timelineSelected > .timelineCompactCell,
.timelineSelected > .timelineDetailCell,
.timelineSelected > .timelineModernCell,
.timelineSelected > .timelineVerboseCell {
  padding: .75em;
  border-radius: 5px;
  border: solid #ff8000;
  vertical-align: top;
  text-align: left;
  background: #442800
}
.timelineCurrent > .timelineColumnarCell,
.timelineCurrent > .timelineCompactCell,
.timelineCurrent > .timelineDetailCell,
.timelineCurrent > .timelineModernCell,
.timelineCurrent > .timelineVerboseCell {
  vertical-align: top;
  text-align: left;
  padding: .75em;
  border-radius: 5px;
  border: dashed #ff8000
}
.timelineModernCell[id], .timelineColumnarCell[id], .timelineDetailCell[id] {
  background-color: #000
}
.tl-canvas {
  margin: 0 6px 0 10px
}
.tl-rail {
  width: 18px
}
.tl-mergeoffset {
  width: 2px
}
.tl-nodemark {
  margin-top: .8em
}
.tl-node {
  width: 10px;
  height: 10px;
  border: 2px solid #bbb;
  background: #111;
  cursor: pointer
}
.tl-node.leaf:after {
  content: '';
  position: absolute;
  top: 3px;
  left: 3px;
  width: 4px;
  height: 4px;
  background: #bbb
}
.tl-node.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
}
.intLink[title="Add indentation"],
.intLink[title="Center align"],
.intLink[title="Dotted list"],
.intLink[title="Left align"],
.intLink[title="Numbered list"],
.intLink[title="Remove formatting"],
.intLink[title="Right align"],
.intLink[title=Bold],
.intLink[title=Hyperlink],
.intLink[title=Italic],
.intLink[title=Quote],
.intLink[title=Redo],
.intLink[title=Underline],
.intLink[title=Undo] {
  width: 0;
  height: 0;
  padding: 11px
}
.tl-arrow.warp {
  margin-left: 1px;
  border-width: 3px 0;
  border-left: 7px solid #600000
}
.tl-line.warp {
  background: #600000
}
table.login_out .login_out_label {
  font-weight: 700;
  text-align: right
}
pre.udiff,
table.sbsdiffcols {
  width: 100%;
  overflow: auto;
  padding: 0 5px;
  font-size: 1rem;
  background: #000;
  border-radius: 5px
}
pre.udiff,
pre.udiff pre,
table.sbsdiffcols pre {
  font-size: 1.15rem
}
pre.udiff {
  padding: 10px 0
}
div.difftxtcol {
  width: 52rem;
  overflow-x: auto
}
span.diffchng {
  background-color: #8080e8;
  color: #000
}
span.diffadd {
  background-color: #559855;
  color: #000
}
span.diffrm {
  background-color: #c55;
  color: #000
}
div.diffmkrcol {
  padding: 0 1em;
  background: #111
}
span.diffhr {
  display: inline-block;
  margin: .5em 0 1em;
  color: #555
}
span.diffln {
  color: #666
}
table.report {
  width: 100%;
  cursor: auto;
  margin: 0 0 1em;
  color: #000
}
table.report thead {
  color: #ddd
}
table.report a {
  color: #0374ca
}
.report td,
.report th {
  border: 0;
  font-size: .9em;
  padding: 5px
}
.report thead + tbody tr:hover {
  background-color: #ff8000!important
}
tbody tr:nth-child(odd) td.tktDescValue,
tbody tr:nth-child(odd) td.tktDspValue {
  text-align: left;
  vertical-align: top;
  background: #181818;
  padding: 10px
}
tbody tr:nth-child(odd) td.tktDescLabel,
tbody tr:nth-child(odd) td.tktDspLabel {
  width: 70px;
  text-align: right;
  overflow: hidden;
  font-weight: 700;
  padding: 10px;
  background: #484848
}
tbody tr:nth-child(even) td.tktDescValue,
tbody tr:nth-child(even) td.tktDspValue {
  text-align: left;
  vertical-align: top;
  background: #2c2c2c;
  padding: 10px
}
tbody tr:nth-child(even) td.tktDescLabel,
tbody tr:nth-child(even) td.tktDspLabel {
  width: 70px;
  text-align: right;
  overflow: hidden;
  font-weight: 700;
  padding: 10px;
  margin: 2px;
  background: #555
}
td.tktDescLabel,
td.tktDspLabel {
  width: 70px;
  text-align: right;
  overflow: hidden;
  font-weight: 700;
  padding: 10px;
  background-color: #404040
}
td.tktDescValue code,
td.tktDescValue pre,
td.tktDspValue code,
td.tktDspValue pre {
  white-space: pre-wrap
}
div.tktComments {
  width: 100%;
  margin: 30px 0 10px
}
div.tktCommentHeader {
  border: 1px solid #ccc;
  background-color: #f8f8f8;
  padding: 10px;
  margin-bottom: 10px
}
span.tktCommentLogin {
  display: inline-block;
  font-weight: 700;
  color: #002060
}
div.tktCommentBody {
  margin: 10px 40px 30px
}
span.ueditInheritNobody {
  color: #72d472;
  padding: .2em
}
span.ueditInheritDeveloper {
  color: #ff5d5d;
  padding: .2em
}
span.ueditInheritReader {
  color: #f0b850;
  padding: .2em
}
span.ueditInheritAnonymous {
  color: #7d7dff;
  padding: .2em
}
#wysiwygBox {
  padding: 12px;
  color: #bbb;
  background-color: #000;
  border: transparent!important;
  border-radius: 5px
}
[id=toolBar2] {
  cursor: pointer;
  display: inline-block
}
.intLink[title=Undo] {
  background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cGF0aCBkPSJNMS44NTIgMi4wMDdjLjMxNC0uNDE4Ljg0Ny0uNjU1IDEuMzIzLS41MjlzLjk0Ni41MjggMS4wNTggMS4wNThjLjExMy41MzEtLjE1NCAxLjIzNy0uNTI5IDEuNTg4IiBmaWxsPSJub25lIiBzdHJva2U9IiM2NmE4YzciIHN0cm9rZS13aWR0aD0iLjUyOSIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIgc3Ryb2tlLWxpbmVqb2luPSJiZXZlbCIvPjxwYXRoIGQ9Ik0xLjA1OCAxLjE5bDEuNTg4IDEuNzJIMS4wNTh6IiBmaWxsPSIjNjZhOGM3Ii8+PC9zdmc+)
}
.intLink[title=Undo]:hover {
  background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cmVjdCByeT0iLjI2NSIgcng9Ii4yNjUiIHk9Ii0uMDAxIiBoZWlnaHQ9IjUuODIxIiB3aWR0aD0iNS44MjEiIGZpbGw9IiM1NTUiLz48cGF0aCBkPSJNMS44NTIgMi4wMDZjLjMxNC0uNDE3Ljg0Ny0uNjU1IDEuMzIzLS41MjlzLjk0Ni41MjggMS4wNTggMS4wNTljLjExMy41My0uMTU0IDEuMjM2LS41MjkgMS41ODciIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2ZmODAwMCIgc3Ryb2tlLXdpZHRoPSIuNTI5IiBzdHJva2UtbGluZWNhcD0ic3F1YXJlIiBzdHJva2UtbGluZWpvaW49ImJldmVsIi8+PHBhdGggZD0iTTEuMDU4IDEuMTlsMS41ODggMS43MkgxLjA1OHoiIGZpbGw9IiNmZjgwMDAiLz48L3N2Zz4=)
}
.intLink[title=Redo] {
  background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cGF0aCBkPSJNMy43MzQgMi4wMDdjLS4zMTQtLjQxOC0uODQ3LS42NTUtMS4zMjMtLjUyOXMtLjk0Ni41MjgtMS4wNTggMS4wNThjLS4xMTMuNTMxLjE1NCAxLjIzNy41MjkgMS41ODgiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzY2YThjNyIgc3Ryb2tlLXdpZHRoPSIuNTI5IiBzdHJva2UtbGluZWNhcD0ic3F1YXJlIiBzdHJva2UtbGluZWpvaW49ImJldmVsIi8+PHBhdGggZD0iTTQuNTI4IDEuMTlMMi45NCAyLjkxaDEuNTg4eiIgZmlsbD0iIzY2YThjNyIvPjwvc3ZnPg==)
}
.intLink[title=Redo]:hover {
  background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cmVjdCByeT0iLjI2NSIgcng9Ii4yNjUiIHk9Ii0uMDAxIiBoZWlnaHQ9IjUuODIxIiB3aWR0aD0iNS44MjEiIGZpbGw9IiM1NTUiLz48cGF0aCBkPSJNMy43MzQgMi4wMDZjLS4zMTQtLjQxNy0uODQ3LS42NTUtMS4zMjMtLjUyOXMtLjk0Ni41MjgtMS4wNTggMS4wNTljLS4xMTMuNTMuMTU0IDEuMjM2LjUyOSAxLjU4NyIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZmY4MDAwIiBzdHJva2Utd2lkdGg9Ii41MjkiIHN0cm9rZS1saW5lY2FwPSJzcXVhcmUiIHN0cm9rZS1saW5lam9pbj0iYmV2ZWwiLz48cGF0aCBkPSJNNC41MjggMS4xOUwyLjk0IDIuOTFoMS41ODh6IiBmaWxsPSIjZmY4MDAwIi8+PC9zdmc+)
}
.intLink[title="Remove formatting"] {
  background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cGF0aCBkPSJNMS43MiAyLjM0NmwtLjE3Mi4zMTdxLS4wMzQuMDY0LS4wNTEuMTEyLS4wMTYuMDQ5LS4wMTYuMDg2IDAgLjA2Ni4wNDguMDk3LjA0OS4wMy4xNS4wM2guMDdsLS4wMzkuMTg3SC43OTRsLjAzOC0uMTg4aC4wNzRxLjA0OCAwIC4wODQtLjAxLjAzOC0uMDEyLjA3Mi0uMDQzLjAzNS0uMDMxLjA3MS0uMDg1LjAzOC0uMDUzLjA4Ni0uMTM2TDIuNDE2LjY1N0gyLjlsLjI5NiAyLjA3N3EuMDA5LjA1NS4wMjIuMTAyLjAxNi4wNDYuMDQuMDguMDI0LjAzMy4wNi4wNTIuMDM4LjAyLjA5My4wMmguMDY0bC0uMDM4LjE4N0gyLjI3MmwuMDM4LS4xODhoLjA4cS4xMTQgMCAuMTgxLS4wMzQuMDY4LS4wMzYuMDY4LS4xMTQgMC0uMDI5LS4wMDItLjA1N2wtLjAwNS0uMDU1LS4wNTQtLjM4em0uNzctLjgwN3EtLjAwOC0uMDc4LS4wMTctLjE0NUwyLjQ2IDEuMjZsLS4wMS0uMTI0LS4wMDUtLjEyNHEtLjAyNi4wNjQtLjA1Mi4xMmwtLjA1NS4xMS0uMDY1LjExOC0uMDgxLjE0NS0uMzUuNjI1aC43MTN6bTIuMjM4IDMuNzUzbC0uMTI4LS40MmgtLjY0MmwtLjEyOC40MmgtLjQwM2wuNjIyLTEuNzdoLjQ1N2wuNjI1IDEuNzd6bS0uMjE3LS43MzNsLS4yLS42NDZxLS4wMjItLjA3NC0uMDMxLS4xMTgtLjA0LjE1NS0uMjI4Ljc2NHoiIGZpbGw9IiM2NmE4YzciLz48cGF0aCBkPSJNMS4zMjMgMy43MDRjMCAxLjA1OSAxLjMyMy43NjQgMS4zMjMuNzY0IiBmaWxsPSJub25lIiBzdHJva2U9IiNhYWEiIHN0cm9rZS13aWR0aD0iLjUyOSIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIvPjxwYXRoIGQ9Ik0yLjM4MSAzLjcwNHYxLjU4OGwxLjA1OS0uNzk0eiIgZmlsbD0iI2FhYSIvPjwvc3ZnPg==)
}
.intLink[title="Remove formatting"]:hover {
  background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1LjgyMSA1LjgyMSIgaGVpZ2h0PSIyMiIgd2lkdGg9IjIyIj48cmVjdCBmaWxsPSIjNTU1IiB3aWR0aD0iNS44MjEiIGhlaWdodD0iNS44MjEiIHk9Ii0uMDAxIiByeD0iLjI2NSIgcnk9Ii4yNjUiLz48cGF0aCBmaWxsPSIjZmY4MDAwIiBkPSJNMS43MiAyLjM0NmwtLjE3Mi4zMTdxLS4wMzQuMDYzLS4wNTEuMTEyLS4wMTYuMDQ4LS4wMTYuMDg2IDAgLjA2NS4wNDguMDk2LjA0OS4wMy4xNS4wM2guMDdsLS4wMzkuMTg3SC43OTRsLjAzOC0uMTg4aC4wNzRxLjA0OCAwIC4wODQtLjAxLjAzOC0uMDEyLjA3Mi0uMDQzLjAzNS0uMDMxLjA3MS0uMDg0LjAzOC0uMDU0LjA4Ni0uMTM2TDIuNDE2LjY1NkgyLjlsLjI5NiAyLjA3N3EuMDA5LjA1NS4wMjIuMTAyLjAxNi4wNDYuMDQuMDh0LjA2LjA1M3EuMDM4LjAxOS4wOTMuMDE5aC4wNjRsLS4wMzguMTg3SDIuMjcybC4wMzgtLjE4N2guMDhxLjExNCAwIC4xODEtLjAzNS4wNjgtLjAzNi4wNjgtLjExNCAwLS4wMjktLjAwMi0uMDU2bC0uMDA1LS4wNTUtLjA1NC0uMzgxem0uNzctLjgwOHEtLjAwOC0uMDc4LS4wMTctLjE0NWwtLjAxNC0uMTMzLS4wMS0uMTI0LS4wMDUtLjEyNHEtLjAyNi4wNjQtLjA1Mi4xMmwtLjA1NS4xMS0uMDY1LjExOC0uMDgxLjE0NS0uMzUuNjI1aC43MTN6TTQuNzI4IDUuMjlMNC42IDQuODcxaC0uNjQybC0uMTI4LjQyaC0uNDAzbC42MjItMS43N2guNDU3bC42MjUgMS43N3ptLS4yMTctLjczMmwtLjItLjY0NXEtLjAyMi0uMDc1LS4wMzEtLjExOS0uMDQuMTU1LS4yMjguNzY0eiIvPjxwYXRoIHN0cm9rZS1saW5lY2FwPSJzcXVhcmUiIHN0cm9rZS13aWR0aD0iLjUyOSIgc3Ryb2tlPSIjZGRkIiBmaWxsPSJub25lIiBkPSJNMS4zMjMgMy43MDNjMCAxLjA1OSAxLjMyMy43NjQgMS4zMjMuNzY0Ii8+PHBhdGggZmlsbD0iI2RkZCIgZD0iTTIuMzgxIDMuNzAzdjEuNTg4bDEuMDU5LS43OTR6Ii8+PC9zdmc+)
}
.intLink[title=Bold] {
  background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cGF0aCBkPSJNMS44NjYgMS4zNDloLjk4cS42NyAwIC45NzEuMTkxLjMwNC4xOS4zMDQuNjA2IDAgLjI4Mi0uMTM0LjQ2My0uMTMxLjE4LS4zNS4yMTd2LjAyMnEuMjk5LjA2Ni40My4yNS4xMzMuMTgyLjEzMy40ODYgMCAuNDMtLjMxMi42NzItLjMxLjI0MS0uODQ0LjI0MUgxLjg2NnptLjY2OCAxLjI0N2guMzg3cS4yNzIgMCAuMzkyLS4wODQuMTIzLS4wODQuMTIzLS4yNzggMC0uMTgxLS4xMzQtLjI1OS0uMTMtLjA4LS40MTctLjA4aC0uMzUxem0wIC41M3YuODJoLjQzNXEuMjc1IDAgLjQwNy0uMTA2LjEzMS0uMTA1LjEzMS0uMzIzIDAtLjM5Mi0uNTYtLjM5MnoiIGZpbGw9IiNhYWEiLz48L3N2Zz4=)
}
.intLink[title=Bold]:hover {
  background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cmVjdCByeT0iLjI2NSIgcng9Ii4yNjUiIHk9Ii0uMDAxIiBoZWlnaHQ9IjUuODIxIiB3aWR0aD0iNS44MjEiIGZpbGw9IiM1NTUiLz48cGF0aCBkPSJNMS44NjYgMS4zNDloLjk4cS42NyAwIC45NzEuMTkxLjMwNC4xOS4zMDQuNjA2IDAgLjI4Mi0uMTM0LjQ2My0uMTMxLjE4LS4zNS4yMTd2LjAyMnEuMjk5LjA2Ni40My4yNS4xMzMuMTgyLjEzMy40ODYgMCAuNDMtLjMxMi42NzItLjMxLjI0MS0uODQ0LjI0MUgxLjg2NnptLjY2OCAxLjI0N2guMzg3cS4yNzIgMCAuMzkyLS4wODQuMTIzLS4wODQuMTIzLS4yNzggMC0uMTgxLS4xMzQtLjI1OS0uMTMtLjA4LS40MTctLjA4aC0uMzUxem0wIC41M3YuODJoLjQzNXEuMjc1IDAgLjQwNy0uMTA2LjEzMS0uMTA1LjEzMS0uMzIzIDAtLjM5Mi0uNTYtLjM5MnoiIGZpbGw9IiNkZGQiLz48L3N2Zz4=)
}
.intLink[title=Italic] {
  background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cGF0aCBkPSJNMy4yNDUgNC40OTdIMS44NTJsLjA3Ny0uMzc5LjQwMy0uMTc3LjQzMy0yLjAzNy0uMzI3LS4xNzYuMDgtLjM4SDMuOTFsLS4wOC4zOC0uNDEuMTc2LS40MzIgMi4wMzcuMzM2LjE3N3oiIGZpbGw9IiNhYWEiLz48L3N2Zz4=)
}
.intLink[title=Italic]:hover {
  background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cmVjdCByeT0iLjI2NSIgcng9Ii4yNjUiIHk9Ii0uMDAxIiBoZWlnaHQ9IjUuODIxIiB3aWR0aD0iNS44MjEiIGZpbGw9IiM1NTUiLz48cGF0aCBkPSJNMy4yNDUgNC40OTdIMS44NTJsLjA3Ny0uMzc5LjQwMy0uMTc3LjQzMy0yLjAzNy0uMzI3LS4xNzYuMDgtLjM4SDMuOTFsLS4wOC4zOC0uNDEuMTc2LS40MzIgMi4wMzcuMzM2LjE3N3oiIGZpbGw9IiNkZGQiLz48L3N2Zz4=)
}
.intLink[title=Underline] {
  background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cGF0aCBkPSJNNC4xNzIgMS4wNHYyLjAzOHEwIC4zNDktLjE1OC42MTEtLjE1NS4yNjMtLjQ1LjQwMy0uMjk1LjE0LS42OTcuMTQtLjYwNyAwLS45NDMtLjMxLS4zMzYtLjMxMi0uMzM2LS44NTNWMS4wNDFoLjY2NXYxLjkyN3EwIC4zNjQuMTQ2LjUzNC4xNDcuMTcuNDg1LjE3LjMyNyAwIC40NzQtLjE3LjE0OC0uMTcyLjE0OC0uNTM4VjEuMDR6IiBmaWxsPSIjYWFhIi8+PHBhdGggZD0iTTEuMzIzIDQuNzYyaDMuMTc1IiBmaWxsPSJub25lIiBzdHJva2U9IiNhYWEiIHN0cm9rZS13aWR0aD0iLjUyOSIvPjwvc3ZnPg==)
}
.intLink[title=Underline]:hover {
  background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cmVjdCByeT0iLjI2NSIgcng9Ii4yNjUiIHk9Ii0uMDAxIiBoZWlnaHQ9IjUuODIxIiB3aWR0aD0iNS44MjEiIGZpbGw9IiM1NTUiLz48cGF0aCBkPSJNNC4xNzIgMS4wNHYyLjAzOHEwIC4zNDktLjE1OC42MTEtLjE1NS4yNjMtLjQ1LjQwMy0uMjk1LjE0LS42OTcuMTQtLjYwNyAwLS45NDMtLjMxLS4zMzYtLjMxMi0uMzM2LS44NTNWMS4wNDFoLjY2NXYxLjkyN3EwIC4zNjQuMTQ2LjUzNC4xNDcuMTcuNDg1LjE3LjMyNyAwIC40NzQtLjE3LjE0OC0uMTcyLjE0OC0uNTM4VjEuMDR6IiBmaWxsPSIjZGRkIi8+PHBhdGggZD0iTTEuMzIzIDQuNzYyaDMuMTc1IiBmaWxsPSJub25lIiBzdHJva2U9IiNkZGQiIHN0cm9rZS13aWR0aD0iLjUyOSIvPjwvc3ZnPg==)
}
.intLink[title="Left align"] {
  background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cGF0aCBkPSJNMS4wNTggMS4zMjNoMy43MDRNMS4wNTggMi4zODFoMi45MU0xLjA1OCAzLjQ0aDMuNzA0TTEuMDU4IDQuNDk4aDIuOTEiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2FhYSIgc3Ryb2tlLXdpZHRoPSIuNTI5Ii8+PC9zdmc+)
}
.intLink[title="Left align"]:hover {
  background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cmVjdCByeT0iLjI2NSIgcng9Ii4yNjUiIHk9Ii0uMDAxIiBoZWlnaHQ9IjUuODIxIiB3aWR0aD0iNS44MjEiIGZpbGw9IiM1NTUiLz48cGF0aCBkPSJNMS4wNTggMS4zMjJoMy43MDRNMS4wNTggMi4zOGgyLjkxbS0yLjkxIDEuMDU5aDMuNzA0TTEuMDU4IDQuNDk3aDIuOTEiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2RkZCIgc3Ryb2tlLXdpZHRoPSIuNTI5Ii8+PC9zdmc+)
}
.intLink[title="Center align"] {
  background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cGF0aCBkPSJNMS4wNTggMS4zMjNoMy43MDRtLTIuOTEgMS4wNThIMy45N00xLjA1OCAzLjQ0aDMuNzA0bS0yLjkxIDEuMDU4SDMuOTciIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2FhYSIgc3Ryb2tlLXdpZHRoPSIuNTI5Ii8+PC9zdmc+)
}
.intLink[title="Center align"]:hover {
  background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cmVjdCByeT0iLjI2NSIgcng9Ii4yNjUiIHk9Ii0uMDAxIiBoZWlnaHQ9IjUuODIxIiB3aWR0aD0iNS44MjEiIGZpbGw9IiM1NTUiLz48cGF0aCBkPSJNMS4wNTggMS4zMjJoMy43MDRNMS44NTIgMi4zOEgzLjk3TTEuMDU4IDMuNDM5aDMuNzA0bS0yLjkxIDEuMDU4SDMuOTciIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2RkZCIgc3Ryb2tlLXdpZHRoPSIuNTI5Ii8+PC9zdmc+)
}
.intLink[title="Right align"] {
  background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cGF0aCBkPSJNMS4wNTggMS4zMjNoMy43MDRtLTIuOTEgMS4wNThoMi45MU0xLjA1OCAzLjQ0aDMuNzA0bS0yLjkxIDEuMDU4aDIuOTEiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2FhYSIgc3Ryb2tlLXdpZHRoPSIuNTI5Ii8+PC9zdmc+)
}
.intLink[title="Right align"]:hover {
  background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cmVjdCByeT0iLjI2NSIgcng9Ii4yNjUiIHk9Ii0uMDAxIiBoZWlnaHQ9IjUuODIxIiB3aWR0aD0iNS44MjEiIGZpbGw9IiM1NTUiLz48cGF0aCBkPSJNMS4wNTggMS4zMjJoMy43MDRNMS44NTIgMi4zOGgyLjkxTTEuMDU4IDMuNDM5aDMuNzA0bS0yLjkxIDEuMDU4aDIuOTEiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2RkZCIgc3Ryb2tlLXdpZHRoPSIuNTI5Ii8+PC9zdmc+)
}
.intLink[title="Numbered list"] {
  background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cGF0aCBkPSJNMi4xMTcgMS4zMjNoMi45MU0yLjExNyAyLjkxaDIuOTFtLTIuOTEgMS41ODhoMi45MSIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjYWFhIiBzdHJva2Utd2lkdGg9Ii41MjkiLz48cGF0aCBkPSJNMS41MjMgMS44NTFoLS4yNjZ2LS43MjlsLjAwMi0uMTIuMDA0LS4xM3EtLjA2Ni4wNjYtLjA5Mi4wODdsLS4xNDQuMTE2LS4xMjktLjE2LjQwNi0uMzIzaC4yMTl6bS4yMjIgMS41ODhoLS44OHYtLjE4NWwuMzE2LS4zMnEuMTQtLjE0NC4xODMtLjE5OS4wNDMtLjA1Ni4wNjItLjEwMy4wMi0uMDQ3LjAyLS4wOTggMC0uMDc2LS4wNDMtLjExMy0uMDQxLS4wMzctLjExMS0uMDM3LS4wNzMgMC0uMTQyLjAzMy0uMDcuMDM0LS4xNDQuMDk2TC44NiAyLjM0MnEuMDkzLS4wOC4xNTQtLjExMi4wNjItLjAzMy4xMzQtLjA1LjA3Mi0uMDE4LjE2Mi0uMDE4LjExOCAwIC4yMDguMDQzdC4xNC4xMnEuMDUuMDc4LjA1LjE3OCAwIC4wODctLjAzLjE2My0uMDMuMDc2LS4wOTUuMTU2LS4wNjQuMDgtLjIyNi4yMjlsLS4xNjIuMTUydi4wMTJoLjU0OXptLS4wNDkuNjA5cTAgLjExOC0uMDcyLjIwMS0uMDcxLjA4My0uMi4xMTR2LjAwNXEuMTUyLjAxOS4yMy4wOTMuMDc5LjA3My4wNzkuMTk4IDAgLjE4Mi0uMTMyLjI4My0uMTMyLjEwMS0uMzc2LjEwMS0uMjA1IDAtLjM2NC0uMDY4di0uMjI2cS4wNzMuMDM3LjE2MS4wNi4wODguMDIzLjE3NC4wMjMuMTMyIDAgLjE5NS0uMDQ1dC4wNjMtLjE0NHEwLS4wODgtLjA3My0uMTI1LS4wNzItLjAzNy0uMjMtLjAzN2gtLjA5NnYtLjIwNWguMDk3cS4xNDcgMCAuMjE0LS4wMzcuMDY4LS4wNC4wNjgtLjEzMiAwLS4xNDMtLjE4LS4xNDMtLjA2MSAwLS4xMjYuMDItLjA2NC4wMjEtLjE0Mi4wNzJsLS4xMjMtLjE4M3EuMTcyLS4xMjQuNDEtLjEyNC4xOTYgMCAuMzEuMDc5LjExMy4wNzkuMTEzLjIyeiIgZmlsbD0iIzY2YThjNyIvPjwvc3ZnPg==)
}
.intLink[title="Numbered list"]:hover {
  background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cmVjdCByeT0iLjI2NSIgcng9Ii4yNjUiIHk9Ii0uMDAxIiBoZWlnaHQ9IjUuODIxIiB3aWR0aD0iNS44MjEiIGZpbGw9IiM1NTUiLz48cGF0aCBkPSJNMi4xMTcgMS4zMjJoMi45MU0yLjExNyAyLjkxaDIuOTFtLTIuOTEgMS41ODdoMi45MSIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZGRkIiBzdHJva2Utd2lkdGg9Ii41MjkiLz48cGF0aCBkPSJNMS41MjMgMS44NTFoLS4yNjZ2LS43MjlsLjAwMi0uMTIuMDA0LS4xM3EtLjA2Ni4wNjYtLjA5Mi4wODdsLS4xNDQuMTE2LS4xMjktLjE2LjQwNi0uMzIzaC4yMTl6bS4yMjIgMS41ODhoLS44OHYtLjE4NWwuMzE2LS4zMnEuMTQtLjE0NC4xODMtLjE5OS4wNDMtLjA1Ni4wNjItLjEwMy4wMi0uMDQ3LjAyLS4wOTggMC0uMDc2LS4wNDMtLjExMy0uMDQxLS4wMzctLjExMS0uMDM3LS4wNzMgMC0uMTQyLjAzMy0uMDcuMDM0LS4xNDQuMDk2TC44NiAyLjM0MnEuMDkzLS4wOC4xNTQtLjExMi4wNjItLjAzMy4xMzQtLjA1LjA3Mi0uMDE4LjE2Mi0uMDE4LjExOCAwIC4yMDguMDQzdC4xNC4xMnEuMDUuMDc4LjA1LjE3OCAwIC4wODctLjAzLjE2My0uMDMuMDc2LS4wOTUuMTU2LS4wNjQuMDgtLjIyNi4yMjlsLS4xNjIuMTUydi4wMTJoLjU0OXptLS4wNDkuNjA5cTAgLjExOC0uMDcyLjIwMS0uMDcxLjA4My0uMi4xMTR2LjAwNXEuMTUyLjAxOS4yMy4wOTMuMDc5LjA3My4wNzkuMTk4IDAgLjE4Mi0uMTMyLjI4My0uMTMyLjEwMS0uMzc2LjEwMS0uMjA1IDAtLjM2NC0uMDY4di0uMjI2cS4wNzMuMDM3LjE2MS4wNi4wODguMDIzLjE3NC4wMjMuMTMyIDAgLjE5NS0uMDQ1dC4wNjMtLjE0NHEwLS4wODgtLjA3My0uMTI1LS4wNzItLjAzNy0uMjMtLjAzN2gtLjA5NnYtLjIwNWguMDk3cS4xNDcgMCAuMjE0LS4wMzcuMDY4LS4wNC4wNjgtLjEzMiAwLS4xNDMtLjE4LS4xNDMtLjA2MSAwLS4xMjYuMDItLjA2NC4wMjEtLjE0Mi4wNzJsLS4xMjMtLjE4M3EuMTcyLS4xMjQuNDEtLjEyNC4xOTYgMCAuMzEuMDc5LjExMy4wNzkuMTEzLjIyeiIgZmlsbD0iI2ZmODAwMCIvPjwvc3ZnPg==)
}
.intLink[title="Dotted list"] {
  background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cGF0aCBkPSJNMi4xMTcgMS4zMjJoMi45MU0yLjExNyAyLjkxaDIuOTFtLTIuOTEgMS41ODdoMi45MSIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjYWFhIiBzdHJva2Utd2lkdGg9Ii41MjkiLz48Y2lyY2xlIHI9Ii4yNjUiIGN5PSIxLjMyMiIgY3g9IjEuMzIzIiBmaWxsPSIjNjZhOGM3Ii8+PGNpcmNsZSByPSIuMjY1IiBjeT0iMi45MSIgY3g9IjEuMzIzIiBmaWxsPSIjNjZhOGM3Ii8+PGNpcmNsZSByPSIuMjY1IiBjeT0iNC40OTciIGN4PSIxLjMyMyIgZmlsbD0iIzY2YThjNyIvPjwvc3ZnPg==)
}
.intLink[title="Dotted list"]:hover {
  background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cmVjdCByeT0iLjI2NSIgcng9Ii4yNjUiIHk9Ii0uMDAxIiBoZWlnaHQ9IjUuODIxIiB3aWR0aD0iNS44MjEiIGZpbGw9IiM1NTUiLz48cGF0aCBkPSJNMi4xMTcgMS4zMjJoMi45MU0yLjExNyAyLjkxaDIuOTFtLTIuOTEgMS41ODdoMi45MSIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZGRkIiBzdHJva2Utd2lkdGg9Ii41MjkiLz48Y2lyY2xlIHI9Ii4yNjUiIGN5PSIxLjMyMiIgY3g9IjEuMzIzIiBmaWxsPSIjZmY4MDAwIi8+PGNpcmNsZSByPSIuMjY1IiBjeT0iMi45MSIgY3g9IjEuMzIzIiBmaWxsPSIjZmY4MDAwIi8+PGNpcmNsZSByPSIuMjY1IiBjeT0iNC40OTciIGN4PSIxLjMyMyIgZmlsbD0iI2ZmODAwMCIvPjwvc3ZnPg==)
}
.intLink[title=Quote] {
  background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cGF0aCBkPSJNMy4wOTIgMi41MDlxMC0uMjcuMDgzLS41MjEuMDgyLS4yNTMuMjYtLjQ2OC4xODItLjIxNS40NjctLjM4NC4yOS0uMTc0LjcwMy0uMjg1di40MzhxLS4xODIuMDY2LS4zMjIuMTMyLS4xNDEuMDY2LS4yMzYuMTQ1LS4wOTEuMDc0LS4xNC4xNy0uMDQ2LjA5LS4wNDYuMjE0IDAgLjA4My4wNDUuMTMzLjA0Ni4wNS4xMTYuMDk1bC4xNDkuMDk1cS4wNzkuMDQ1LjE0OS4xMi4wNy4wNy4xMTYuMTgyLjA0NS4xMTEuMDQ1LjI4IDAgLjI4Ni0uMTc4LjQzNS0uMTczLjE0OS0uNDM0LjE0OS0uMzYgMC0uNTctLjI0OC0uMjA3LS4yNDgtLjIwNy0uNjgyem0tMS43NyAwcTAtLjI3LjA4My0uNTIxLjA4My0uMjUzLjI2LS40NjguMTgyLS4yMTUuNDY4LS4zODQuMjktLjE3NC43MDMtLjI4NXYuNDM4cS0uMTg2LjA2Ni0uMzI3LjEzMi0uMTM2LjA2Ni0uMjMyLjE0NS0uMDkuMDc0LS4xNC4xNy0uMDQ2LjA5LS4wNDYuMjE0IDAgLjA4My4wNDYuMTMzLjA0NS4wNS4xMTYuMDk1bC4xNDguMDk1cS4wNzkuMDQ1LjE1LjEyLjA3LjA3LjExNS4xODIuMDQ2LjExMS4wNDYuMjggMCAuMjg2LS4xNzguNDM1LS4xNzQuMTQ5LS40MzQuMTQ5LS4zNiAwLS41Ny0uMjQ4LS4yMDgtLjI0OC0uMjA4LS42ODJ6IiBmaWxsPSIjNjZhOGM3Ii8+PC9zdmc+)
}
.intLink[title=Quote]:hover {
  background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cmVjdCByeT0iLjI2NSIgcng9Ii4yNjUiIHk9Ii0uMDAxIiBoZWlnaHQ9IjUuODIxIiB3aWR0aD0iNS44MjEiIGZpbGw9IiM1NTUiLz48cGF0aCBkPSJNMy4wOTIgMi41MDlxMC0uMjcuMDgzLS41MjEuMDgyLS4yNTMuMjYtLjQ2OC4xODItLjIxNS40NjctLjM4NC4yOS0uMTc0LjcwMy0uMjg1di40MzhxLS4xODIuMDY2LS4zMjIuMTMyLS4xNDEuMDY2LS4yMzYuMTQ1LS4wOTEuMDc0LS4xNC4xNy0uMDQ2LjA5LS4wNDYuMjE0IDAgLjA4My4wNDUuMTMzLjA0Ni4wNS4xMTYuMDk1bC4xNDkuMDk1cS4wNzkuMDQ1LjE0OS4xMi4wNy4wNy4xMTYuMTgyLjA0NS4xMTEuMDQ1LjI4IDAgLjI4Ni0uMTc4LjQzNS0uMTczLjE0OS0uNDM0LjE0OS0uMzYgMC0uNTctLjI0OC0uMjA3LS4yNDgtLjIwNy0uNjgyem0tMS43NyAwcTAtLjI3LjA4My0uNTIxLjA4My0uMjUzLjI2LS40NjguMTgyLS4yMTUuNDY4LS4zODQuMjktLjE3NC43MDMtLjI4NXYuNDM4cS0uMTg2LjA2Ni0uMzI3LjEzMi0uMTM2LjA2Ni0uMjMyLjE0NS0uMDkuMDc0LS4xNC4xNy0uMDQ2LjA5LS4wNDYuMjE0IDAgLjA4My4wNDYuMTMzLjA0NS4wNS4xMTYuMDk1bC4xNDguMDk1cS4wNzkuMDQ1LjE1LjEyLjA3LjA3LjExNS4xODIuMDQ2LjExMS4wNDYuMjggMCAuMjg2LS4xNzguNDM1LS4xNzQuMTQ5LS40MzQuMTQ5LS4zNiAwLS41Ny0uMjQ4LS4yMDgtLjI0OC0uMjA4LS42ODJ6IiBmaWxsPSIjZmY4MDAwIi8+PC9zdmc+)
}
.intLink[title="Delete indentation"] {
  width: 0;
  height: 0;
  padding: 11px;
  background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cGF0aCBkPSJNMS4wNTggMS4zMjNoMy43MDRNMi4zODEgMi4zODFoMi4zODJNMi4zODEgMy40NGgyLjM4Mk0xLjA1OCA0LjQ5OGgzLjcwNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjYWFhIiBzdHJva2Utd2lkdGg9Ii41MjkiLz48cGF0aCBkPSJNLjUzIDIuOTFsMS4zMjItLjc5M3YxLjU4N3oiIGZpbGw9IiM2NmE4YzciLz48L3N2Zz4=)
}
.intLink[title="Delete indentation"]:hover {
  background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cmVjdCByeT0iLjI2NSIgcng9Ii4yNjUiIHk9Ii0uMDAxIiBoZWlnaHQ9IjUuODIxIiB3aWR0aD0iNS44MjEiIGZpbGw9IiM1NTUiLz48cGF0aCBkPSJNMS4wNTggMS4zMjJoMy43MDRNMi4zODEgMi4zOGgyLjM4Mk0yLjM4MSAzLjQzOWgyLjM4Mk0xLjA1OCA0LjQ5N2gzLjcwNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZGRkIiBzdHJva2Utd2lkdGg9Ii41MjkiLz48cGF0aCBkPSJNLjUzIDIuOTFsMS4zMjItLjc5NHYxLjU4N3oiIGZpbGw9IiNmZjgwMDAiLz48L3N2Zz4=)
}
.intLink[title="Add indentation"] {
  background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cGF0aCBkPSJNMS4wNTggMS4zMjNoMy43MDRNMi4zODEgMi4zODFoMi4zODJNMi4zODEgMy40NGgyLjM4Mk0xLjA1OCA0LjQ5OGgzLjcwNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjYWFhIiBzdHJva2Utd2lkdGg9Ii41MjkiLz48cGF0aCBkPSJNMi4xMTcgMi45MUwuNzk0IDIuMTE3djEuNTg3eiIgZmlsbD0iIzY2YThjNyIvPjwvc3ZnPg==)
}
.intLink[title="Add indentation"]:hover {
  background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1LjgyMSA1LjgyMSIgaGVpZ2h0PSIyMiIgd2lkdGg9IjIyIj48cmVjdCB3aWR0aD0iNS44MjEiIGhlaWdodD0iNS44MjEiIHk9Ii0uMDAxIiByeD0iLjI2NSIgcnk9Ii4yNjUiIGZpbGw9IiM1NTUiLz48cGF0aCBkPSJNMS4wNTggMS4zMjJoMy43MDRNMi4zODEgMi4zOGgyLjM4Mk0yLjM4MSAzLjQzOWgyLjM4Mk0xLjA1OCA0LjQ5N2gzLjcwNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZGRkIiBzdHJva2Utd2lkdGg9Ii41MjkiLz48cGF0aCBkPSJNMi4xMTcgMi45MUwuNzk0IDIuMTE2djEuNTg3eiIgZmlsbD0iI2ZmODAwMCIvPjwvc3ZnPg==)
}
.intLink[title=Hyperlink] {
  background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cGF0aCBkPSJNMS43NTIgMy45NjloLjc5M20tLjc5My0yLjExN2guNzkzTTEuNzUyIDMuOTdjLS4zNDMgMC0uNjU5LS4xMTktLjgzLS40MTUtLjE3MS0uMjk3LS4xNzEtLjk5IDAtMS4yODcuMTcxLS4yOTYuNDg3LS40MTUuODMtLjQxNW0yLjIxNyAyLjExNmgtLjc5NG0uNzk0LTIuMTE3aC0uNzk0bS43OTQgMi4xMTdjLjM0MiAwIC42NTgtLjExOS44My0uNDE1LjE3LS4yOTcuMTctLjk5IDAtMS4yODctLjE3Mi0uMjk2LS40ODgtLjQxNS0uODMtLjQxNU0yLjExNyAyLjkxaDEuNTg3IiBmaWxsPSJub25lIiBzdHJva2U9IiNhYWEiIHN0cm9rZS13aWR0aD0iLjUyOSIvPjwvc3ZnPg==)
}
.intLink[title=Hyperlink]:hover {
  background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cmVjdCByeT0iLjI2NSIgcng9Ii4yNjUiIHk9Ii0uMDAxIiBoZWlnaHQ9IjUuODIxIiB3aWR0aD0iNS44MjEiIGZpbGw9IiM1NTUiLz48cGF0aCBkPSJNMS43NTIgMy45NjhoLjc5M20tLjc5My0yLjExN2guNzkzbS0uNzkzIDIuMTE3Yy0uMzQzIDAtLjY1OS0uMTE5LS44My0uNDE1LS4xNzEtLjI5Ny0uMTcxLS45OSAwLTEuMjg3LjE3MS0uMjk2LjQ4Ny0uNDE1LjgzLS40MTVtMi4yMTcgMi4xMTdoLS43OTRtLjc5NC0yLjExN2gtLjc5NG0uNzk0IDIuMTE3Yy4zNDIgMCAuNjU4LS4xMTkuODMtLjQxNS4xNy0uMjk3LjE3LS45OSAwLTEuMjg3LS4xNzItLjI5Ni0uNDg4LS40MTUtLjgzLS40MTVNMi4xMTcgMi45MWgxLjU4NyIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZGRkIiBzdHJva2Utd2lkdGg9Ii41MjkiLz48L3N2Zz4=)
}
.statistics-report-graph-line {
  background-color: #ff8000
}
mark,
p.noMoreShun,
p.shunned,
span.modpending {
  color: #ff8000
}
table.captcha {
  margin: auto;
  padding: 10px;
  background-color: #000;
  border-radius: 5px
}
.container:after,
.mainmenu:after,
.row:after,
.u-cf {
  content: "";
  display: table;
  clear: both
}

Added skins/ardoise/details.txt.









>
>
>
>
1
2
3
4
timeline-arrowheads:        0
timeline-circle-nodes:      1
timeline-color-graph-lines: 1
white-foreground:           1

Added skins/ardoise/footer.txt.































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  <th1>
    if {[string first artifact $current_page] == 0 || [string first hexdump $current_page] == 0} {
      html "</div>"
    }
  </th1>
  </div> <!-- end div container -->
</div> <!-- end div middle max-full-width -->
<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>

Added skins/ardoise/header.txt.

















































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
<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 {[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>

Added skins/ardoise/images/active.svg.



>
1
<svg xmlns="http://www.w3.org/2000/svg" width="17" height="9" viewBox="0 0 4.498 2.381"><path d="M4.233 2.381H.265l.998-1.058.986-1.058.998 1.062z" fill="#ff8000"/></svg>

Added skins/ardoise/images/addindent.svg.



>
1
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M1.058 1.323h3.704M2.381 2.381h2.382M2.381 3.44h2.382M1.058 4.498h3.704" fill="none" stroke="#aaa" stroke-width=".529"/><path d="M2.117 2.91L.794 2.117v1.587z" fill="#66a8c7"/></svg>

Added skins/ardoise/images/addindent_h.svg.



>
1
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 5.821 5.821" height="22" width="22"><rect width="5.821" height="5.821" y="-.001" rx=".265" ry=".265" fill="#555"/><path d="M1.058 1.322h3.704M2.381 2.38h2.382M2.381 3.439h2.382M1.058 4.497h3.704" fill="none" stroke="#ddd" stroke-width=".529"/><path d="M2.117 2.91L.794 2.116v1.587z" fill="#ff8000"/></svg>

Added skins/ardoise/images/blist.svg.



>
1
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M2.117 1.322h2.91M2.117 2.91h2.91m-2.91 1.587h2.91" fill="none" stroke="#aaa" stroke-width=".529"/><circle r=".265" cy="1.322" cx="1.323" fill="#66a8c7"/><circle r=".265" cy="2.91" cx="1.323" fill="#66a8c7"/><circle r=".265" cy="4.497" cx="1.323" fill="#66a8c7"/></svg>

Added skins/ardoise/images/blist_h.svg.



>
1
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M2.117 1.322h2.91M2.117 2.91h2.91m-2.91 1.587h2.91" fill="none" stroke="#ddd" stroke-width=".529"/><circle r=".265" cy="1.322" cx="1.323" fill="#ff8000"/><circle r=".265" cy="2.91" cx="1.323" fill="#ff8000"/><circle r=".265" cy="4.497" cx="1.323" fill="#ff8000"/></svg>

Added skins/ardoise/images/bold.svg.



>
1
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M1.866 1.349h.98q.67 0 .971.191.304.19.304.606 0 .282-.134.463-.131.18-.35.217v.022q.299.066.43.25.133.182.133.486 0 .43-.312.672-.31.241-.844.241H1.866zm.668 1.247h.387q.272 0 .392-.084.123-.084.123-.278 0-.181-.134-.259-.13-.08-.417-.08h-.351zm0 .53v.82h.435q.275 0 .407-.106.131-.105.131-.323 0-.392-.56-.392z" fill="#aaa"/></svg>

Added skins/ardoise/images/bold_h.svg.



>
1
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M1.866 1.349h.98q.67 0 .971.191.304.19.304.606 0 .282-.134.463-.131.18-.35.217v.022q.299.066.43.25.133.182.133.486 0 .43-.312.672-.31.241-.844.241H1.866zm.668 1.247h.387q.272 0 .392-.084.123-.084.123-.278 0-.181-.134-.259-.13-.08-.417-.08h-.351zm0 .53v.82h.435q.275 0 .407-.106.131-.105.131-.323 0-.392-.56-.392z" fill="#ddd"/></svg>

Added skins/ardoise/images/calign.svg.



>
1
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M1.058 1.323h3.704m-2.91 1.058H3.97M1.058 3.44h3.704m-2.91 1.058H3.97" fill="none" stroke="#aaa" stroke-width=".529"/></svg>

Added skins/ardoise/images/calign_h.svg.



>
1
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M1.058 1.322h3.704M1.852 2.38H3.97M1.058 3.439h3.704m-2.91 1.058H3.97" fill="none" stroke="#ddd" stroke-width=".529"/></svg>

Added skins/ardoise/images/clrfmt.svg.



>
1
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M1.72 2.346l-.172.317q-.034.064-.051.112-.016.049-.016.086 0 .066.048.097.049.03.15.03h.07l-.039.187H.794l.038-.188h.074q.048 0 .084-.01.038-.012.072-.043.035-.031.071-.085.038-.053.086-.136L2.416.657H2.9l.296 2.077q.009.055.022.102.016.046.04.08.024.033.06.052.038.02.093.02h.064l-.038.187H2.272l.038-.188h.08q.114 0 .181-.034.068-.036.068-.114 0-.029-.002-.057l-.005-.055-.054-.38zm.77-.807q-.008-.078-.017-.145L2.46 1.26l-.01-.124-.005-.124q-.026.064-.052.12l-.055.11-.065.118-.081.145-.35.625h.713zm2.238 3.753l-.128-.42h-.642l-.128.42h-.403l.622-1.77h.457l.625 1.77zm-.217-.733l-.2-.646q-.022-.074-.031-.118-.04.155-.228.764z" fill="#66a8c7"/><path d="M1.323 3.704c0 1.059 1.323.764 1.323.764" fill="none" stroke="#aaa" stroke-width=".529" stroke-linecap="square"/><path d="M2.381 3.704v1.588l1.059-.794z" fill="#aaa"/></svg>

Added skins/ardoise/images/clrfmt_h.svg.



>
1
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 5.821 5.821" height="22" width="22"><rect fill="#555" width="5.821" height="5.821" y="-.001" rx=".265" ry=".265"/><path fill="#ff8000" d="M1.72 2.346l-.172.317q-.034.063-.051.112-.016.048-.016.086 0 .065.048.096.049.03.15.03h.07l-.039.187H.794l.038-.188h.074q.048 0 .084-.01.038-.012.072-.043.035-.031.071-.084.038-.054.086-.136L2.416.656H2.9l.296 2.077q.009.055.022.102.016.046.04.08t.06.053q.038.019.093.019h.064l-.038.187H2.272l.038-.187h.08q.114 0 .181-.035.068-.036.068-.114 0-.029-.002-.056l-.005-.055-.054-.381zm.77-.808q-.008-.078-.017-.145l-.014-.133-.01-.124-.005-.124q-.026.064-.052.12l-.055.11-.065.118-.081.145-.35.625h.713zM4.728 5.29L4.6 4.871h-.642l-.128.42h-.403l.622-1.77h.457l.625 1.77zm-.217-.732l-.2-.645q-.022-.075-.031-.119-.04.155-.228.764z"/><path stroke-linecap="square" stroke-width=".529" stroke="#ddd" fill="none" d="M1.323 3.703c0 1.059 1.323.764 1.323.764"/><path fill="#ddd" d="M2.381 3.703v1.588l1.059-.794z"/></svg>

Added skins/ardoise/images/delindent.svg.



>
1
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M1.058 1.323h3.704M2.381 2.381h2.382M2.381 3.44h2.382M1.058 4.498h3.704" fill="none" stroke="#aaa" stroke-width=".529"/><path d="M.53 2.91l1.322-.793v1.587z" fill="#66a8c7"/></svg>

Added skins/ardoise/images/delindent_h.svg.



>
1
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M1.058 1.322h3.704M2.381 2.38h2.382M2.381 3.439h2.382M1.058 4.497h3.704" fill="none" stroke="#ddd" stroke-width=".529"/><path d="M.53 2.91l1.322-.794v1.587z" fill="#ff8000"/></svg>

Added skins/ardoise/images/dir.svg.



>
1
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="16" viewBox="0 0 5.292 4.233"><path d="M.794.53v3.174h3.704V1.323H2.91V.529z" fill="#1d2021" stroke="#ff8000" stroke-width=".529" stroke-linecap="round" stroke-linejoin="round"/></svg>

Added skins/ardoise/images/file.svg.



>
1
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="16" viewBox="0 0 5.292 4.233"><path d="M1.323.265v3.704h2.646V1.323L2.91.265z" fill="#1d2021" stroke="#ddd" stroke-width=".529" stroke-linejoin="round"/><path d="M2.646.265h.264v1.323h1.06" fill="#1d2021" stroke="#ddd" stroke-width=".529" stroke-linejoin="round"/></svg>

Added skins/ardoise/images/italic.svg.



>
1
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M3.245 4.497H1.852l.077-.379.403-.177.433-2.037-.327-.176.08-.38H3.91l-.08.38-.41.176-.432 2.037.336.177z" fill="#aaa"/></svg>

Added skins/ardoise/images/italic_h.svg.



>
1
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M3.245 4.497H1.852l.077-.379.403-.177.433-2.037-.327-.176.08-.38H3.91l-.08.38-.41.176-.432 2.037.336.177z" fill="#ddd"/></svg>

Added skins/ardoise/images/lalign.svg.



>
1
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M1.058 1.323h3.704M1.058 2.381h2.91M1.058 3.44h3.704M1.058 4.498h2.91" fill="none" stroke="#aaa" stroke-width=".529"/></svg>

Added skins/ardoise/images/lalign_h.svg.



>
1
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M1.058 1.322h3.704M1.058 2.38h2.91m-2.91 1.059h3.704M1.058 4.497h2.91" fill="none" stroke="#ddd" stroke-width=".529"/></svg>

Added skins/ardoise/images/link.svg.



>
1
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M1.752 3.969h.793m-.793-2.117h.793M1.752 3.97c-.343 0-.659-.119-.83-.415-.171-.297-.171-.99 0-1.287.171-.296.487-.415.83-.415m2.217 2.116h-.794m.794-2.117h-.794m.794 2.117c.342 0 .658-.119.83-.415.17-.297.17-.99 0-1.287-.172-.296-.488-.415-.83-.415M2.117 2.91h1.587" fill="none" stroke="#aaa" stroke-width=".529"/></svg>

Added skins/ardoise/images/link_h.svg.



>
1
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M1.752 3.968h.793m-.793-2.117h.793m-.793 2.117c-.343 0-.659-.119-.83-.415-.171-.297-.171-.99 0-1.287.171-.296.487-.415.83-.415m2.217 2.117h-.794m.794-2.117h-.794m.794 2.117c.342 0 .658-.119.83-.415.17-.297.17-.99 0-1.287-.172-.296-.488-.415-.83-.415M2.117 2.91h1.587" fill="none" stroke="#ddd" stroke-width=".529"/></svg>

Added skins/ardoise/images/nlist.svg.



>
1
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M2.117 1.323h2.91M2.117 2.91h2.91m-2.91 1.588h2.91" fill="none" stroke="#aaa" stroke-width=".529"/><path d="M1.523 1.851h-.266v-.729l.002-.12.004-.13q-.066.066-.092.087l-.144.116-.129-.16.406-.323h.219zm.222 1.588h-.88v-.185l.316-.32q.14-.144.183-.199.043-.056.062-.103.02-.047.02-.098 0-.076-.043-.113-.041-.037-.111-.037-.073 0-.142.033-.07.034-.144.096L.86 2.342q.093-.08.154-.112.062-.033.134-.05.072-.018.162-.018.118 0 .208.043t.14.12q.05.078.05.178 0 .087-.03.163-.03.076-.095.156-.064.08-.226.229l-.162.152v.012h.549zm-.049.609q0 .118-.072.201-.071.083-.2.114v.005q.152.019.23.093.079.073.079.198 0 .182-.132.283-.132.101-.376.101-.205 0-.364-.068v-.226q.073.037.161.06.088.023.174.023.132 0 .195-.045t.063-.144q0-.088-.073-.125-.072-.037-.23-.037h-.096v-.205h.097q.147 0 .214-.037.068-.04.068-.132 0-.143-.18-.143-.061 0-.126.02-.064.021-.142.072l-.123-.183q.172-.124.41-.124.196 0 .31.079.113.079.113.22z" fill="#66a8c7"/></svg>

Added skins/ardoise/images/nlist_h.svg.



>
1
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M2.117 1.322h2.91M2.117 2.91h2.91m-2.91 1.587h2.91" fill="none" stroke="#ddd" stroke-width=".529"/><path d="M1.523 1.851h-.266v-.729l.002-.12.004-.13q-.066.066-.092.087l-.144.116-.129-.16.406-.323h.219zm.222 1.588h-.88v-.185l.316-.32q.14-.144.183-.199.043-.056.062-.103.02-.047.02-.098 0-.076-.043-.113-.041-.037-.111-.037-.073 0-.142.033-.07.034-.144.096L.86 2.342q.093-.08.154-.112.062-.033.134-.05.072-.018.162-.018.118 0 .208.043t.14.12q.05.078.05.178 0 .087-.03.163-.03.076-.095.156-.064.08-.226.229l-.162.152v.012h.549zm-.049.609q0 .118-.072.201-.071.083-.2.114v.005q.152.019.23.093.079.073.079.198 0 .182-.132.283-.132.101-.376.101-.205 0-.364-.068v-.226q.073.037.161.06.088.023.174.023.132 0 .195-.045t.063-.144q0-.088-.073-.125-.072-.037-.23-.037h-.096v-.205h.097q.147 0 .214-.037.068-.04.068-.132 0-.143-.18-.143-.061 0-.126.02-.064.021-.142.072l-.123-.183q.172-.124.41-.124.196 0 .31.079.113.079.113.22z" fill="#ff8000"/></svg>

Added skins/ardoise/images/quote.svg.



>
1
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M3.092 2.509q0-.27.083-.521.082-.253.26-.468.182-.215.467-.384.29-.174.703-.285v.438q-.182.066-.322.132-.141.066-.236.145-.091.074-.14.17-.046.09-.046.214 0 .083.045.133.046.05.116.095l.149.095q.079.045.149.12.07.07.116.182.045.111.045.28 0 .286-.178.435-.173.149-.434.149-.36 0-.57-.248-.207-.248-.207-.682zm-1.77 0q0-.27.083-.521.083-.253.26-.468.182-.215.468-.384.29-.174.703-.285v.438q-.186.066-.327.132-.136.066-.232.145-.09.074-.14.17-.046.09-.046.214 0 .083.046.133.045.05.116.095l.148.095q.079.045.15.12.07.07.115.182.046.111.046.28 0 .286-.178.435-.174.149-.434.149-.36 0-.57-.248-.208-.248-.208-.682z" fill="#66a8c7"/></svg>

Added skins/ardoise/images/quote_h.svg.



>
1
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M3.092 2.509q0-.27.083-.521.082-.253.26-.468.182-.215.467-.384.29-.174.703-.285v.438q-.182.066-.322.132-.141.066-.236.145-.091.074-.14.17-.046.09-.046.214 0 .083.045.133.046.05.116.095l.149.095q.079.045.149.12.07.07.116.182.045.111.045.28 0 .286-.178.435-.173.149-.434.149-.36 0-.57-.248-.207-.248-.207-.682zm-1.77 0q0-.27.083-.521.083-.253.26-.468.182-.215.468-.384.29-.174.703-.285v.438q-.186.066-.327.132-.136.066-.232.145-.09.074-.14.17-.046.09-.046.214 0 .083.046.133.045.05.116.095l.148.095q.079.045.15.12.07.07.115.182.046.111.046.28 0 .286-.178.435-.174.149-.434.149-.36 0-.57-.248-.208-.248-.208-.682z" fill="#ff8000"/></svg>

Added skins/ardoise/images/ralign.svg.



>
1
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M1.058 1.323h3.704m-2.91 1.058h2.91M1.058 3.44h3.704m-2.91 1.058h2.91" fill="none" stroke="#aaa" stroke-width=".529"/></svg>

Added skins/ardoise/images/ralign_h.svg.



>
1
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M1.058 1.322h3.704M1.852 2.38h2.91M1.058 3.439h3.704m-2.91 1.058h2.91" fill="none" stroke="#ddd" stroke-width=".529"/></svg>

Added skins/ardoise/images/redo.svg.



>
1
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M3.734 2.007c-.314-.418-.847-.655-1.323-.529s-.946.528-1.058 1.058c-.113.531.154 1.237.529 1.588" fill="none" stroke="#66a8c7" stroke-width=".529" stroke-linecap="square" stroke-linejoin="bevel"/><path d="M4.528 1.19L2.94 2.91h1.588z" fill="#66a8c7"/></svg>

Added skins/ardoise/images/redo_h.svg.



>
1
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M3.734 2.006c-.314-.417-.847-.655-1.323-.529s-.946.528-1.058 1.059c-.113.53.154 1.236.529 1.587" fill="none" stroke="#ff8000" stroke-width=".529" stroke-linecap="square" stroke-linejoin="bevel"/><path d="M4.528 1.19L2.94 2.91h1.588z" fill="#ff8000"/></svg>

Added skins/ardoise/images/underline.svg.



>
1
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M4.172 1.04v2.038q0 .349-.158.611-.155.263-.45.403-.295.14-.697.14-.607 0-.943-.31-.336-.312-.336-.853V1.041h.665v1.927q0 .364.146.534.147.17.485.17.327 0 .474-.17.148-.172.148-.538V1.04z" fill="#aaa"/><path d="M1.323 4.762h3.175" fill="none" stroke="#aaa" stroke-width=".529"/></svg>

Added skins/ardoise/images/underline_h.svg.



>
1
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M4.172 1.04v2.038q0 .349-.158.611-.155.263-.45.403-.295.14-.697.14-.607 0-.943-.31-.336-.312-.336-.853V1.041h.665v1.927q0 .364.146.534.147.17.485.17.327 0 .474-.17.148-.172.148-.538V1.04z" fill="#ddd"/><path d="M1.323 4.762h3.175" fill="none" stroke="#ddd" stroke-width=".529"/></svg>

Added skins/ardoise/images/undo.svg.



>
1
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M1.852 2.007c.314-.418.847-.655 1.323-.529s.946.528 1.058 1.058c.113.531-.154 1.237-.529 1.588" fill="none" stroke="#66a8c7" stroke-width=".529" stroke-linecap="square" stroke-linejoin="bevel"/><path d="M1.058 1.19l1.588 1.72H1.058z" fill="#66a8c7"/></svg>

Added skins/ardoise/images/undo_h.svg.



>
1
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M1.852 2.006c.314-.417.847-.655 1.323-.529s.946.528 1.058 1.059c.113.53-.154 1.236-.529 1.587" fill="none" stroke="#ff8000" stroke-width=".529" stroke-linecap="square" stroke-linejoin="bevel"/><path d="M1.058 1.19l1.588 1.72H1.058z" fill="#ff8000"/></svg>

Changes to skins/black_and_white/footer.txt.

1
2
3
4
<div class="footer">
Fossil $release_version $manifest_version $manifest_date
</div>
</body></html>



<
1
2
3

<div class="footer">
Fossil $release_version $manifest_version $manifest_date
</div>

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
<html>
<head>
<base href="$baseurl/$current_page" />
<title>$<project_name>: $<title></title>
<link rel="alternate" type="application/rss+xml" title="RSS Feed"
      href="$home/timeline.rss">
<link rel="stylesheet" href="$stylesheet_url" type="text/css"
      media="screen">
</head>
<body>
<div class="header">
  <div class="logo">
    <img src="$logo_image_url" alt="logo">
    <br />$<project_name>
  </div>
  <div class="title">$<title></div>
  <div class="status"><th1>
<
<
<
<
<
<
<
<
<
<

















1
2
3
4
5
6
7










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

Changes to skins/blitz/css.txt.

1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
  border-bottom: 1px solid #ddd;
  border-right: 1px solid #ddd;
}

tr.timelineCurrent td.timelineTableCell {
}

tr.timelineSpacer {
}

tr.timelineBottom td {
  border-bottom: 0;
}

div.timelineDate {
  font-weight: bold;
  white-space: nowrap;







<
<
<







1068
1069
1070
1071
1072
1073
1074



1075
1076
1077
1078
1079
1080
1081
  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;

Changes to skins/blitz/footer.txt.

1
2
3
4
5
6
7
8
9
10
11
12
      </div> <!-- end div container -->
    </div> <!-- end div middle max-full-width -->
    <div class="footer">
      <div class="container">
        <div class="pull-right">
          <a href="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>
  </body>
</html>
|
|
|
|
|
|
|
|
|
|
<
<
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>


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
<html>
  <head>
    <base href="$baseurl/$current_page" />
    <title>$<project_name>: $<title></title>
      <link rel="alternate" type="application/rss+xml" title="RSS Feed" href="$home/timeline.rss" />
      <link rel="stylesheet" href="$stylesheet_url" type="text/css" media="screen" />
  </head>

  <body>
    <div class="header">
      <div class="container">

        <!-- Header -->
        <div class="login pull-right">
          <th1>
            if {[info exists login]} {
              html "<b>$login</b> — <a class='button' href='$home/login'>Logout</a>\n"
            } else {
              html "<a class='button' href='$home/login'>Login</a>\n"
            }
          </th1>
          <div>
            <h2><small>$title</small></h2>
          </div>
        </div>
        <div class='logo'>
          <img src='$logo_image_url' />
          <th1>
          if {[anycap jor]} {
            html "<a class='rss' href='$home/timeline.rss'></a>"
          }
          </th1>
        </div>

        <!-- Main Menu -->
        <div class="mainmenu">
          <ul>
            <th1>
proc menulink {url name} {
  upvar current_page current
  upvar home home
  if {[string range $url 0 [string length $current]] eq "/$current"} {
    html "<li class='active'>"
  } else {
    html "<li>"
<
<
<
<
<
<
<
<
<
|
|

|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|

|
|
|
|
















1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36









<div class="header">
  <div class="container">

    <!-- Header -->
    <div class="login pull-right">
      <th1>
        if {[info exists login]} {
          html "<b>$login</b> — <a class='button' href='$home/login'>Logout</a>\n"
        } else {
          html "<a class='button' href='$home/login'>Login</a>\n"
        }
      </th1>
      <div>
        <h2><small>$title</small></h2>
      </div>
    </div>
    <div class='logo'>
      <img src='$logo_image_url' />
      <th1>
      if {[anycap jor]} {
        html "<a class='rss' href='$home/timeline.rss'></a>"
      }
      </th1>
    </div>

    <!-- Main Menu -->
    <div class="mainmenu">
      <ul>
        <th1>
proc menulink {url name} {
  upvar current_page current
  upvar home home
  if {[string range $url 0 [string length $current]] eq "/$current"} {
    html "<li class='active'>"
  } else {
    html "<li>"

Changes to skins/blitz_no_logo/css.txt.

1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
  border-bottom: 1px solid #ddd;
  border-right: 1px solid #ddd;
}

tr.timelineCurrent td.timelineTableCell {
}

tr.timelineSpacer {
}

tr.timelineBottom td {
  border-bottom: 0;
}

div.timelineDate {
  font-weight: bold;
  white-space: nowrap;







<
<
<







1068
1069
1070
1071
1072
1073
1074



1075
1076
1077
1078
1079
1080
1081
  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;

Changes to skins/blitz_no_logo/footer.txt.

1
2
3
4
5
6
7
8
9
10
11
12
      </div> <!-- end div container -->
    </div> <!-- end div middle max-full-width -->
    <div class="footer">
      <div class="container">
        <div class="pull-right">
          <a href="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>
  </body>
</html>
|
|
|
|
|
|
|
|
|
|
<
<
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>


Changes to 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
<html>
  <head>
    <base href="$baseurl/$current_page" />
    <title>$<project_name>: $<title></title>
      <link rel="alternate" type="application/rss+xml" title="RSS Feed" href="$home/timeline.rss" />
      <link rel="stylesheet" href="$stylesheet_url" type="text/css" media="screen" />
  </head>

  <body>
    <div class="header">
      <div class="container">

        <div class="login pull-right">
          <th1>
            if {[info exists login]} {
              html "<b>$login</b> — <a class='button' href='$home/login'>Logout</a>\n"
            } else {
              html "<a class='button' href='$home/login'>Login</a>\n"
            }
          </th1>
        </div>
        <div class='title'>
          <h1>$<project_name>
          <th1>
          if {[anycap jor]} {
            html "<a class='rss' href='$home/timeline.rss'></a>"
          }
          </th1>
          <small> &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>"
<
<
<
<
<
<
<
<
<
|
|

|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|

|
|
|
|
















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









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

Changes to skins/bootstrap/footer.txt.

32
33
34
35
36
37
38
39
  var target = document.querySelector(
    collapse.getAttribute('data-target')
  );
  target.classList.toggle('collapse');
  target.classList.toggle('collapsed');
};
</script>
</body></html>







<
32
33
34
35
36
37
38

  var target = document.querySelector(
    collapse.getAttribute('data-target')
  );
  target.classList.toggle('collapse');
  target.classList.toggle('collapsed');
};
</script>

Changes to skins/default/css.txt.

40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
...
179
180
181
182
183
184
185




186
187
188
189
190
191
192

.content h1 {
    font-size: 1.25em;
}
.content h2 {
    font-size: 1.15em;
}
.content h2 {
    font-size: 1.05em;
    font-weight: bold;
}

.section {
    font-size: 1em;
    font-weight: bold;
................................................................................
    vertical-align: top;
    background-color: #f8f8f8;
    border: 1px solid #ccc;
}
td.tktDspValue pre {
    white-space: pre-wrap;
}





.footer {
    border-top: 1px solid #ccc;
    padding: 10px;
    font-size:.7em;
    margin-top: 10px;
    color: #ccc;







|







 







>
>
>
>







40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
...
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196

.content h1 {
    font-size: 1.25em;
}
.content h2 {
    font-size: 1.15em;
}
.content h3 {
    font-size: 1.05em;
    font-weight: bold;
}

.section {
    font-size: 1em;
    font-weight: bold;
................................................................................
    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;

Changes to skins/default/footer.txt.

1
2
3
4
5
6
<div class="footer">
This page was generated in about
<th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s by
Fossil $release_version $manifest_version $manifest_date
</div>
</body></html>





<
1
2
3
4
5

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

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
<html>
  <head>
    <base href="$baseurl/$current_page" />
    <title>$<project_name>: $<title></title>
      <link rel="alternate" type="application/rss+xml" title="RSS Feed"
            href="$home/timeline.rss" />
      <link rel="stylesheet" href="$stylesheet_url" type="text/css"
            media="screen" />
  </head>

  <body>
    <div class="header">
      <div class="title"><h1>$<project_name></h1>$<title></div>
        <div class="status"><th1>
     if {[info exists login]} {
       html "$login — <a href='$home/login'>Logout</a>\n"
     } else {
       html "<a href='$home/login'>Login</a>\n"
     }
        </th1></div>
    </div>

    <div class="mainmenu">
<th1>
proc menulink {url name} {
  upvar current_page current
  upvar home home
  if {[string range $url 0 [string length $current]] eq "/$current"} {
    html "<a href='$home$url' class='active'>$name</a>\n"
  } else {
<
<
<
<
<
<
<
<
<
<
<
|
|
|
|
|
|
|
|
|
|
<
|


















1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
18











<div class="header">
  <div class="title"><h1>$<project_name></h1>$<title></div>
    <div class="status"><th1>
 if {[info exists login]} {
   html "$login — <a href='$home/login'>Logout</a>\n"
 } else {
   html "<a href='$home/login'>Login</a>\n"
 }
    </th1></div>
</div>

<div class="mainmenu">
<th1>
proc menulink {url name} {
  upvar current_page current
  upvar home home
  if {[string range $url 0 [string length $current]] eq "/$current"} {
    html "<a href='$home$url' class='active'>$name</a>\n"
  } else {

Changes to skins/eagle/css.txt.

162
163
164
165
166
167
168

169
170
171
172
173
174
175
176
...
331
332
333
334
335
336
337





/* the format for the timeline data table */
table.timelineTable {
  cellspacing: 0;
  border: 0;
  cellpadding: 0;
  font-family: "courier new";

  border-collapse: collapse;
}

tr.timelineSelected {
  background-color: #7EA2D9;
}

/* commit node */
................................................................................
div.selectedText {
  background-color: #7EA2D9;
}

.statistics-report-graph-line {
  background-color: #7EA2D9;
}











>
|







 







>
>
>
>
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
...
332
333
334
335
336
337
338
339
340
341
342

/* the format for the timeline data table */
table.timelineTable {
  cellspacing: 0;
  border: 0;
  cellpadding: 0;
  font-family: "courier new";
  border-spacing: 0px 2px;
  // border-collapse: collapse;
}

tr.timelineSelected {
  background-color: #7EA2D9;
}

/* commit node */
................................................................................
div.selectedText {
  background-color: #7EA2D9;
}

.statistics-report-graph-line {
  background-color: #7EA2D9;
}

.timelineModernCell[id], .timelineColumnarCell[id], .timelineDetailCell[id] {
  background-color: #455978;
}

Changes to skins/eagle/footer.txt.

18
19
20
21
22
23
24
25
  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>
</body></html>







<
18
19
20
21
22
23
24

  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>

Changes to skins/eagle/header.txt.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<html>
<head>
<base href="$baseurl/$current_page" />
<title>$<project_name>: $<title></title>
<link rel="alternate" type="application/rss+xml" title="RSS Feed"
      href="$home/timeline.rss" />
<link rel="stylesheet" href="$stylesheet_url" type="text/css"
      media="screen" />
</head>
<body>
<div class="header">
  <div class="logo">
    <th1>
    ##
    ## NOTE: The purpose of this procedure is to take the base URL of the
    ##       Fossil project and return the root of the entire web site using
    ##       the same URI scheme as the base URL (e.g. http or https).
<
<
<
<
<
<
<
<
<
<

















1
2
3
4
5
6
7










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

Changes to skins/enhanced1/footer.txt.

18
19
20
21
22
23
24
25
  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>
</body></html>







<
18
19
20
21
22
23
24

  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>

Changes to skins/enhanced1/header.txt.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<html>
<head>
<base href="$baseurl/$current_page" />
<title>$<project_name>: $<title></title>
<link rel="alternate" type="application/rss+xml" title="RSS Feed"
      href="$home/timeline.rss" />
<link rel="stylesheet" href="$stylesheet_url" type="text/css"
      media="screen" />
</head>
<body>
<div class="header">
  <div class="logo">
    <th1>
    ##
    ## NOTE: The purpose of this procedure is to take the base URL of the
    ##       Fossil project and return the root of the entire web site using
    ##       the same URI scheme as the base URL (e.g. http or https).
<
<
<
<
<
<
<
<
<
<

















1
2
3
4
5
6
7










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

Changes to skins/khaki/footer.txt.

1
2
3
4
<div class="footer">
Fossil $release_version $manifest_version $manifest_date
</div>
</body></html>



<
1
2
3

<div class="footer">
Fossil $release_version $manifest_version $manifest_date
</div>

Changes to skins/khaki/header.txt.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<html>
<head>
<base href="$baseurl/$current_page" />
<title>$<project_name>: $<title></title>
<link rel="alternate" type="application/rss+xml" title="RSS Feed"
      href="$home/timeline.rss">
<link rel="stylesheet" href="$stylesheet_url" type="text/css"
      media="screen">
</head>
<body>
<div class="header">
  <div class="title">$<title></div>
  <div class="status">
    <div class="logo">$<project_name></div><br/>
    <th1>
     if {[info exists login]} {
       puts "Logged in as $login"
<
<
<
<
<
<
<
<
<
<

















1
2
3
4
5
6
7










<div class="header">
  <div class="title">$<title></div>
  <div class="status">
    <div class="logo">$<project_name></div><br/>
    <th1>
     if {[info exists login]} {
       puts "Logged in as $login"

Changes to skins/original/footer.txt.

1
2
3
4
5
6
<div class="footer">
This page was generated in about
<th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s by
Fossil $release_version $manifest_version $manifest_date
</div>
</body></html>





<
1
2
3
4
5

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

Changes to skins/original/header.txt.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<html>
<head>
<base href="$baseurl/$current_page" />
<title>$<project_name>: $<title></title>
<link rel="alternate" type="application/rss+xml" title="RSS Feed"
      href="$home/timeline.rss" />
<link rel="stylesheet" href="$stylesheet_url" type="text/css"
      media="screen" />
</head>
<body>
<div class="header">
  <div class="logo">
    <img src="$logo_image_url" alt="logo" />
  </div>
  <div class="title"><small>$<project_name></small><br />$<title></div>
  <div class="status"><th1>
     if {[info exists login]} {
<
<
<
<
<
<
<
<
<
<

















1
2
3
4
5
6
7










<div class="header">
  <div class="logo">
    <img src="$logo_image_url" alt="logo" />
  </div>
  <div class="title"><small>$<project_name></small><br />$<title></div>
  <div class="status"><th1>
     if {[info exists login]} {

Changes to skins/plain_gray/footer.txt.

1
2
3
4
<div class="footer">
Fossil $release_version $manifest_version $manifest_date
</div>
</body></html>



<
1
2
3

<div class="footer">
Fossil $release_version $manifest_version $manifest_date
</div>

Changes to skins/plain_gray/header.txt.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<html>
<head>
<base href="$baseurl/$current_page" />
<title>$<project_name>: $<title></title>
<link rel="alternate" type="application/rss+xml" title="RSS Feed"
      href="$home/timeline.rss">
<link rel="stylesheet" href="$stylesheet_url" type="text/css"
      media="screen">
</head>
<body>
<div class="header">
  <div class="title"><small>$<project_name></small><br />$<title></div>
  <div class="status"><th1>
     if {[info exists login]} {
       puts "Logged in as $login"
     } else {
       puts "Not logged in"
<
<
<
<
<
<
<
<
<
<

















1
2
3
4
5
6
7










<div class="header">
  <div class="title"><small>$<project_name></small><br />$<title></div>
  <div class="status"><th1>
     if {[info exists login]} {
       puts "Logged in as $login"
     } else {
       puts "Not logged in"

Changes to skins/rounded1/details.txt.

1
2
3
4
timeline-arrowheads:        1
timeline-circle-nodes:      0
timeline-color-graph-lines: 0
white-foreground:           0

|


1
2
3
4
timeline-arrowheads:        1
timeline-circle-nodes:      1
timeline-color-graph-lines: 0
white-foreground:           0

Changes to skins/rounded1/footer.txt.

1
2
3
4
<div class="footer">
Fossil $release_version $manifest_version $manifest_date
</div>
</body></html>



<
1
2
3

<div class="footer">
Fossil $release_version $manifest_version $manifest_date
</div>

Changes to skins/rounded1/header.txt.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<html>
<head>
<base href="$baseurl/$current_page" />
<title>$<project_name>: $<title></title>
<link rel="alternate" type="application/rss+xml" title="RSS Feed"
      href="$home/timeline.rss">
<link rel="stylesheet" href="$stylesheet_url" type="text/css"
      media="screen">
</head>
<body>
<div class="header">
  <div class="logo">
    <img src="$logo_image_url" alt="logo">
    <br />$<project_name>
  </div>
  <div class="title">$<title></div>
  <div class="status"><th1>
<
<
<
<
<
<
<
<
<
<

















1
2
3
4
5
6
7










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

Changes to skins/xekri/css.txt.

705
706
707
708
709
710
711
712























713
714
715
716
717
718
719
720







721
722
723



724
725
726
727






728


729
730
731
732
733




734


735
736
737
738





739
740
741
742
743
744


745

746
747
748









749
750
751
752
753
754
755
756



757
758
759
760



761
762
763
764
765










766

767
768
769











770
771
772
773
774
775



776
777
778
779
780


781
782
783
784
785
786












787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
...
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
...
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
...
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
}


/**************************************
 * Timeline
 */

div.divider {























  color: #ee0;
  font-size: 1.2rem;
  font-weight: bold;
  margin-top: 1rem;
  white-space: nowrap;
}

/* The suppressed duplicates lines in timeline, .. */







span.timelineDisabled {
  font-size: 0.5rem;
  font-style: italic;



}

/* the format for the timeline data table */
table.timelineTable {






  border: 0;


}

/* The row in the timeline table that contains the entry of interest */
tr.timelineSelected {
  border: 1px solid #eee;




  border-radius: 1rem;


}

tr.timelineSelected td.timelineTime
, tr.timelineSelected td.timelineTableCell {





  background-color: #333;
  box-shadow: 2px 2px 1px #000;
  padding: 0.5rem;
}

tr.timelineSelected td.timelineTime {


  border-radius: 1rem 0 0 1rem;

}

tr.timelineSelected td.timelineTableCell {









  border-radius: 0 1rem 1rem 0;
}

/* the format for the timeline data cells */
td.timelineTableCell {
  padding: 0.3rem;
  text-align: left;
  vertical-align: top;



}

td.timelineTableCell[style] {
  color: #000;



}

/* the format for the timeline data cell of the current checkout */
tr.timelineCurrent td.timelineTableCell {
  border: 0;










  border-radius: 1em 0em;

}

/* the format for the timeline leaf marks */











span.timelineLeaf {
  font-weight: bold;
}

/* the format for the timeline version links */
a.timelineHistLink {



}

/* the format for the timeline version display(no history permission!) */
span.timelineHistDsp {
  font-weight: bold;


}

/* the format for the timeline time display */
td.timelineTime {
  text-align: right;
  vertical-align: top;












  white-space: nowrap;
}

/* the format for the grap placeholder cells in timelines */
td.timelineGraph {
  text-align: left;
  vertical-align: top;
  width: 20px;
}


/**************************************
 * User Edit
 */

/* layout definition for the capabilities box on the user edit detail page */
div.ueditCapBox {
................................................................................
/* color for capabilities, inherited by developer */
span.ueditInheritDeveloper {
  color: #f00;
}

/* color for capabilities, inherited by reader */
span.ueditInheritReader {
  color: black;
}

/* color for capabilities, inherited by anonymous */
span.ueditInheritAnonymous {
  color: #00f;
}

................................................................................
  /* no special definitions, class defined, to enable color pickers,
  * f.e.:
  * ** add the color picker found at http:jscolor.com as java script
  * include
  * ** to the header and configure the java script file with
  * ** 1. use as bindClass :checkinUserColor
  * ** 2. change the default hash adding behaviour to ON
  * ** or change the class defition of element identified by
  * id="clrcust"
  * ** to a standard jscolor definition with java script in the footer.
  * */
}

/* format for end of content area, to be used to clear page flow. */
div.endContent {
................................................................................
  color: #f00;
  font-weight: bold;
}
/* format for artifact lines, no longer shunned */
p.noMoreShun {
  color: #00f;
}
/* format for artifact lines beeing shunned */
p.shunned {
  color: #00f;
}
/* a broken hyperlink */
span.brokenlink {
  color: #f00;
}







|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|

|



<
>
>
>
>
>
>
>
|
<
<
>
>
>


<
|
>
>
>
>
>
>
|
>
>


<
<
<
>
>
>
>
|
>
>


<
<
>
>
>
>
>
|
<
<


<
>
>

>


<
>
>
>
>
>
>
>
>
>



<
|
<
<
<
>
>
>


|
<
>
>
>


<
<
<
>
>
>
>
>
>
>
>
>
>
|
>


<
>
>
>
>
>
>
>
>
>
>
>
|



<
<
>
>
>


<
<
<
>
>


<
<
<
<
>
>
>
>
>
>
>
>
>
>
>
>
|


|
|
<
<
<









 







|







 







|







 







|







705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742

743
744
745
746
747
748
749
750


751
752
753
754
755

756
757
758
759
760
761
762
763
764
765
766
767



768
769
770
771
772
773
774
775
776


777
778
779
780
781
782


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
...
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
....
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
....
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
}


/**************************************
 * Timeline
 */

/* The suppressed duplicates lines in timeline, .. */
.timelineDisabled {
  font-size:  0.5rem;
  font-style: italic;
}

/* the format for the timeline version display(no history permission!) */
.timelineHistDsp {
  font-weight: bold;
}

.content .timelineTable {
  border:         0;
  border-spacing: 0 0.5rem;
}

.content .timelineTable tr {
  background: #222;
  border:     0;
  padding:    0;
  box-shadow: none;
}

.timelineTable .timelineDate {
  color:       #ee0;
  font-size:   1.2rem;
  font-weight: bold;
  margin-top:  1rem;
  white-space: nowrap;
}


.timelineTable .timelineTime {
  border-radius: 0;
  border-width:  0;
  padding:       0.25rem 0.5rem 0.5rem 0.5rem;
  white-space:   nowrap;
}

.timelineGraph {


  text-align:     left;
  vertical-align: top;
  width:          20px;
}


.timelineTable .timelineModernCell  ,
.timelineTable .timelineCompactCell ,
.timelineTable .timelineVerboseCell ,
.timelineTable .timelineDetailCell  {
/*
  background:    linear-gradient(to bottom, #222 0%, #333 16%, #222 100%);
*/
  border-radius: 0;
  border-width:  0;
  padding:       0.25rem 0.5rem 0.5rem 0.5rem;
}




.timelineTable .timelineColumnarCell {
/*
  background:    linear-gradient(to bottom, #222 0%, #333 16%, #222 100%);
*/
  border-radius: 0;
  border-width:  0;
  padding:       0.25rem 0.5rem 0.5rem 0.5rem;
}



.timelineTable .timelineModernCell[id]   ,
.timelineTable .timelineCompactCell[id]  ,
.timelineTable .timelineVerboseCell[id]  ,
.timelineTable .timelineColumnarCell[id] ,
.timelineTable .timelineDetailCell[id]   {
  background: #272727;


}


.timelineTable .timelineCurrent .timelineTime {
  background:    #333;
  border-radius: 1rem 0 0 1rem;
  border-width:  0;
}


.timelineTable .timelineCurrent .timelineColumnarCell {
  background:    #333;
}

.timelineTable .timelineCurrent .timelineModernCell  ,
.timelineTable .timelineCurrent .timelineCompactCell ,
.timelineTable .timelineCurrent .timelineVerboseCell ,
.timelineTable .timelineCurrent .timelineDetailCell  {
  background:    #333;
  border-radius: 0 1rem 1rem 0;
}


.timelineTable .timelineSelected {



  background: #222;
  border:     0;
  box-shadow: none;
}

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


.timelineTable .timelineModernCell  .timelineModernComment  ,
.timelineTable .timelineModernCell  .timelineModernDetail   ,
.timelineTable .timelineCompactCell .timelineCompactComment ,
.timelineTable .timelineCompactCell .timelineCompactDetail  ,
.timelineTable .timelineVerboseCell .timelineVerboseComment ,
.timelineTable .timelineVerboseCell .timelineVerboseDetail  {
}

.timelineTable .timelineModernCell     .timelineLeaf ,
.timelineTable .timelineCompactCell    .timelineLeaf ,
.timelineTable .timelineVerboseCell    .timelineLeaf ,
.timelineTable .timelineVerboseComment .timelineLeaf {
  font-weight: bold;
}



.timelineTable .timelineModernCell .timelineModernDetail ,
.timelineTable .timelineDetailCell {
  font-size: 85%;
}




.timelineTable .timelineDetailCell .timelineColumnarDetail {
  white-space: pre-line;
}





.timelineTable .timelineDetailCell ul.filelist::before {
  content: "files:";
}

.timelineTable .timelineDetailCell ul.filelist {
  margin-left:  0;
  padding-left: 0;
}

.timelineTable .timelineDetailCell ul.filelist li {
  margin-left:  1.5rem;
  padding-left: 0;
  white-space:  nowrap;
}

/* the format for the timeline version links */
a.timelineHistLink {



}


/**************************************
 * User Edit
 */

/* layout definition for the capabilities box on the user edit detail page */
div.ueditCapBox {
................................................................................
/* color for capabilities, inherited by developer */
span.ueditInheritDeveloper {
  color: #f00;
}

/* color for capabilities, inherited by reader */
span.ueditInheritReader {
  color: #ee0;
}

/* color for capabilities, inherited by anonymous */
span.ueditInheritAnonymous {
  color: #00f;
}

................................................................................
  /* no special definitions, class defined, to enable color pickers,
  * f.e.:
  * ** add the color picker found at http:jscolor.com as java script
  * include
  * ** to the header and configure the java script file with
  * ** 1. use as bindClass :checkinUserColor
  * ** 2. change the default hash adding behaviour to ON
  * ** or change the class definition of element identified by
  * id="clrcust"
  * ** to a standard jscolor definition with java script in the footer.
  * */
}

/* format for end of content area, to be used to clear page flow. */
div.endContent {
................................................................................
  color: #f00;
  font-weight: bold;
}
/* format for artifact lines, no longer shunned */
p.noMoreShun {
  color: #00f;
}
/* format for artifact lines being shunned */
p.shunned {
  color: #00f;
}
/* a broken hyperlink */
span.brokenlink {
  color: #f00;
}

Changes to skins/xekri/details.txt.

1
2
3
4
timeline-arrowheads:        1
timeline-circle-nodes:      0
timeline-color-graph-lines: 0
white-foreground:           0


|

1
2
3
4
timeline-arrowheads:        1
timeline-circle-nodes:      0
timeline-color-graph-lines: 1
white-foreground:           0

Changes to skins/xekri/footer.txt.

3
4
5
6
7
8
9
10
11
<div class="page-time">
Generated in <th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s
</div>
<div class="fossil-info">
Fossil v$release_version $manifest_version
</div>
</div>
</body>
</html>







<
<
3
4
5
6
7
8
9


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


Changes to skins/xekri/header.txt.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<html>
<head>
<base href="$baseurl/$current_page" />
<title>$<project_name>: $<title></title>
<link rel="alternate" type="application/rss+xml" title="RSS Feed"
      href="$home/timeline.rss" />
<link rel="stylesheet" href="$stylesheet_url" type="text/css"
      media="screen" />
</head>
<body>
<div class="header">
  <div class="logo">
    <th1>
    ##
    ## NOTE: The purpose of this procedure is to take the base URL of the
    ##       Fossil project and return the root of the entire web site using
    ##       the same URI scheme as the base URL (e.g. http or https).
<
<
<
<
<
<
<
<
<
<

















1
2
3
4
5
6
7










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

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
...
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
...
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
...
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
...
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
...
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
#include "config.h"
#include "add.h"
#include <assert.h>
#include <dirent.h>
#include "cygsup.h"

/*
** WARNING: For Fossil version 1.x this value was always zero.  For Fossil
**          2.x, 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 1.x behavior when using Fossil 2.x,
**          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

................................................................................
  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_wd_isexe(zFullname);
    db_multi_exec(
      "INSERT INTO vfile(vid,deleted,rid,mrid,pathname,isexe,islink)"
      "VALUES(%d,0,0,0,%Q,%d,%d)",
      vid, zPath, isExe, file_wd_islink(0));
    fossil_free(zFullname);
  }
  if( db_changes() ){
    fossil_print("ADDED  %s\n", zPath);
    return 1;
  }else{
    fossil_print("SKIP   %s\n", zPath);
................................................................................
    /* file_tree_name() throws a fatal error if g.argv[i] is outside of the
    ** checkout. */
    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_wd_isdir(zName);
    if( isDir==1 ){
      vfile_scan(&fullName, nRoot-1, scanFlags, pClean, pIgnore);
    }else if( isDir==0 ){
      fossil_warning("not found: %s", zName);
    }else{
      char *zTreeName = &zName[nRoot];
      if( !forceFlag && glob_match(pIgnore, zTreeName) ){
................................................................................
  );
  while( db_step(&q)==SQLITE_ROW ){
    const char *zFile;
    const char *zPath;

    zFile = db_column_text(&q, 0);
    zPath = db_column_text(&q, 1);
    if( !file_wd_isfile_or_link(zPath) ){
      if( !dryRunFlag ){
        db_multi_exec("UPDATE vfile SET deleted=1 WHERE pathname=%Q", zFile);
      }
      fossil_print("DELETED  %s\n", zFile);
      nDelete++;
    }
  }
................................................................................
  Stmt move;
  if( db_table_exists("temp", "fmove") ){
    db_prepare(&move, "SELECT x, y FROM fmove ORDER BY x;");
    while( db_step(&move)==SQLITE_ROW ){
      const char *zOldName = db_column_text(&move, 0);
      const char *zNewName = db_column_text(&move, 1);
      if( !dryRunFlag ){
        int isOldDir = file_isdir(zOldName);
        if( isOldDir==1 ){
          int isNewDir = file_isdir(zNewName);
          if( isNewDir==0 ){
            file_rename(zOldName, zNewName, isOldDir, isNewDir);
          }
        }else{
          if( file_wd_islink(zOldName) ){
            symlink_copy(zOldName, zNewName);
          }else{
            file_copy(zOldName, zNewName);
          }
          file_delete(zOldName);
        }
      }
................................................................................
  file_tree_name(zDest, &dest, 0, 1);
  db_multi_exec(
    "UPDATE vfile SET origname=pathname WHERE origname IS NULL;"
  );
  db_multi_exec(
    "CREATE TEMP TABLE mv(f TEXT UNIQUE ON CONFLICT IGNORE, t TEXT);"
  );
  if( file_wd_isdir(zDest)!=1 ){
    Blob orig;
    if( g.argc!=4 ){
      usage("OLDNAME NEWNAME");
    }
    file_tree_name(g.argv[2], &orig, 0, 1);
    db_multi_exec(
      "INSERT INTO mv VALUES(%B,%B)", &orig, &dest







|
|







|







 







|



|







 







|







 







|







 







|

|




|







 







|







21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
...
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
...
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
...
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
...
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
...
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
#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

................................................................................
  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);
    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));
    fossil_free(zFullname);
  }
  if( db_changes() ){
    fossil_print("ADDED  %s\n", zPath);
    return 1;
  }else{
    fossil_print("SKIP   %s\n", zPath);
................................................................................
    /* file_tree_name() throws a fatal error if g.argv[i] is outside of the
    ** checkout. */
    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);
    }else if( isDir==0 ){
      fossil_warning("not found: %s", zName);
    }else{
      char *zTreeName = &zName[nRoot];
      if( !forceFlag && glob_match(pIgnore, zTreeName) ){
................................................................................
  );
  while( db_step(&q)==SQLITE_ROW ){
    const char *zFile;
    const char *zPath;

    zFile = db_column_text(&q, 0);
    zPath = db_column_text(&q, 1);
    if( !file_isfile_or_link(zPath) ){
      if( !dryRunFlag ){
        db_multi_exec("UPDATE vfile SET deleted=1 WHERE pathname=%Q", zFile);
      }
      fossil_print("DELETED  %s\n", zFile);
      nDelete++;
    }
  }
................................................................................
  Stmt move;
  if( db_table_exists("temp", "fmove") ){
    db_prepare(&move, "SELECT x, y FROM fmove ORDER BY x;");
    while( db_step(&move)==SQLITE_ROW ){
      const char *zOldName = db_column_text(&move, 0);
      const char *zNewName = db_column_text(&move, 1);
      if( !dryRunFlag ){
        int isOldDir = file_isdir(zOldName, RepoFILE);
        if( isOldDir==1 ){
          int isNewDir = file_isdir(zNewName, RepoFILE);
          if( isNewDir==0 ){
            file_rename(zOldName, zNewName, isOldDir, isNewDir);
          }
        }else{
          if( file_islink(zOldName) ){
            symlink_copy(zOldName, zNewName);
          }else{
            file_copy(zOldName, zNewName);
          }
          file_delete(zOldName);
        }
      }
................................................................................
  file_tree_name(zDest, &dest, 0, 1);
  db_multi_exec(
    "UPDATE vfile SET origname=pathname WHERE origname IS NULL;"
  );
  db_multi_exec(
    "CREATE TEMP TABLE mv(f TEXT UNIQUE ON CONFLICT IGNORE, t TEXT);"
  );
  if( file_isdir(zDest, RepoFILE)!=1 ){
    Blob orig;
    if( g.argc!=4 ){
      usage("OLDNAME NEWNAME");
    }
    file_tree_name(g.argv[2], &orig, 0, 1);
    db_multi_exec(
      "INSERT INTO mv VALUES(%B,%B)", &orig, &dest

Changes to src/allrepo.c.

329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
...
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
    db_begin_transaction();
    for(j=3; j<g.argc; j++, blob_reset(&fn), blob_reset(&sql)){
      sqlite3 *db;
      int rc;
      const char *z;
      file_canonical_name(g.argv[j], &fn, 0);
      z = blob_str(&fn);
      if( !file_isfile(z) ) continue;
      g.dbIgnoreErrors++;
      rc = sqlite3_open(z, &db);
      if( rc!=SQLITE_OK ){ sqlite3_close(db); g.dbIgnoreErrors--; continue; }
      rc = sqlite3_exec(db, "SELECT rcvid FROM blob, delta LIMIT 1", 0, 0, 0);
      sqlite3_close(db);
      g.dbIgnoreErrors--;
      if( rc!=SQLITE_OK ) continue;
................................................................................
    int rc;
    const char *zFilename = db_column_text(&q, 0);
#if !USE_SEE
    if( sqlite3_strglob("*.efossil", zFilename)==0 ) continue;
#endif
    if( file_access(zFilename, F_OK)
     || !file_is_canonical(zFilename)
     || (useCheckouts && file_isdir(zFilename)!=1)
    ){
      db_multi_exec("INSERT INTO toDel VALUES(%Q)", db_column_text(&q, 1));
      nToDel++;
      continue;
    }
    if( zCmd[0]=='l' ){
      fossil_print("%s\n", zFilename);







|







 







|







329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
...
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
    db_begin_transaction();
    for(j=3; j<g.argc; j++, blob_reset(&fn), blob_reset(&sql)){
      sqlite3 *db;
      int rc;
      const char *z;
      file_canonical_name(g.argv[j], &fn, 0);
      z = blob_str(&fn);
      if( !file_isfile(z, ExtFILE) ) continue;
      g.dbIgnoreErrors++;
      rc = sqlite3_open(z, &db);
      if( rc!=SQLITE_OK ){ sqlite3_close(db); g.dbIgnoreErrors--; continue; }
      rc = sqlite3_exec(db, "SELECT rcvid FROM blob, delta LIMIT 1", 0, 0, 0);
      sqlite3_close(db);
      g.dbIgnoreErrors--;
      if( rc!=SQLITE_OK ) continue;
................................................................................
    int rc;
    const char *zFilename = db_column_text(&q, 0);
#if !USE_SEE
    if( sqlite3_strglob("*.efossil", zFilename)==0 ) continue;
#endif
    if( file_access(zFilename, F_OK)
     || !file_is_canonical(zFilename)
     || (useCheckouts && file_isdir(zFilename, ExtFILE)!=1)
    ){
      db_multi_exec("INSERT INTO toDel VALUES(%Q)", db_column_text(&q, 1));
      nToDel++;
      continue;
    }
    if( zCmd[0]=='l' ){
      fossil_print("%s\n", zFilename);

Changes to src/attach.c.

762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
      }
      zTarget = db_text(0,
        "SELECT substr(tagname,7) FROM tag WHERE tagid=(SELECT tagid FROM event WHERE objid='%d')",
        rid
      );
      zFile = g.argv[3];
    }
    blob_read_from_file(&content, zFile);
    user_select();
    attach_commit(
      zFile,                   /* The filename of the attachment */
      zTarget,                 /* The artifact uuid 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 */







|







762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
      }
      zTarget = db_text(0,
        "SELECT substr(tagname,7) FROM tag WHERE tagid=(SELECT tagid FROM event WHERE objid='%d')",
        rid
      );
      zFile = g.argv[3];
    }
    blob_read_from_file(&content, zFile, ExtFILE);
    user_select();
    attach_commit(
      zFile,                   /* The filename of the attachment */
      zTarget,                 /* The artifact uuid 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 */

Changes to src/blob.c.

781
782
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
...
840
841
842
843
844
845
846
847
848
849
850
851





852
853
854
855
856
857
858
...
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
...
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
...
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
....
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
  }
  return blob_size(pBlob);
}

/*
** Initialize a blob to be the content of a file.  If the filename
** is blank or "-" then read from standard input.











**
** Any prior content of the blob is discarded, not freed.
**
** Return the number of bytes read. Calls fossil_fatal() on error (i.e.
** it exit()s and does not return).
*/
sqlite3_int64 blob_read_from_file(Blob *pBlob, const char *zFilename){




  sqlite3_int64 size, got;
  FILE *in;
  if( zFilename==0 || zFilename[0]==0
        || (zFilename[0]=='-' && zFilename[1]==0) ){
    return blob_read_from_channel(pBlob, stdin, -1);
  }



  size = file_wd_size(zFilename);
  blob_zero(pBlob);
  if( size<0 ){
    fossil_fatal("no such file: %s", zFilename);
  }
  if( size==0 ){
    return 0;
  }
................................................................................
  return len;
#else
  blob_zero(pBlob);
  return 0;
#endif
}


/*
** Write the content of a blob into a file.
**
** If the filename is blank or "-" then write to standard output.





**
** Return the number of bytes written.
*/
int blob_write_to_file(Blob *pBlob, const char *zFilename){
  FILE *out;
  int nWrote;

................................................................................
#endif
    nWrote = fwrite(blob_buffer(pBlob), 1, blob_size(pBlob), stdout);
#if defined(_WIN32)
    fflush(stdout);
    _setmode(_fileno(stdout), _O_TEXT);
#endif
  }else{
    file_mkfolder(zFilename, 1, 0);
    out = fossil_fopen(zFilename, "wb");
    if( out==0 ){
#if _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);
................................................................................
** Run compression on INPUTFILE and write the result into OUTPUTFILE.
**
** This is used to test and debug the blob_compress() routine.
*/
void compress_cmd(void){
  Blob f;
  if( g.argc!=4 ) usage("INPUTFILE OUTPUTFILE");
  blob_read_from_file(&f, g.argv[2]);
  blob_compress(&f, &f);
  blob_write_to_file(&f, g.argv[3]);
}

/*
** Compress the concatenation of a blobs pIn1 and pIn2.  Store the result
** in pOut.
................................................................................
** content, then write results into OUT.
**
** This is used to test and debug the blob_compress2() routine.
*/
void compress2_cmd(void){
  Blob f1, f2;
  if( g.argc!=5 ) usage("INPUTFILE1 INPUTFILE2 OUTPUTFILE");
  blob_read_from_file(&f1, g.argv[2]);
  blob_read_from_file(&f2, g.argv[3]);
  blob_compress2(&f1, &f2, &f1);
  blob_write_to_file(&f1, g.argv[4]);
}

/*
** Uncompress blob pIn and store the result in pOut.  It is ok for pIn and
** pOut to be the same blob.
................................................................................
** Read the content of file IN, uncompress that content, and write the
** result into OUT.  This command is intended for testing of the
** blob_compress() function.
*/
void uncompress_cmd(void){
  Blob f;
  if( g.argc!=4 ) usage("INPUTFILE OUTPUTFILE");
  blob_read_from_file(&f, g.argv[2]);
  blob_uncompress(&f, &f);
  blob_write_to_file(&f, g.argv[3]);
}

/*
** COMMAND: test-cycle-compress
**
................................................................................
** Compress and uncompress each file named on the command line.
** Verify that the original content is recovered.
*/
void test_cycle_compress(void){
  int i;
  Blob b1, b2, b3;
  for(i=2; i<g.argc; i++){
    blob_read_from_file(&b1, g.argv[i]);
    blob_compress(&b1, &b2);
    blob_uncompress(&b2, &b3);
    if( blob_compare(&b1, &b3) ){
      fossil_fatal("compress/uncompress cycle failed for %s", g.argv[i]);
    }
    blob_reset(&b1);
    blob_reset(&b2);







>
>
>
>
>
>
>
>
>
>
>






|
>
>
>
>






>
>
>
|







 







<




>
>
>
>
>







 







|







 







|







 







|
|







 







|







 







|







781
782
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
...
858
859
860
861
862
863
864

865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
...
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
...
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
....
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
....
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
  }
  return blob_size(pBlob);
}

/*
** Initialize a blob to be the content of a file.  If the filename
** is blank or "-" then read from standard input.
**
** If zFilename is a symbolic link, behavior depends on the eFType
** parameter:
**
**    *  If eFType is ExtFILE or allow-symlinks is OFF, then the
**       pBlob is initialized to the *content* of the object to which
**       the zFilename symlink points.
**
**    *  If eFType is RepoFILE and allow-symlinks is ON, then the
**       pBlob is initialized to the *name* of the object to which
**       the zFilename symlink points.
**
** Any prior content of the blob is discarded, not freed.
**
** Return the number of bytes read. Calls fossil_fatal() on error (i.e.
** it exit()s and does not return).
*/
sqlite3_int64 blob_read_from_file(
  Blob *pBlob,               /* The blob to be initialized */
  const char *zFilename,     /* Extract content from this file */
  int eFType                 /* ExtFILE or RepoFILE - see above */
){
  sqlite3_int64 size, got;
  FILE *in;
  if( zFilename==0 || zFilename[0]==0
        || (zFilename[0]=='-' && zFilename[1]==0) ){
    return blob_read_from_channel(pBlob, stdin, -1);
  }
  if( file_islink(zFilename) ){
    return blob_read_link(pBlob, zFilename);
  }
  size = file_size(zFilename, eFType);
  blob_zero(pBlob);
  if( size<0 ){
    fossil_fatal("no such file: %s", zFilename);
  }
  if( size==0 ){
    return 0;
  }
................................................................................
  return len;
#else
  blob_zero(pBlob);
  return 0;
#endif
}


/*
** Write the content of a blob into a file.
**
** If the filename is blank or "-" then write to standard output.
**
** This routine always assumes ExtFILE.  If zFilename is a symbolic link
** then the content is written into the object that symbolic link points
** to, not into the symbolic link itself.  This is true regardless of
** the allow-symlinks setting.
**
** Return the number of bytes written.
*/
int blob_write_to_file(Blob *pBlob, const char *zFilename){
  FILE *out;
  int nWrote;

................................................................................
#endif
    nWrote = fwrite(blob_buffer(pBlob), 1, blob_size(pBlob), stdout);
#if defined(_WIN32)
    fflush(stdout);
    _setmode(_fileno(stdout), _O_TEXT);
#endif
  }else{
    file_mkfolder(zFilename, ExtFILE, 1, 0);
    out = fossil_fopen(zFilename, "wb");
    if( out==0 ){
#if _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);
................................................................................
** Run compression on INPUTFILE and write the result into OUTPUTFILE.
**
** This is used to test and debug the blob_compress() routine.
*/
void compress_cmd(void){
  Blob f;
  if( g.argc!=4 ) usage("INPUTFILE OUTPUTFILE");
  blob_read_from_file(&f, g.argv[2], ExtFILE);
  blob_compress(&f, &f);
  blob_write_to_file(&f, g.argv[3]);
}

/*
** Compress the concatenation of a blobs pIn1 and pIn2.  Store the result
** in pOut.
................................................................................
** content, then write results into OUT.
**
** This is used to test and debug the blob_compress2() routine.
*/
void compress2_cmd(void){
  Blob f1, f2;
  if( g.argc!=5 ) usage("INPUTFILE1 INPUTFILE2 OUTPUTFILE");
  blob_read_from_file(&f1, g.argv[2], ExtFILE);
  blob_read_from_file(&f2, g.argv[3], ExtFILE);
  blob_compress2(&f1, &f2, &f1);
  blob_write_to_file(&f1, g.argv[4]);
}

/*
** Uncompress blob pIn and store the result in pOut.  It is ok for pIn and
** pOut to be the same blob.
................................................................................
** Read the content of file IN, uncompress that content, and write the
** result into OUT.  This command is intended for testing of the
** blob_compress() function.
*/
void uncompress_cmd(void){
  Blob f;
  if( g.argc!=4 ) usage("INPUTFILE OUTPUTFILE");
  blob_read_from_file(&f, g.argv[2], ExtFILE);
  blob_uncompress(&f, &f);
  blob_write_to_file(&f, g.argv[3]);
}

/*
** COMMAND: test-cycle-compress
**
................................................................................
** Compress and uncompress each file named on the command line.
** Verify that the original content is recovered.
*/
void test_cycle_compress(void){
  int i;
  Blob b1, b2, b3;
  for(i=2; i<g.argc; i++){
    blob_read_from_file(&b1, g.argv[i], ExtFILE);
    blob_compress(&b1, &b2);
    blob_uncompress(&b2, &b3);
    if( blob_compare(&b1, &b3) ){
      fossil_fatal("compress/uncompress cycle failed for %s", g.argv[i]);
    }
    blob_reset(&b1);
    blob_reset(&b2);

Changes to src/branch.c.

391
392
393
394
395
396
397
398

399
400
401
402
403
404
405
...
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
  style_header("Branches");
  style_adunit_config(ADUNIT_RIGHT_OK);
  style_submenu_checkbox("colors", "Use Branch Colors", 0, 0);
  login_anonymous_available();

  db_prepare(&q, brlistQuery/*works-like:""*/);
  rNow = db_double(0.0, "SELECT julianday('now')");
  @ <div class="brlist"><table id="branchlisttable">

  @ <thead><tr>
  @ <th>Branch Name</th>
  @ <th>Age</th>
  @ <th>Check-ins</th>
  @ <th>Status</th>
  @ <th>Resolution</th>
  @ </tr></thead><tbody>
................................................................................
    }else{
      @ <td></td>
    }
    @ </tr>
  }
  @ </tbody></table></div>
  db_finalize(&q);
  output_table_sorting_javascript("branchlisttable","tkNtt",2);
  style_footer();
}

/*
** WEBPAGE: brlist
** Show a list of branches.  With no query parameters, a sortable table
** is used to show all branches.  If query parameters are present a







|
>







 







|







391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
...
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
  style_header("Branches");
  style_adunit_config(ADUNIT_RIGHT_OK);
  style_submenu_checkbox("colors", "Use Branch Colors", 0, 0);
  login_anonymous_available();

  db_prepare(&q, brlistQuery/*works-like:""*/);
  rNow = db_double(0.0, "SELECT julianday('now')");
  @ <div class="brlist">
  @ <table class='sortable' data-column-types='tkNtt' data-init-sort='2'>
  @ <thead><tr>
  @ <th>Branch Name</th>
  @ <th>Age</th>
  @ <th>Check-ins</th>
  @ <th>Status</th>
  @ <th>Resolution</th>
  @ </tr></thead><tbody>
................................................................................
    }else{
      @ <td></td>
    }
    @ </tr>
  }
  @ </tbody></table></div>
  db_finalize(&q);
  style_table_sorter();
  style_footer();
}

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

Changes to src/browse.c.

195
196
197
198
199
200
201
202




203
204
205
206
207
208
209
...
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
....
1144
1145
1146
1147
1148
1149
1150
1151

1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
    @ </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))</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));

  /* Compute the temporary table "localfiles" containing the names
................................................................................
      while( nClose-- > 0 ){
        @ </ul>
      }
    }
  }
  @ </ul>
  @ </ul></div>
  @ <script>(function(){
  @ function isExpanded(ul){
  @   return ul.className=='';
  @ }
  @
  @ function toggleDir(ul, useInitValue){
  @   if( !useInitValue ){
  @     expandMap[ul.id] = !isExpanded(ul);
  @     history.replaceState(expandMap, '');
  @   }
  @   ul.className = expandMap[ul.id] ? '' : 'collapsed';
  @ }
  @
  @ function toggleAll(tree, useInitValue){
  @   var lists = tree.querySelectorAll('.subdir > ul > li ul');
  @   if( !useInitValue ){
  @     var expand = true;  /* Default action: make all sublists visible */
  @     for( var i=0; lists[i]; i++ ){
  @       if( isExpanded(lists[i]) ){
  @         expand = false; /* Any already visible - make them all hidden */
  @         break;
  @       }
  @     }
  @     expandMap = {'*': expand};
  @     history.replaceState(expandMap, '');
  @   }
  @   var className = expandMap['*'] ? '' : 'collapsed';
  @   for( var i=0; lists[i]; i++ ){
  @     lists[i].className = className;
  @   }
  @ }
  @
  @ function checkState(){
  @   expandMap = history.state || {};
  @   if( '*' in expandMap ) toggleAll(outer_ul, true);
  @   for( var id in expandMap ){
  @     if( id!=='*' ) toggleDir(gebi(id), true);
  @   }
  @ }
  @
  @ function belowSubdir(node){
  @   do{
  @     node = node.parentNode;
  @     if( node==subdir ) return true;
  @   } while( node && node!=outer_ul );
  @   return false;
  @ }
  @
  @ var history = window.history || {};
  @ if( !history.replaceState ) history.replaceState = function(){};
  @ var outer_ul = document.querySelector('.filetree > ul');
  @ var subdir = outer_ul.querySelector('.subdir');
  @ var expandMap = {};
  @ checkState();
  @ outer_ul.onclick = function(e){
  @   e = e || window.event;
  @   var a = e.target || e.srcElement;
  @   if( a.nodeName!='A' ) return true;
  @   if( a.parentNode.parentNode==subdir ){
  @     toggleAll(outer_ul);
  @     return false;
  @   }
  @   if( !belowSubdir(a) ) return true;
  @   var ul = a.parentNode.nextSibling;
  @   while( ul && ul.nodeName!='UL' ) ul = ul.nextSibling;
  @   if( !ul ) return true; /* This is a file link, not a directory */
  @   toggleDir(ul);
  @   return false;
  @ }
  @ }())</script>
  style_footer();

  /* We could free memory used by sTree here if we needed to.  But
  ** the process is about to exit, so doing so would not really accomplish
  ** anything useful. */
}

................................................................................
      }else{
        @ %z(href("%R/artifact/%!S",zFUuid))%h(zFile)</a><br />
      }
    }
    db_reset(&q2);
    @ </td>
    @ <td>
    @ %z(href("%R/info/%!S",zUuid))[%S(zUuid)]</a>

    if( showId ){
      @ (%d(mid))
    }
    @ %W(zComment) (user:
    @ %z(href("%R/timeline?u=%t&c=%!S&nd&n=200",zUser,zUuid))%h(zUser)</a>,
    @ branch:
    @ %z(href("%R/timeline?r=%t&c=%!S&nd&n=200",zBranch,zUuid))%h(zBranch)</a>)
    @ </td></tr>
    @
    fossil_free(zAge);
  }
  @ </table></div>
  db_finalize(&q1);
  db_finalize(&q2);
  style_footer();
}







|
>
>
>
>







 







|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







 







|
>

|

<
|
|
|









195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
...
845
846
847
848
849
850
851
852





































































853
854
855
856
857
858
859
....
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090

1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
    @ </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 ){
      @ &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));

  /* Compute the temporary table "localfiles" containing the names
................................................................................
      while( nClose-- > 0 ){
        @ </ul>
      }
    }
  }
  @ </ul>
  @ </ul></div>
  style_load_one_js_file("tree.js");





































































  style_footer();

  /* We could free memory used by sTree here if we needed to.  But
  ** the process is about to exit, so doing so would not really accomplish
  ** anything useful. */
}

................................................................................
      }else{
        @ %z(href("%R/artifact/%!S",zFUuid))%h(zFile)</a><br />
      }
    }
    db_reset(&q2);
    @ </td>
    @ <td>
    @ %W(zComment)
    @ (check-in:&nbsp;%z(href("%R/ci/%!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();
}

Changes to src/builtin.c.

62
63
64
65
66
67
68

















69
70
71
72
73
74
75
*/
void test_builtin_list(void){
  int i;
  for(i=0; i<count(aBuiltinFiles); i++){
    fossil_print("%-30s %6d\n", aBuiltinFiles[i].zName,aBuiltinFiles[i].nByte);
  }
}


















/*
** COMMAND: test-builtin-get
**
** Usage: %fossil test-builtin-get NAME ?OUTPUT-FILE?
*/
void test_builtin_get(void){







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
*/
void test_builtin_list(void){
  int i;
  for(i=0; i<count(aBuiltinFiles); i++){
    fossil_print("%-30s %6d\n", aBuiltinFiles[i].zName,aBuiltinFiles[i].nByte);
  }
}

/*
** WEBPAGE: test-builtin-files
**
** Show all built-in text files.
*/
void test_builtin_list_page(void){
  int i;
  style_header("Built-in Text Files");
  @ <ul>
  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>
  }
  @ </ul>
  style_footer();
}

/*
** COMMAND: test-builtin-get
**
** Usage: %fossil test-builtin-get NAME ?OUTPUT-FILE?
*/
void test_builtin_get(void){

Changes to src/bundle.c.

53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
...
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
  const char *zFile,       /* Name of the file that contains the bundle */
  const char *zBName,      /* Attachment name */
  int doInit               /* Initialize a new bundle, if true */
){
  int rc;
  char *zErrMsg = 0;
  char *zSql;
  if( !doInit && file_size(zFile)<0 ){
    fossil_fatal("no such file: %s", zFile);
  }
  assert( g.db );
  zSql = sqlite3_mprintf("ATTACH %Q AS %Q", zFile, zBName);
  if( zSql==0 ) fossil_fatal("out of memory");
  rc = sqlite3_exec(g.db, zSql, 0, 0, &zErrMsg);
  sqlite3_free(zSql);
................................................................................
  bundle_attach_file(g.argv[3], "b1", 1);
  db_prepare(&q,
    "INSERT INTO bblob(blobid, uuid, sz, delta, data, notes) "
    "VALUES(NULL, $uuid, $sz, NULL, $data, $filename)");
  db_begin_transaction();
  for(i=4; i<g.argc; i++){
    int sz;
    blob_read_from_file(&content, g.argv[i]);
    sz = blob_size(&content);
    sha1sum_blob(&content, &hash);
    blob_compress(&content, &content);
    db_bind_text(&q, "$uuid", blob_str(&hash));
    db_bind_int(&q, "$sz", sz);
    db_bind_blob(&q, "$data", &content);
    db_bind_text(&q, "$filename", g.argv[i]);







|







 







|







53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
...
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
  const char *zFile,       /* Name of the file that contains the bundle */
  const char *zBName,      /* Attachment name */
  int doInit               /* Initialize a new bundle, if true */
){
  int rc;
  char *zErrMsg = 0;
  char *zSql;
  if( !doInit && file_size(zFile, ExtFILE)<0 ){
    fossil_fatal("no such file: %s", zFile);
  }
  assert( g.db );
  zSql = sqlite3_mprintf("ATTACH %Q AS %Q", zFile, zBName);
  if( zSql==0 ) fossil_fatal("out of memory");
  rc = sqlite3_exec(g.db, zSql, 0, 0, &zErrMsg);
  sqlite3_free(zSql);
................................................................................
  bundle_attach_file(g.argv[3], "b1", 1);
  db_prepare(&q,
    "INSERT INTO bblob(blobid, uuid, sz, delta, data, notes) "
    "VALUES(NULL, $uuid, $sz, NULL, $data, $filename)");
  db_begin_transaction();
  for(i=4; i<g.argc; i++){
    int sz;
    blob_read_from_file(&content, g.argv[i], ExtFILE);
    sz = blob_size(&content);
    sha1sum_blob(&content, &hash);
    blob_compress(&content, &content);
    db_bind_text(&q, "$uuid", blob_str(&hash));
    db_bind_int(&q, "$sz", sz);
    db_bind_blob(&q, "$data", &content);
    db_bind_text(&q, "$filename", g.argv[i]);

Changes to src/cache.c.

47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
...
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
...
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
  sqlite3 *db = 0;
  int rc;
  i64 sz;

  zDbName = cacheName();
  if( zDbName==0 ) return 0;
  if( bForce==0 ){
    sz = file_size(zDbName);
    if( sz<=0 ){
      fossil_free(zDbName);
      return 0;
    }
  }
  rc = sqlite3_open(zDbName, &db);
  fossil_free(zDbName);
................................................................................
             sqlite3_column_text(pStmt, 0));
          nEntry++;
        }
        sqlite3_finalize(pStmt);
      }
      sqlite3_close(db);
      fossil_print("Entries: %d  Cache-file Size: %lld\n",
                   nEntry, file_size(zDbName));
      fossil_free(zDbName);
    }
  }else if( strncmp(zCmd, "status", nCmd)==0 ){
    fossil_print("TBD...\n");
  }else{
    fossil_fatal("Unknown subcommand \"%s\"."
                 " Should be one of: clear init list status", zCmd);
................................................................................
        @ hit-count: %d(sqlite3_column_int(pStmt,2))
        @ last-access: %s(sqlite3_column_text(pStmt,3))</p></li>
      }
      sqlite3_finalize(pStmt);
      @ </ol>
    }
    zDbName = cacheName();
    bigSizeName(sizeof(zBuf), zBuf, file_size(zDbName));
    @ <p>cache-file name: %h(zDbName)</p>
    @ <p>cache-file size: %s(zBuf)</p>
    fossil_free(zDbName);
    sqlite3_close(db);
  }
  style_footer();
}







|







 







|







 







|







47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
...
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
...
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
  sqlite3 *db = 0;
  int rc;
  i64 sz;

  zDbName = cacheName();
  if( zDbName==0 ) return 0;
  if( bForce==0 ){
    sz = file_size(zDbName, ExtFILE);
    if( sz<=0 ){
      fossil_free(zDbName);
      return 0;
    }
  }
  rc = sqlite3_open(zDbName, &db);
  fossil_free(zDbName);
................................................................................
             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_free(zDbName);
    }
  }else if( strncmp(zCmd, "status", nCmd)==0 ){
    fossil_print("TBD...\n");
  }else{
    fossil_fatal("Unknown subcommand \"%s\"."
                 " Should be one of: clear init list status", zCmd);
................................................................................
        @ hit-count: %d(sqlite3_column_int(pStmt,2))
        @ last-access: %s(sqlite3_column_text(pStmt,3))</p></li>
      }
      sqlite3_finalize(pStmt);
      @ </ol>
    }
    zDbName = cacheName();
    bigSizeName(sizeof(zBuf), zBuf, file_size(zDbName, ExtFILE));
    @ <p>cache-file name: %h(zDbName)</p>
    @ <p>cache-file size: %s(zBuf)</p>
    fossil_free(zDbName);
    sqlite3_close(db);
  }
  style_footer();
}

Changes to src/cgi.c.

207
208
209
210
211
212
213

214


215
216
217
218
219
220
221
...
451
452
453
454
455
456
457























458
459
460
461
462
463
464
....
1338
1339
1340
1341
1342
1343
1344
1345


1346
1347
1348
1349
1350
1351
1352
1353
....
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758


1759
1760
1761
1762
1763
1764
1765
....
1775
1776
1777
1778
1779
1780
1781

1782
1783
1784
1785
1786
1787
1788
....
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
....
1877
1878
1879
1880
1881
1882
1883


1884
1885
1886
1887
1888
1889




1890
1891

1892
1893
1894
1895
1896
1897
1898
....
2012
2013
2014
2015
2016
2017
2018









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 *zSecure = "";

  if( zPath==0 ) zPath = g.zTop;


  if( g.zBaseURL!=0 && strncmp(g.zBaseURL, "https:", 6)==0 ){
    zSecure = " secure;";
  }
  if( lifetime>0 ){
    lifetime += (int)time(0);
    blob_appendf(&extraHeader,
       "Set-Cookie: %s=%t; Path=%s; expires=%z; HttpOnly;%s Version=1\r\n",
................................................................................
  const char *zRef = P("referer");
  if( zRef==0 ){
    zRef = P("HTTP_REFERER");
    if( zRef==0 ) zRef = zDefault;
  }
  return zRef;
}
























/*
** Information about all query parameters and cookies are stored
** in these variables.
*/
static int nAllocQP = 0; /* Space allocated for aParamQP[] */
static int nUsedQP = 0;  /* Space actually used in aParamQP[] */
................................................................................
/* z[] is the value of an X-FORWARDED-FOR: line in an HTTP header.
** Return a pointer to a string containing the real IP address, or a
** NULL pointer to stick with the IP address previously computed and
** loaded into g.zIpAddr.
*/
static const char *cgi_accept_forwarded_for(const char *z){
  int i;
  if( fossil_strcmp(g.zIpAddr, "127.0.0.1")!=0 ) return 0;



  i = strlen(z)-1;
  while( i>=0 && z[i]!=',' && !fossil_isspace(z[i]) ) i--;
  return &z[++i];
}

/*
** Remove the first space-delimited token from a string and return
................................................................................
#define HTTP_SERVER_HAD_CHECKOUT   0x0008     /* Was a checkout open? */
#define HTTP_SERVER_REPOLIST       0x0010     /* Allow repo listing */

#endif /* INTERFACE */

/*
** Maximum number of child processes that we can have running
** at one time before we start slowing things down.
*/
#define MAX_PARALLEL 2



/*
** Implement an HTTP server daemon listening on port iPort.
**
** As new connections arrive, fork a child and let child return
** out of this procedure call.  The child will handle the request.
** The parent never returns from this procedure.
................................................................................
){
#if defined(_WIN32)
  /* Use win32_http_server() instead */
  fossil_exit(1);
#else
  int listener = -1;           /* The server socket */
  int connection;              /* A socket for each individual connection */

  fd_set readfds;              /* Set of file descriptors for select() */
  socklen_t lenaddr;           /* Length of the inaddr structure */
  int child;                   /* PID of the child process */
  int nchildren = 0;           /* Number of child processes */
  struct timeval delay;        /* How long to wait inside select() */
  struct sockaddr_in inaddr;   /* The socket address */
  int opt = 1;                 /* setsockopt flag */
................................................................................
    }else
#endif
    if( system(zBrowser)<0 ){
      fossil_warning("cannot start browser: %s\n", zBrowser);
    }
  }
  while( 1 ){
    if( nchildren>MAX_PARALLEL ){
      /* Slow down if connections are arriving too fast */
      sleep( nchildren-MAX_PARALLEL );
    }

    delay.tv_sec = 60;
    delay.tv_usec = 0;
    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 ){
        child = fork();
        if( child!=0 ){
          if( child>0 ) nchildren++;



          close(connection);
        }else{
          int nErr = 0, fd;
          close(0);
          fd = dup(connection);
          if( fd!=0 ) nErr++;
          close(1);
................................................................................
          if( fd!=1 ) nErr++;
          if( !g.fAnyTrace ){
            close(2);
            fd = dup(connection);
            if( fd!=2 ) nErr++;
          }
          close(connection);


          return nErr;
        }
      }
    }
    /* Bury dead children */
    while( waitpid(0, 0, WNOHANG)>0 ){




      nchildren--;
    }

  }
  /* NOT REACHED */
  fossil_exit(1);
#endif
  /* NOT REACHED */
  return 0;
}
................................................................................
    if( (zIndex = strchr(zSshClient,' '))!=0 ){
      zSshClient[zIndex-zSshClient] = '\0';
      return zSshClient;
    }
  }
  return zDefault;
}
















>
|
>
>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







|
>
>
|







 







|

|
>
>







 







>







 







|
|
|

>
|
|










|
>
>
>







 







>
>





|
>
>
>
>
|
|
>







 







>
>
>
>
>
>
>
>
>
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
...
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
....
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
....
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
....
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
....
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
....
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
....
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
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 *zSecure = "";
  if( zPath==0 ){
    zPath = g.zTop;
    if( zPath[0]==0 ) zPath = "/";
  }
  if( g.zBaseURL!=0 && strncmp(g.zBaseURL, "https:", 6)==0 ){
    zSecure = " secure;";
  }
  if( lifetime>0 ){
    lifetime += (int)time(0);
    blob_appendf(&extraHeader,
       "Set-Cookie: %s=%t; Path=%s; expires=%z; HttpOnly;%s Version=1\r\n",
................................................................................
  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
** 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 nBase;
  if( zRef==0 ) return 0;
  if( requirePost ){
    const char *zMethod = P("REQUEST_METHOD");
    if( zMethod==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( zRef[nBase]!=0 && zRef[nBase]!='/' ) return 0;
  return 1;
}

/*
** Information about all query parameters and cookies are stored
** in these variables.
*/
static int nAllocQP = 0; /* Space allocated for aParamQP[] */
static int nUsedQP = 0;  /* Space actually used in aParamQP[] */
................................................................................
/* z[] is the value of an X-FORWARDED-FOR: line in an HTTP header.
** Return a pointer to a string containing the real IP address, or a
** NULL pointer to stick with the IP address previously computed and
** loaded into g.zIpAddr.
*/
static const char *cgi_accept_forwarded_for(const char *z){
  int i;
  if( !cgi_is_loopback(g.zIpAddr) ){
    /* Only accept X-FORWARDED-FOR if input coming from the local machine */
    return 0;
  }
  i = strlen(z)-1;
  while( i>=0 && z[i]!=',' && !fossil_isspace(z[i]) ) i--;
  return &z[++i];
}

/*
** Remove the first space-delimited token from a string and return
................................................................................
#define HTTP_SERVER_HAD_CHECKOUT   0x0008     /* Was a checkout open? */
#define HTTP_SERVER_REPOLIST       0x0010     /* Allow repo listing */

#endif /* INTERFACE */

/*
** Maximum number of child processes that we can have running
** at one time.  Set this to 0 for "no limit".
*/
#ifndef FOSSIL_MAX_CONNECTIONS
# define FOSSIL_MAX_CONNECTIONS 1000
#endif

/*
** Implement an HTTP server daemon listening on port iPort.
**
** As new connections arrive, fork a child and let child return
** out of this procedure call.  The child will handle the request.
** The parent never returns from this procedure.
................................................................................
){
#if defined(_WIN32)
  /* Use win32_http_server() instead */
  fossil_exit(1);
#else
  int listener = -1;           /* The server socket */
  int connection;              /* A socket for each individual connection */
  int nRequest = 0;            /* Number of requests handled so far */
  fd_set readfds;              /* Set of file descriptors for select() */
  socklen_t lenaddr;           /* Length of the inaddr structure */
  int child;                   /* PID of the child process */
  int nchildren = 0;           /* Number of child processes */
  struct timeval delay;        /* How long to wait inside select() */
  struct sockaddr_in inaddr;   /* The socket address */
  int opt = 1;                 /* setsockopt flag */
................................................................................
    }else
#endif
    if( 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 ){
        child = fork();
        if( child!=0 ){
          if( child>0 ){
            nchildren++;
            nRequest++;
          }
          close(connection);
        }else{
          int nErr = 0, fd;
          close(0);
          fd = dup(connection);
          if( fd!=0 ) nErr++;
          close(1);
................................................................................
          if( fd!=1 ) nErr++;
          if( !g.fAnyTrace ){
            close(2);
            fd = dup(connection);
            if( fd!=2 ) nErr++;
          }
          close(connection);
          g.nPendingRequest = nchildren+1;
          g.nRequest = nRequest+1;
          return nErr;
        }
      }
    }
    /* Bury dead children */
    if( nchildren ){
      while(1){
        int iStatus = 0;
        pid_t x = waitpid(-1, &iStatus, WNOHANG);
        if( x<=0 ) break;
        nchildren--;
      }
    }  
  }
  /* NOT REACHED */
  fossil_exit(1);
#endif
  /* NOT REACHED */
  return 0;
}
................................................................................
    if( (zIndex = strchr(zSshClient,' '))!=0 ){
      zSshClient[zIndex-zSshClient] = '\0';
      return zSshClient;
    }
  }
  return zDefault;
}

/*
** Return true if information is coming from the loopback network.
*/
int cgi_is_loopback(const char *zIpAddr){
  return fossil_strcmp(zIpAddr, "127.0.0.1")==0 ||
         fossil_strcmp(zIpAddr, "::ffff:127.0.0.1")==0 ||
         fossil_strcmp(zIpAddr, "::1")==0;
}

Changes to src/checkin.c.

84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
...
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
...
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
...
469
470
471
472
473
474
475


476
477
478
479
480
481
482
...
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
....
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
....
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
....
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
....
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
....
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
....
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
    blob_init(&name, g.zLocalRoot, nRoot - 1);
    vfile_scan(&name, blob_size(&name), scanFlags, pIgnore, 0);
    blob_reset(&name);
  }else{
    for(i=0; i<argc; i++){
      file_canonical_name(argv[i], &name, 0);
      zName = blob_str(&name);
      isDir = file_wd_isdir(zName);
      if( isDir==1 ){
        vfile_scan(&name, nRoot-1, scanFlags, pIgnore, 0);
      }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{
................................................................................
    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);
    char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname);
    int isMissing = !file_wd_isfile_or_link(zFullName);

    /* Determine the file change classification, if any. */
    if( isDeleted ){
      if( flags & C_DELETED ){
        zClass = "DELETED";
      }
    }else if( isMissing ){
................................................................................
      zClass = "EXECUTABLE";
    }else if( (flags & C_META) && isChnged==7 ){
      zClass = "SYMLINK";
    }else if( (flags & C_META) && isChnged==8 ){
      zClass = "UNEXEC";
    }else if( (flags & C_META) && isChnged==9 ){
      zClass = "UNLINK";
    }else if( (flags & C_CONFLICT) && isChnged && !file_wd_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";
................................................................................
  int useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0;
  int showHdr = command==CHANGES && find_option("header", 0, 0);
  int verboseFlag = command==CHANGES && find_option("verbose", "v", 0);
  const char *zIgnoreFlag = find_option("ignore", 0, 1);
  unsigned scanFlags = 0;
  unsigned flags = 0;
  int vid, i;



  /* Load affirmative flag options. */
  for( i=0; i<count(flagDefs); ++i ){
    if( (command==CHANGES || !(flagDefs[i].mask & C_CLASSIFY))
     && find_option(flagDefs[i].option, 0, 0) ){
      flags |= flagDefs[i].mask;
    }
................................................................................
    char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname);
    const char *type = "";
    if( verboseFlag ){
      if( isNew ){
        type = "ADDED      ";
      }else if( isDeleted ){
        type = "DELETED    ";
      }else if( !file_wd_isfile_or_link(zFullName) ){
        if( file_access(zFullName, F_OK)==0 ){
          type = "NOT_A_FILE ";
        }else{
          type = "MISSING    ";
        }
      }else if( chnged ){
        if( chnged==2 ){
................................................................................
  if( zEditor ){
    zCmd = mprintf("%s \"%s\"", zEditor, zFile);
    fossil_print("%s\n", zCmd);
    if( fossil_system(zCmd) ){
      fossil_fatal("editor aborted: \"%s\"", zCmd);
    }

    blob_read_from_file(&reply, zFile);
  }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;
      }
................................................................................
    ** directly from the filesystem.  On windows, permissions are
    ** unchanged from the original.  However, only do this if the file
    ** itself is actually selected to be part of this check-in.
    */
    if( isSelected ){
      int mPerm;

      mPerm = file_wd_perm(blob_str(&filename));
      isExe = ( mPerm==PERM_EXE );
      isLink = ( mPerm==PERM_LNK );
    }
#endif
    if( isExe ){
      zPerm = " x";
    }else if( isLink ){
................................................................................

    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);
    blob_zero(&content);
    if( file_wd_islink(zFullname) ){
      blob_read_link(&content, zFullname);
    }else{
      blob_read_from_file(&content, zFullname);
    }
    blob_zero(&reason);
    fileRc = commit_warning(&content, crlfOk, binOk, encodingOk, 2,
                            zFullname, &reason);
    if( fileRc || verboseFlag ){
      fossil_print("%d\t%s\t%s\n", fileRc, zName, blob_str(&reason));
    }
    blob_reset(&reason);
................................................................................
  }

  if( zComment ){
    blob_zero(&comment);
    blob_append(&comment, zComment, -1);
  }else if( zComFile ){
    blob_zero(&comment);
    blob_read_from_file(&comment, zComFile);
    blob_to_utf8_no_bom(&comment, 1);
  }else if( dryRunFlag ){
    blob_zero(&comment);
  }else 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 ){
................................................................................
    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);

    blob_zero(&content);
    if( file_wd_islink(zFullname) ){
      /* Instead of file content, put link destination path */
      blob_read_link(&content, zFullname);
    }else{
      blob_read_from_file(&content, zFullname);
    }
    /* Do not emit any warnings when they are disabled. */
    if( !noWarningFlag ){
      abortCommit |= commit_warning(&content, crlfOk, binOk,
                                    encodingOk, noPrompt,
                                    zFullname, 0);
    }
    if( contains_merge_marker(&content) ){
................................................................................
  if( dryRunFlag ){
    blob_write_to_file(&manifest, "");
  }
  if( outputManifest & MFESTFLG_RAW ){
    zManifestFile = mprintf("%smanifest", g.zLocalRoot);
    blob_write_to_file(&manifest, zManifestFile);
    blob_reset(&manifest);
    blob_read_from_file(&manifest, zManifestFile);
    free(zManifestFile);
  }

  nvid = content_put(&manifest);
  if( nvid==0 ){
    fossil_fatal("trouble committing manifest: %s", g.zErrMsg);
  }







|







 







|







 







|







 







>
>







 







|







 







|







 







|







 







<
<
<
|
<







 







|







 







<
<
<
<
|
<







 







|







84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
...
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
...
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
...
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
...
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
....
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
....
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
....
1908
1909
1910
1911
1912
1913
1914



1915

1916
1917
1918
1919
1920
1921
1922
....
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
....
2370
2371
2372
2373
2374
2375
2376




2377

2378
2379
2380
2381
2382
2383
2384
....
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
    blob_init(&name, g.zLocalRoot, nRoot - 1);
    vfile_scan(&name, blob_size(&name), scanFlags, pIgnore, 0);
    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);
      if( isDir==1 ){
        vfile_scan(&name, nRoot-1, scanFlags, pIgnore, 0);
      }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{
................................................................................
    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);
    char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname);
    int isMissing = !file_isfile_or_link(zFullName);

    /* Determine the file change classification, if any. */
    if( isDeleted ){
      if( flags & C_DELETED ){
        zClass = "DELETED";
      }
    }else if( isMissing ){
................................................................................
      zClass = "EXECUTABLE";
    }else if( (flags & C_META) && isChnged==7 ){
      zClass = "SYMLINK";
    }else if( (flags & C_META) && isChnged==8 ){
      zClass = "UNEXEC";
    }else if( (flags & C_META) && isChnged==9 ){
      zClass = "UNLINK";
    }else if( (flags & C_CONFLICT) && isChnged && !file_islink(zFullName)
           && file_contains_merge_marker(zFullName) ){
      zClass = "CONFLICT";
    }else if( (flags & (C_EDITED | C_CHANGED)) && isChnged
           && (isChnged<2 || isChnged>9) ){
      zClass = "EDITED";
    }else if( (flags & C_RENAMED) && isRenamed ){
      zClass = "RENAMED";
................................................................................
  int useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0;
  int showHdr = command==CHANGES && find_option("header", 0, 0);
  int verboseFlag = command==CHANGES && find_option("verbose", "v", 0);
  const char *zIgnoreFlag = find_option("ignore", 0, 1);
  unsigned scanFlags = 0;
  unsigned flags = 0;
  int vid, i;

  fossil_pledge("stdio rpath wpath cpath id flock tty chown");

  /* Load affirmative flag options. */
  for( i=0; i<count(flagDefs); ++i ){
    if( (command==CHANGES || !(flagDefs[i].mask & C_CLASSIFY))
     && find_option(flagDefs[i].option, 0, 0) ){
      flags |= flagDefs[i].mask;
    }
................................................................................
    char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname);
    const char *type = "";
    if( verboseFlag ){
      if( isNew ){
        type = "ADDED      ";
      }else if( isDeleted ){
        type = "DELETED    ";
      }else if( !file_isfile_or_link(zFullName) ){
        if( file_access(zFullName, F_OK)==0 ){
          type = "NOT_A_FILE ";
        }else{
          type = "MISSING    ";
        }
      }else if( chnged ){
        if( chnged==2 ){
................................................................................
  if( zEditor ){
    zCmd = mprintf("%s \"%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);
  }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;
      }
................................................................................
    ** directly from the filesystem.  On windows, permissions are
    ** unchanged from the original.  However, only do this if the file
    ** itself is actually selected to be part of this check-in.
    */
    if( isSelected ){
      int mPerm;

      mPerm = file_perm(blob_str(&filename), RepoFILE);
      isExe = ( mPerm==PERM_EXE );
      isLink = ( mPerm==PERM_LNK );
    }
#endif
    if( isExe ){
      zPerm = " x";
    }else if( isLink ){
................................................................................

    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);
    blob_zero(&content);



    blob_read_from_file(&content, zFullname, RepoFILE);

    blob_zero(&reason);
    fileRc = commit_warning(&content, crlfOk, binOk, encodingOk, 2,
                            zFullname, &reason);
    if( fileRc || verboseFlag ){
      fossil_print("%d\t%s\t%s\n", fileRc, zName, blob_str(&reason));
    }
    blob_reset(&reason);
................................................................................
  }

  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 ){
................................................................................
    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);

    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,
                                    zFullname, 0);
    }
    if( contains_merge_marker(&content) ){
................................................................................
  if( dryRunFlag ){
    blob_write_to_file(&manifest, "");
  }
  if( outputManifest & MFESTFLG_RAW ){
    zManifestFile = mprintf("%smanifest", g.zLocalRoot);
    blob_write_to_file(&manifest, zManifestFile);
    blob_reset(&manifest);
    blob_read_from_file(&manifest, zManifestFile, ExtFILE);
    free(zManifestFile);
  }

  nvid = content_put(&manifest);
  if( nvid==0 ){
    fossil_fatal("trouble committing manifest: %s", g.zErrMsg);
  }

Changes to src/checkout.c.

112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
  blob_appendf(&filename, "%s", g.zLocalRoot);
  baseLen = blob_size(&filename);
  manifest_file_rewind(pManifest);
  while( (pFile = manifest_file_next(pManifest, 0))!=0 ){
    int isExe;
    blob_append(&filename, pFile->zName, -1);
    isExe = pFile->zPerm && strstr(pFile->zPerm, "x");
    file_wd_setexe(blob_str(&filename), isExe);
    set_or_clear_isexe(pFile->zName, vid, isExe);
    blob_resize(&filename, baseLen);
  }
  blob_reset(&filename);
  manifest_destroy(pManifest);
}








|







112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
  blob_appendf(&filename, "%s", g.zLocalRoot);
  baseLen = blob_size(&filename);
  manifest_file_rewind(pManifest);
  while( (pFile = manifest_file_next(pManifest, 0))!=0 ){
    int isExe;
    blob_append(&filename, pFile->zName, -1);
    isExe = pFile->zPerm && strstr(pFile->zPerm, "x");
    file_setexe(blob_str(&filename), isExe);
    set_or_clear_isexe(pFile->zName, vid, isExe);
    blob_resize(&filename, baseLen);
  }
  blob_reset(&filename);
  manifest_destroy(pManifest);
}

Added src/ci_edit.js.





































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/* Javascript used to make the check-in edit screen more interactive.
*/
function chgcbn(){
  var newbr = document.getElementById('newbr');
  var brname = document.getElementById('brname');
  var checked = newbr.checked;
  var x = brname.value.trim();
  if( !x || !newbr.checked ) x = newbr.getAttribute('data-branch');
  if( newbr.checked ) brname.select();
  document.getElementById('hbranch').textContent = x;
  cidbrid = document.getElementById('cbranch');
  if( cidbrid ) cidbrid.textContent = x;
}
function chgbn(){
  var newbr = document.getElementById('newbr');
  var brname = document.getElementById('brname');
  var x = brname.value.trim();
  var br = newbr.getAttribute('data-branch');
  if( !x ) x = br;
  newbr.checked = (x!=br);
  document.getElementById('hbranch').textContent = x;
  cidbrid = document.getElementById('cbranch');
  if( cidbrid ) cidbrid.textContent = x;
}
function chgtn(){
  var newtag = document.getElementById('newtag');
  var tagname = document.getElementById('tagname');
  newtag.checked=!!tagname.value;
}
(function(){
  document.getElementById('newbr').onchange = chgcbn;
  document.getElementById('brname').onkeyup = chgbn;
  document.getElementById('tagname').onkeyup = chgtn;
}());

Changes to src/clearsign.c.

44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
  rc = fossil_system(zCmd);
  free(zCmd);
  if( rc==0 ){
    if( pOut==pIn ){
      blob_reset(pIn);
    }
    blob_zero(pOut);
    blob_read_from_file(pOut, zIn);
  }else{
    if( pOut!=pIn ){
      blob_copy(pOut, pIn);
    }
  }
  file_delete(zOut);
  file_delete(zIn);
  free(zOut);
  free(zIn);
  return rc;
}







|











44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
  rc = fossil_system(zCmd);
  free(zCmd);
  if( rc==0 ){
    if( pOut==pIn ){
      blob_reset(pIn);
    }
    blob_zero(pOut);
    blob_read_from_file(pOut, zIn, ExtFILE);
  }else{
    if( pOut!=pIn ){
      blob_copy(pOut, pIn);
    }
  }
  file_delete(zOut);
  file_delete(zIn);
  free(zOut);
  free(zIn);
  return rc;
}

Changes to src/clone.c.

142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
...
164
165
166
167
168
169
170

171
172

173
174
175
176
177
178
179
...
291
292
293
294
295
296
297













































  /* We should be done with options.. */
  verify_all_options();

  if( g.argc < 4 ){
    usage("?OPTIONS? FILE-OR-URL NEW-REPOSITORY");
  }
  db_open_config(0, 0);
  if( -1 != file_size(g.argv[3]) ){
    fossil_fatal("file already exists: %s", g.argv[3]);
  }

  url_parse(g.argv[2], urlFlags);
  if( zDefaultUser==0 && g.url.user!=0 ) zDefaultUser = g.url.user;
  if( g.url.isFile ){
    file_copy(g.url.name, g.argv[3]);
................................................................................
    if( zDefaultUser ){
      g.zLogin = zDefaultUser;
    }else{
      g.zLogin = db_text(0, "SELECT login FROM user WHERE cap LIKE '%%s%%'");
    }
    fossil_print("Repository cloned into %s\n", g.argv[3]);
  }else{

    db_create_repository(g.argv[3]);
    db_open_repository(g.argv[3]);

    db_begin_transaction();
    db_record_repository_filename(g.argv[3]);
    db_initial_setup(0, 0, zDefaultUser);
    user_select();
    db_set("content-schema", CONTENT_SCHEMA, 0);
    db_set("aux-schema", AUX_SCHEMA_MAX, 0);
    db_set("rebuilt", get_version(), 0);
................................................................................
** options).
*/
void clone_ssh_db_set_options(void){
  if( g.zSshCmd && g.zSshCmd[0] ){
    db_set("ssh-command", g.zSshCmd, 0);
  }
}




















































|







 







>


>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
...
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
...
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
  /* We should be done with options.. */
  verify_all_options();

  if( g.argc < 4 ){
    usage("?OPTIONS? FILE-OR-URL NEW-REPOSITORY");
  }
  db_open_config(0, 0);
  if( -1 != file_size(g.argv[3], ExtFILE) ){
    fossil_fatal("file already exists: %s", g.argv[3]);
  }

  url_parse(g.argv[2], urlFlags);
  if( zDefaultUser==0 && g.url.user!=0 ) zDefaultUser = g.url.user;
  if( g.url.isFile ){
    file_copy(g.url.name, g.argv[3]);
................................................................................
    if( zDefaultUser ){
      g.zLogin = zDefaultUser;
    }else{
      g.zLogin = db_text(0, "SELECT login FROM user WHERE cap LIKE '%%s%%'");
    }
    fossil_print("Repository cloned into %s\n", g.argv[3]);
  }else{
    db_close_config();
    db_create_repository(g.argv[3]);
    db_open_repository(g.argv[3]);
    db_open_config(0,0);
    db_begin_transaction();
    db_record_repository_filename(g.argv[3]);
    db_initial_setup(0, 0, zDefaultUser);
    user_select();
    db_set("content-schema", CONTENT_SCHEMA, 0);
    db_set("aux-schema", AUX_SCHEMA_MAX, 0);
    db_set("rebuilt", get_version(), 0);
................................................................................
** options).
*/
void clone_ssh_db_set_options(void){
  if( g.zSshCmd && g.zSshCmd[0] ){
    db_set("ssh-command", g.zSshCmd, 0);
  }
}

/*
** 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();
  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>.
    }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.zip?uuid=%t", zNm, zDLTag);
    @ <p>ZIP Archive: %z(zUrl)%h(zNm).zip</a>
    zUrl = href("%R/tarball/%t.tar.gz?uuid=%t", zNm, zDLTag);
    @ <p>Tarball: %z(zUrl)%h(zNm).tar.gz</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>.
    }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();
}

Changes to src/codecheck1.c.

139
140
141
142
143
144
145




146
147
148
149
150
151
152
...
199
200
201
202
203
204
205

206
207
208
209
210
211
212
...
308
309
310
311
312
313
314

315

316

317

318
319
320

321
322
323
324
325
326

327
328

329
330
331
332
333
334
335
336
337
338
339


340
341
342
343
344


345
346
347
348
349
350
351
...
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
    for(i=2; z[i] && z[i]!='\n'; i++){}
    if( z[i] ){
      (*pLN)++;
      i++;
    }
    *pType = TK_SPACE;
    return i;




  }
  *pType = TK_OTHER;
  return 1;
}

/*
** Return the next non-whitespace token
................................................................................

/*
** Return true if the input is a string literal.
*/
static int is_string_lit(const char *z){
  int nu1, nu2;
  z = next_non_whitespace(z, &nu1, &nu2);

  return z[0]=='"';
}

/*
** Return true if the input is an expression of string literals:
**
**      EXPR ? "..." : "..."
................................................................................
  const char *zFName;    /* Name of the function */
  int iFmtArg;           /* Index of format argument.  Leftmost is 1. */
  unsigned fmtFlags;     /* Processing flags */
} aFmtFunc[] = {
  { "admin_log",               1, 0 },
  { "blob_append_sql",         2, FMT_NO_S },
  { "blob_appendf",            2, 0 },

  { "cgi_panic",               1, 0 },

  { "cgi_redirectf",           1, 0 },

  { "db_blob",                 2, FMT_NO_S },

  { "db_double",               2, FMT_NO_S },
  { "db_err",                  1, 0 },
  { "db_exists",               1, FMT_NO_S },

  { "db_int",                  2, FMT_NO_S },
  { "db_int64",                2, FMT_NO_S },
  { "db_multi_exec",           1, FMT_NO_S },
  { "db_optional_sql",         2, FMT_NO_S },
  { "db_prepare",              2, FMT_NO_S },
  { "db_prepare_ignore_error", 2, FMT_NO_S },

  { "db_static_prepare",       2, FMT_NO_S },
  { "db_text",                 2, FMT_NO_S },

  { "form_begin",              2, 0 },
  { "fossil_error",            2, 0 },
  { "fossil_errorlog",         1, 0 },
  { "fossil_fatal",            1, 0 },
  { "fossil_fatal_recursive",  1, 0 },
  { "fossil_panic",            1, 0 },
  { "fossil_print",            1, 0 },
  { "fossil_trace",            1, 0 },
  { "fossil_warning",          1, 0 },
  { "href",                    1, 0 },
  { "json_new_string_f",       1, 0 },


  { "mprintf",                 1, 0 },
  { "socket_set_errmsg",       1, 0 },
  { "ssl_set_errmsg",          1, 0 },
  { "style_header",            1, 0 },
  { "style_set_current_page",  1, 0 },


  { "webpage_error",           1, 0 },
  { "xhref",                   2, 0 },
};

/*
** Determine if the indentifier zIdent of length nIndent is a Fossil
** internal interface that uses a printf-style argument.  Return zero if not.
................................................................................
           zFilename, lnFCall, szFName, zFCall);
    nErr++;
  }else{
    const char *zFmt = azArg[fmtArg-1];
    const char *zOverride = strstr(zFmt, "/*works-like:");
    if( zOverride ) zFmt = zOverride + sizeof("/*works-like:")-1;
    if( !is_string_lit(zFmt) ){
      printf("%s:%d: %.*s() has non-constant format string\n",
             zFilename, lnFCall, szFName, zFCall);
      nErr++;
    }else if( (k = formatArgCount(zFmt, nArg, acType))>=0
             && nArg!=fmtArg+k ){
      printf("%s:%d: too %s arguments to %.*s() "
             "- got %d and expected %d\n",
             zFilename, lnFCall, (nArg<fmtArg+k ? "few" : "many"),
             szFName, zFCall, nArg, fmtArg+k);







>
>
>
>







 







>







 







>

>

>

>



>






>


>











>
>





>
>







 







|
|







139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
...
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
...
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
...
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
    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
................................................................................

/*
** 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 ? "..." : "..."
................................................................................
  const char *zFName;    /* Name of the function */
  int iFmtArg;           /* Index of format argument.  Leftmost is 1. */
  unsigned fmtFlags;     /* Processing flags */
} aFmtFunc[] = {
  { "admin_log",               1, 0 },
  { "blob_append_sql",         2, FMT_NO_S },
  { "blob_appendf",            2, 0 },
  { "cgi_debug",               1, 0 },
  { "cgi_panic",               1, 0 },
  { "cgi_printf",              1, 0 },
  { "cgi_redirectf",           1, 0 },
  { "chref",                   2, 0 },
  { "db_blob",                 2, FMT_NO_S },
  { "db_debug",                1, FMT_NO_S },
  { "db_double",               2, FMT_NO_S },
  { "db_err",                  1, 0 },
  { "db_exists",               1, FMT_NO_S },
  { "db_get_mprintf",          2, 0 },
  { "db_int",                  2, FMT_NO_S },
  { "db_int64",                2, FMT_NO_S },
  { "db_multi_exec",           1, FMT_NO_S },
  { "db_optional_sql",         2, FMT_NO_S },
  { "db_prepare",              2, FMT_NO_S },
  { "db_prepare_ignore_error", 2, FMT_NO_S },
  { "db_set_mprintf",          3, 0 },
  { "db_static_prepare",       2, FMT_NO_S },
  { "db_text",                 2, FMT_NO_S },
  { "db_unset_mprintf",        2, 0 },
  { "form_begin",              2, 0 },
  { "fossil_error",            2, 0 },
  { "fossil_errorlog",         1, 0 },
  { "fossil_fatal",            1, 0 },
  { "fossil_fatal_recursive",  1, 0 },
  { "fossil_panic",            1, 0 },
  { "fossil_print",            1, 0 },
  { "fossil_trace",            1, 0 },
  { "fossil_warning",          1, 0 },
  { "href",                    1, 0 },
  { "json_new_string_f",       1, 0 },
  { "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, 0 },
  { "style_set_current_page",  1, 0 },
  { "style_submenu_element",   2, 0 },
  { "style_submenu_sql",       3, 0 },
  { "webpage_error",           1, 0 },
  { "xhref",                   2, 0 },
};

/*
** Determine if the indentifier zIdent of length nIndent is a Fossil
** internal interface that uses a printf-style argument.  Return zero if not.
................................................................................
           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);

Changes to src/comformat.c.

482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
  if( g.argc==5 ){
    zOrigText = g.argv[4];
  }else{
    zOrigText = 0;
  }
  if( fromFile ){
    Blob fileData;
    blob_read_from_file(&fileData, zText);
    zText = mprintf("%s", blob_str(&fileData));
    blob_reset(&fileData);
    if( zOrigText ){
      blob_read_from_file(&fileData, zOrigText);
      zOrigText = mprintf("%s", blob_str(&fileData));
      blob_reset(&fileData);
    }
  }
  if( decode ){
    zText = mprintf(fromFile?"%z":"%s" /*works-like:"%s"*/, zText);
    defossilize(zText);







|



|







482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
  if( g.argc==5 ){
    zOrigText = g.argv[4];
  }else{
    zOrigText = 0;
  }
  if( fromFile ){
    Blob fileData;
    blob_read_from_file(&fileData, zText, ExtFILE);
    zText = mprintf("%s", blob_str(&fileData));
    blob_reset(&fileData);
    if( zOrigText ){
      blob_read_from_file(&fileData, zOrigText, ExtFILE);
      zOrigText = mprintf("%s", blob_str(&fileData));
      blob_reset(&fileData);
    }
  }
  if( decode ){
    zText = mprintf(fromFile?"%z":"%s" /*works-like:"%s"*/, zText);
    defossilize(zText);

Changes to src/config.h.

251
252
253
254
255
256
257
258










259
# define NORETURN
#endif

/*
** Number of elements in an array
*/
#define count(X) (sizeof(X)/sizeof(X[0]))











#endif /* _RC_COMPILE_ */








>
>
>
>
>
>
>
>
>
>

251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
# define NORETURN
#endif

/*
** Number of elements in an array
*/
#define count(X) (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.

336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
...
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
...
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
**    /concealed  $MTIME $HASH content $VALUE
**
** OLD FORMAT:
**
** The old format is retained for backwards compatibility, but is deprecated.
** The cutover from old format to new was on 2011-04-25.  After sufficient
** time has passed, support for the old format will be removed.
** Update: Support for the old format was remoed on 2017-09-20.
**
** zName is either the NAME of an element of the CONFIG table, or else
** one of the special names "@shun", "@reportfmt", "@user", or "@concealed".
** If zName is a CONFIG table name, then CONTENT replaces (overwrites) the
** element in the CONFIG table.  For one of the @-labels, CONTENT is raw
** SQL that is evaluated.  Note that the raw SQL in CONTENT might not
** insert directly into the target table but might instead use a proxy
................................................................................
    export_config(mask, g.argv[3], iStart, g.argv[4]);
  }else
  if( strncmp(zMethod, "import", n)==0
       || strncmp(zMethod, "merge", n)==0 ){
    Blob in;
    int groupMask;
    if( g.argc!=4 ) usage(mprintf("%s FILENAME",zMethod));
    blob_read_from_file(&in, g.argv[3]);
    db_begin_transaction();
    if( zMethod[0]=='i' ){
      groupMask = CONFIGSET_ALL | CONFIGSET_OVERWRITE;
    }else{
      groupMask = CONFIGSET_ALL;
    }
    configure_receive_all(&in, groupMask);
................................................................................
  verify_all_options();
  if( g.argc<3 || (zFile==0 && zBlob==0 && g.argc<4) ){
    usage("VAR ?VALUE? ?--file FILE?");
  }
  zVar = g.argv[2];
  if( zFile ){
    if( zBlob ) fossil_fatal("cannot do both --file or --blob");
    blob_read_from_file(&x, zFile);
  }else if( zBlob ){
    blob_read_from_file(&x, zBlob);
  }else{
    blob_init(&x,g.argv[3],-1);
  }
  db_prepare(&ins,
     "REPLACE INTO config(name,value,mtime)"
     "VALUES(%Q,:val,now())", zVar);
  if( zBlob ){







|







 







|







 







|

|







336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
...
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
...
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
**    /concealed  $MTIME $HASH content $VALUE
**
** OLD FORMAT:
**
** The old format is retained for backwards compatibility, but is deprecated.
** The cutover from old format to new was on 2011-04-25.  After sufficient
** time has passed, support for the old format will be removed.
** Update: Support for the old format was removed on 2017-09-20.
**
** zName is either the NAME of an element of the CONFIG table, or else
** one of the special names "@shun", "@reportfmt", "@user", or "@concealed".
** If zName is a CONFIG table name, then CONTENT replaces (overwrites) the
** element in the CONFIG table.  For one of the @-labels, CONTENT is raw
** SQL that is evaluated.  Note that the raw SQL in CONTENT might not
** insert directly into the target table but might instead use a proxy
................................................................................
    export_config(mask, g.argv[3], iStart, g.argv[4]);
  }else
  if( strncmp(zMethod, "import", n)==0
       || strncmp(zMethod, "merge", n)==0 ){
    Blob in;
    int groupMask;
    if( g.argc!=4 ) usage(mprintf("%s FILENAME",zMethod));
    blob_read_from_file(&in, g.argv[3], ExtFILE);
    db_begin_transaction();
    if( zMethod[0]=='i' ){
      groupMask = CONFIGSET_ALL | CONFIGSET_OVERWRITE;
    }else{
      groupMask = CONFIGSET_ALL;
    }
    configure_receive_all(&in, groupMask);
................................................................................
  verify_all_options();
  if( g.argc<3 || (zFile==0 && zBlob==0 && g.argc<4) ){
    usage("VAR ?VALUE? ?--file FILE?");
  }
  zVar = g.argv[2];
  if( zFile ){
    if( zBlob ) fossil_fatal("cannot do both --file or --blob");
    blob_read_from_file(&x, zFile, ExtFILE);
  }else if( zBlob ){
    blob_read_from_file(&x, zBlob, ExtFILE);
  }else{
    blob_init(&x,g.argv[3],-1);
  }
  db_prepare(&ins,
     "REPLACE INTO config(name,value,mtime)"
     "VALUES(%Q,:val,now())", zVar);
  if( zBlob ){

Changes to src/content.c.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
...
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
/*
** Copyright (c) 2006 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the Simplified BSD License (also
** known as the "2-Clause License" or "FreeBSD License".)

** This program is distributed in the hope that it will be useful,
** but without any warranty; without even the implied warranty of
** merchantability or fitness for a particular purpose.
**
** Author contact information:
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
................................................................................
*/
void test_content_put_cmd(void){
  int rid;
  Blob content;
  if( g.argc!=3 ) usage("FILENAME");
  db_must_be_within_tree();
  user_select();
  blob_read_from_file(&content, g.argv[2]);
  rid = content_put(&content);
  fossil_print("inserted as record %d\n", rid);
}

/*
** Make sure the content at rid is the original content and is not a
** delta.






|







 







|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
...
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
/*
** Copyright (c) 2006 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the Simplified BSD License (also
** known as the "2-Clause License" or "FreeBSD License".)
**
** This program is distributed in the hope that it will be useful,
** but without any warranty; without even the implied warranty of
** merchantability or fitness for a particular purpose.
**
** Author contact information:
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
................................................................................
*/
void test_content_put_cmd(void){
  int rid;
  Blob content;
  if( g.argc!=3 ) usage("FILENAME");
  db_must_be_within_tree();
  user_select();
  blob_read_from_file(&content, g.argv[2], ExtFILE);
  rid = content_put(&content);
  fossil_print("inserted as record %d\n", rid);
}

/*
** Make sure the content at rid is the original content and is not a
** delta.

Added src/cookies.c.









































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
/*
** Copyright (c) 2017 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the Simplified BSD License (also
** known as the "2-Clause License" or "FreeBSD License".)
**
** This program is distributed in the hope that it will be useful,
** but without any warranty; without even the implied warranty of
** merchantability or fitness for a particular purpose.
**
** Author contact information:
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*******************************************************************************
**
** This file contains code used to manage a cookie that stores user-specific
** display preferences for the web interface.
**
** cookie_parse(void);
**
**    Read and parse the display preferences cookie.
**
** cookie_read_parameter(zQP, zPName);
**
**    If query parameter zQP does not exist but zPName does exist in
**    the parsed cookie, then initialize zQP to hold the same value
**    as the zPName element in the parsed cookie.
**
** cookie_write_parameter(zQP, zPName, zDefault);
**
**    If query parameter zQP exists and if it has a different value from
**    the zPName parameter in the parsed cookie, then replace the value of
**    zPName with the value of zQP.  If zQP exists but zPName does not
**    exist, then zPName is created.  If zQP does not exist or if it has
**    the same value as zPName, then this routine is a no-op.
**
** cookie_link_parameter(zQP, zPName, zDefault);
**
**    This does both cookie_read_parameter() and cookie_write_parameter()
**    all at once.
**
** cookie_render();
**
**    If any prior calls to cookie_write_parameter() have changed the
**    value of the user preferences cookie, this routine will cause the
**    new cookie value to be included in the HTTP header for the current
**    web page.  This routine is a destructor for this module and should
**    be called once.
**
** char *cookie_value(zPName, zDefault);
**
**    Look up the value of a cookie parameter zPName.  Return zDefault if
**    there is no display preferences cookie or if zPName does not exist.
*/
#include "cookies.h"
#include <assert.h>
#include <string.h>

#if INTERFACE
/* the standard name of the display settings cookie for fossil */
# define DISPLAY_SETTINGS_COOKIE    "fossil_display_settings"
#endif


/*
** State information private to this module
*/
#define COOKIE_NPARAM  10
static struct {
  char *zCookieValue;         /* Value of the user preferences cookie */
  int bChanged;               /* True if any value has changed */
  int bIsInit;                /* True after initialization */
  int nParam;                 /* Number of parameters in the cookie */
  struct {
    const char *zPName;         /* Name of a parameter */
    char *zPValue;              /* Value of that parameter */
  } aParam[COOKIE_NPARAM];
} cookies;

/* Initialize this module by parsing the content of the cookie named
** by DISPLAY_SETTINGS_COOKIE
*/
void cookie_parse(void){
  char *z;
  if( cookies.bIsInit ) return;
  z = (char*)P(DISPLAY_SETTINGS_COOKIE);
  if( z==0 ) z = "";
  cookies.zCookieValue = z = mprintf("%s", z);
  cookies.bIsInit = 1;
  while( cookies.nParam<COOKIE_NPARAM ){
    while( fossil_isspace(z[0]) ) z++;
    if( z[0]==0 ) break;
    cookies.aParam[cookies.nParam].zPName = z;
    while( *z && *z!='=' && *z!=',' ){ z++; }
    if( *z=='=' ){
      *z = 0;
      z++;
      cookies.aParam[cookies.nParam].zPValue = z;
      while( *z && *z!=',' ){ z++; }
      if( *z ){
        *z = 0;
        z++;
      }
      dehttpize(cookies.aParam[cookies.nParam].zPValue);
    }else{
      if( *z ){ *z++ = 0; }
      cookies.aParam[cookies.nParam].zPValue = "";
    }
    cookies.nParam++;
  }
}

#define COOKIE_READ  1
#define COOKIE_WRITE 2
static void cookie_readwrite(
  const char *zQP,        /* Name of the query parameter */
  const char *zPName,     /* Name of the cooking setting */
  const char *zDflt,      /* Default value for the query parameter */
  int flags               /* READ or WRITE or both */
){
  const char *zQVal = P(zQP);
  int i;
  cookie_parse();
  for(i=0; i<cookies.nParam && strcmp(zPName,cookies.aParam[i].zPName); i++){}
  if( zQVal==0 && (flags & COOKIE_READ)!=0 && i<cookies.nParam ){
    cgi_set_parameter_nocopy(zQP, cookies.aParam[i].zPValue, 1);
    return;
  }
  if( zQVal==0 ) zQVal = zDflt;
  if( (flags & COOKIE_WRITE)!=0
   && i<COOKIE_NPARAM
   && (i==cookies.nParam || strcmp(zQVal, cookies.aParam[i].zPValue))
  ){
    if( i==cookies.nParam ){
      cookies.aParam[i].zPName = zPName;
      cookies.nParam++;
    }
    cookies.aParam[i].zPValue = (char*)zQVal;
    cookies.bChanged = 1;
  }
}

/* If query parameter zQP is missing, initialize it using the zPName
** value from the user preferences cookie
*/
void cookie_read_parameter(const char *zQP, const char *zPName){
  cookie_readwrite(zQP, zPName, 0, COOKIE_READ);
}

/* Update the zPName value of the user preference cookie to match
** the value of query parameter zQP.
*/
void cookie_write_parameter(
  const char *zQP,
  const char *zPName,
  const char *zDflt
){
  cookie_readwrite(zQP, zPName, zDflt, COOKIE_WRITE);
}

/* Use the zPName user preference value as a default for zQP and record
** any changes to the zQP value back into the cookie.
*/
void cookie_link_parameter(
  const char *zQP,       /* The query parameter */
  const char *zPName,    /* The name of the cookie value */
  const char *zDflt      /* Default value for the parameter */
){
  cookie_readwrite(zQP, zPName, zDflt, COOKIE_READ|COOKIE_WRITE);
}

/* Update the user preferences cookie, if necessary, and shut down this
** module
*/
void cookie_render(void){
  if( cookies.bChanged ){
    Blob new;
    int i;
    blob_init(&new, 0, 0);
    for(i=0;i<cookies.nParam;i++){
      if( i>0 ) blob_append(&new, ",", 1);
      blob_appendf(&new, "%s=%T",
          cookies.aParam[i].zPName, cookies.aParam[i].zPValue);
    }
    cgi_set_cookie(DISPLAY_SETTINGS_COOKIE, blob_str(&new), 0, 31536000);
  }
  cookies.bIsInit = 0;
}

/* Return the value of a preference cookie.
*/
const char *cookie_value(const char *zPName, const char *zDefault){
  int i;
  assert( zPName!=0 );
  cookie_parse();
  for(i=0; i<cookies.nParam && strcmp(zPName,cookies.aParam[i].zPName); i++){}
  return i<cookies.nParam ? cookies.aParam[i].zPValue : zDefault;
}

/*
** WEBPAGE:  cookies
**
** Show the current display settings contained in the
** "fossil_display_settings" cookie.
*/
void cookie_page(void){
  int i;
  if( PB("clear") ){
    cgi_set_cookie(DISPLAY_SETTINGS_COOKIE, "", 0, 1);
    cgi_replace_parameter(DISPLAY_SETTINGS_COOKIE, "");
  }
  cookie_parse();
  style_header("User Preference Cookie Values");
  if( cookies.nParam ){
    style_submenu_element("Clear", "%R/cookies?clear");
  }
  @ <p>The following are user preference settings held in the
  @ "fossil_display_settings" cookie.
  @ <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();
}

Changes to src/cson_amalgamation.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
..
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
...
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
...
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
...
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
....
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
....
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
....
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
....
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
....
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
....
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
....
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
....
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
....
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
....
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
....
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
....
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
....
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
....
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
....
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
....
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
....
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
....
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
....
4638
4639
4640
4641
4642
4643
4644
4645
4646
4647
4648
4649
4650
4651
4652
....
4678
4679
4680
4681
4682
4683
4684
4685
4686
4687
4688
4689
4690
4691
4692
....
4832
4833
4834
4835
4836
4837
4838
4839
4840
4841
4842
4843
4844
4845
4846
....
4885
4886
4887
4888
4889
4890
4891
4892
4893
4894
4895
4896
4897
4898
4899
....
5349
5350
5351
5352
5353
5354
5355
5356
5357
5358
5359
5360
5361
5362
5363
....
5489
5490
5491
5492
5493
5494
5495
5496
5497
5498
5499
5500
5501
5502
5503
....
5634
5635
5636
5637
5638
5639
5640
5641
5642
5643
5644
5645
5646
5647
5648
#   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_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;
................................................................................
    - 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);
................................................................................
    \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 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)

................................................................................
#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.
*/
................................................................................
   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 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
................................................................................
#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,
................................................................................
};

/**
  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[] = {
................................................................................
};


/**
    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;
................................................................................
                                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
................................................................................
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();
}
................................................................................
    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;
................................................................................
    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;
................................................................................
}

#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
................................................................................
    {
        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 )
{
................................................................................
{
    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( 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;
................................................................................

   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 );
................................................................................
              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;
................................................................................
   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;
................................................................................
    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;
................................................................................
    {
        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 = 13 };
        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;
................................................................................
#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;
................................................................................
                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;
................................................................................
              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) );
................................................................................
              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) );
................................................................................
    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 );
................................................................................
    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; }
................................................................................
    {
        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;







|


|











|








|

|







 







|







 







|

|









|


|






|












|







 







|





|







 







|




|







 







|
|







 







|







 







|







 







|












|





|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
..
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
..
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
...
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
...
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
...
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
....
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
....
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
....
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
....
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
....
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
....
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
....
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
....
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
....
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
....
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
....
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
....
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
....
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
....
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
....
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
....
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
....
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
....
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
....
4638
4639
4640
4641
4642
4643
4644
4645
4646
4647
4648
4649
4650
4651
4652
....
4678
4679
4680
4681
4682
4683
4684
4685
4686
4687
4688
4689
4690
4691
4692
....
4832
4833
4834
4835
4836
4837
4838
4839
4840
4841
4842
4843
4844
4845
4846
....
4885
4886
4887
4888
4889
4890
4891
4892
4893
4894
4895
4896
4897
4898
4899
....
5349
5350
5351
5352
5353
5354
5355
5356
5357
5358
5359
5360
5361
5362
5363
....
5489
5490
5491
5492
5493
5494
5495
5496
5497
5498
5499
5500
5501
5502
5503
....
5634
5635
5636
5637
5638
5639
5640
5641
5642
5643
5644
5645
5646
5647
5648
#   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_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;
................................................................................
    - 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);
................................................................................
    \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 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)

................................................................................
#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.
*/
................................................................................
   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
................................................................................
#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,
................................................................................
};

/**
  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[] = {
................................................................................
};


/**
    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;
................................................................................
                                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
................................................................................
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();
}
................................................................................
    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;
................................................................................
    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;
................................................................................
}

#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
................................................................................
    {
        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 )
{
................................................................................
{
    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( 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;
................................................................................

   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 );
................................................................................
              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;
................................................................................
   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;
................................................................................
    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;
................................................................................
    {
        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;
................................................................................
#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;
................................................................................
                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;
................................................................................
              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) );
................................................................................
              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) );
................................................................................
    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 );
................................................................................
    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; }
................................................................................
    {
        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;

Changes to src/cson_amalgamation.h.

57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
...
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
...
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
...
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
...
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
...
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
...
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
...
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
...
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
...
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
...
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
...
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
....
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
....
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
....
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
....
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
....
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
....
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
....
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
....
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
....
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
....
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
....
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
....
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
....
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
....
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
....
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
....
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
....
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
....
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
....
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
....
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
....
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
....
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
....
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
....
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
....
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
....
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
....
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
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

................................................................................

/**
   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
................................................................................
   @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.
*/
................................................................................
     */
    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
................................................................................
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;

    /**
................................................................................
   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),
................................................................................
    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:

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

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

/**
   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
................................................................................
   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
................................................................................
   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:
................................................................................
   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();

/**
   Equivalent to cson_value_new_bool(1).
................................................................................
*/
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
................................................................................

/**
   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();
................................................................................
   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);

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

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

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

................................................................................
    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()
*/
................................................................................
   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.
     */
................................................................................
       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 );


/**
................................................................................
       @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;
................................................................................
   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 ) {
................................................................................
   {
       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
................................................................................
   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)

................................................................................

    @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
................................................................................
   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
................................................................................
   (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
................................................................................
   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
................................................................................
   --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]).

................................................................................
   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
................................................................................
   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},
            ...
................................................................................
    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 );
................................................................................
   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 */







|







 







|







 







|












|



|

|






|


|





|







 







|







 







|







 







|












|







 







|







 







|



|







 







|







 







|







 







|

|

|







 







|






|







 







|







 







|







 







|







 







|







 







|







 







|


|

|







 







|







 







|







 







|







 







|







 







|




|







|








|







 







|







 







|







 







|







 







|







 







|







 







|

|







 







|







 







|







 







|







 







|







 







|







 







|







 







|




|
|




|







 







|



|



|
|

|










|


|

|







 







|

|



|


|

|

|


|





|







 







|



|




57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
...
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
...
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
...
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
...
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
...
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
...
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
...
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
...
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
...
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
...
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
...
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
....
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
....
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
....
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
....
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
....
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
....
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
....
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
....
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
....
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
....
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
....
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
....
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
....
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
....
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
....
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
....
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
....
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
....
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
....
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
....
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
....
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
....
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
....
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
....
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
....
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
....
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
....
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
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

................................................................................

/**
   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
................................................................................
   @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.
*/
................................................................................
     */
    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
................................................................................
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;

    /**
................................................................................
   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),
................................................................................
    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:

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

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

/**
   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
................................................................................
   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
................................................................................
   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:
................................................................................
   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();

/**
   Equivalent to cson_value_new_bool(1).
................................................................................
*/
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
................................................................................

/**
   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();
................................................................................
   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);

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

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

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

................................................................................
    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()
*/
................................................................................
   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.
     */
................................................................................
       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 );


/**
................................................................................
       @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;
................................................................................
   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 ) {
................................................................................
   {
       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
................................................................................
   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)

................................................................................

    @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
................................................................................
   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
................................................................................
   (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
................................................................................
   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
................................................................................
   --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]).

................................................................................
   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
................................................................................
   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},
            ...
................................................................................
    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 );
................................................................................
   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.

52
53
54
55
56
57
58

59
60
61
62
63
64
65
66
67
68
69
70
71
72
73

74
75
76

77
78
79
80
81
82
83
..
95
96
97
98
99
100
101

102
103
104
105
106
107
108
...
271
272
273
274
275
276
277

278
279
280
281
282
283
284
...
357
358
359
360
361
362
363

364
365
366
367
368
369
370
....
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
....
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
....
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
....
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
....
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
....
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287

2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
....
2470
2471
2472
2473
2474
2475
2476


































2477
2478
2479
2480
2481
2482
2483
....
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
....
2775
2776
2777
2778
2779
2780
2781

2782




2783
2784
2785
2786
2787

2788




2789
2790
2791
2792
2793
2794
2795
....
2869
2870
2871
2872
2873
2874
2875

2876
2877
2878
2879
2880
2881
2882
2883
....
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
....
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024

3025
3026
3027
3028
3029
3030
3031
** structure.
*/
struct Stmt {
  Blob sql;               /* The SQL for this statement */
  sqlite3_stmt *pStmt;    /* The results of sqlite3_prepare_v2() */
  Stmt *pNext, *pPrev;    /* List of all unfinalized statements */
  int nStep;              /* Number of sqlite3_step() calls */

};

/*
** Copy this to initialize a Stmt object to a clean/empty state. This
** is useful to help avoid assertions when performing cleanup in some
** error handling cases.
*/
#define empty_Stmt_m {BLOB_INITIALIZER,NULL, NULL, NULL, 0}
#endif /* INTERFACE */
const struct Stmt empty_Stmt = empty_Stmt_m;

/*
** Call this routine when a database error occurs.
*/
static void db_err(const char *zFormat, ...){

  va_list ap;
  char *z;
  int rc = 1;

  va_start(ap, zFormat);
  z = vmprintf(zFormat, ap);
  va_end(ap);
#ifdef FOSSIL_ENABLE_JSON
  if( g.json.isJsonMode ){
    json_err( 0, z, 1 );
    if( g.isHTTP ){
................................................................................
    g.cgiOutput = 0;
    cgi_printf("<h1>Database Error</h1>\n<p>%h</p>\n", z);
    cgi_reply();
  }else{
    fprintf(stderr, "%s: %s\n", g.argv[0], z);
  }
  free(z);

  db_force_rollback();
  fossil_exit(rc);
}

/*
** All static variable that a used by only this file are gathered into
** the following structure.
................................................................................
  }
  rc = sqlite3_prepare_v3(g.db, zSql, -1, prepFlags, &pStmt->pStmt, 0);
  if( rc!=0 && (flags & DB_PREPARE_IGNORE_ERROR)!=0 ){
    db_err("%s\n%s", sqlite3_errmsg(g.db), zSql);
  }
  pStmt->pNext = pStmt->pPrev = 0;
  pStmt->nStep = 0;

  return rc;
}
int db_prepare(Stmt *pStmt, const char *zFormat, ...){
  int rc;
  va_list ap;
  va_start(ap, zFormat);
  rc = db_vprepare(pStmt, 0, zFormat, ap);
................................................................................

/*
** Step the SQL statement.  Return either SQLITE_ROW or an error code
** or SQLITE_OK if the statement finishes successfully.
*/
int db_step(Stmt *pStmt){
  int rc;

  rc = sqlite3_step(pStmt->pStmt);
  pStmt->nStep++;
  return rc;
}

/*
** Print warnings if a query is inefficient.
................................................................................
  }
  if( zHome==0 ){
    if( isOptional ) return 0;
    fossil_fatal("cannot locate home directory - please set the "
                 "FOSSIL_HOME or HOME environment variables");
  }
#endif
  if( file_isdir(zHome)!=1 ){
    if( isOptional ) return 0;
    fossil_fatal("invalid home directory: %s", zHome);
  }
#if defined(_WIN32) || defined(__CYGWIN__)
  /* . filenames give some window systems problems and many apps problems */
  zDbName = mprintf("%//_fossil", zHome);
#else
  zDbName = mprintf("%s/.fossil", zHome);
#endif
  if( file_size(zDbName)<1024*3 ){
    if( file_access(zHome, W_OK) ){
      if( isOptional ) return 0;
      fossil_fatal("home directory %s must be writeable", zHome);
    }
    db_init_database(zDbName, zConfigSchema, (char*)0);
  }
  if( file_access(zDbName, W_OK) ){
................................................................................
** 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);
  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;

  /* If the "isexe" column is missing from the vfile table, then
................................................................................
    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)<1024 ){
    if( file_access(zDbName, F_OK) ){
#ifdef FOSSIL_ENABLE_JSON
      g.json.resultCode = FSL_JSON_E_DB_NOT_FOUND;
#endif
      fossil_panic("repository does not exist or"
                   " is in an unreadable directory: %s", zDbName);
    }else if( file_access(zDbName, R_OK) ){
................................................................................
** option to locate the repository.  If no such option is available, then
** use the repository of the open checkout if there is one.
**
** Error out if the repository cannot be opened.
*/
void db_find_and_open_repository(int bFlags, int nArgUsed){
  const char *zRep = find_repository_option();
  if( zRep && file_isdir(zRep)==1 ){
    goto rep_not_found;
  }
  if( zRep==0 && nArgUsed && g.argc==nArgUsed+1 ){
    zRep = g.argv[nArgUsed];
  }
  if( zRep==0 ){
    if( db_open_local(0)==0 ){
................................................................................
  /* We should be done with options.. */
  verify_all_options();

  if( g.argc!=3 ){
    usage("REPOSITORY-NAME");
  }

  if( -1 != file_size(g.argv[2]) ){
    fossil_fatal("file already exists: %s", g.argv[2]);
  }

  db_create_repository(g.argv[2]);
  db_open_repository(g.argv[2]);
  db_open_config(0, 0);
  if( zTemplate ) db_attach(zTemplate, "settingSrc");
................................................................................
      blob_append(&versionedPathname, ".no-warn", -1);
      blob_zero(&noWarnFile);
      if( historical_blob(g.zOpenRevision, blob_str(&versionedPathname),
          &noWarnFile, 0) ){
        noWarn = 1;
      }
      blob_reset(&noWarnFile);
    }else if( file_size(blob_str(&versionedPathname))>=0 ){
      /* File exists, and contains the value for this setting. Load from
      ** the file. */

      if( blob_read_from_file(&setting, blob_str(&versionedPathname))>=0 ){
        found = 1;
      }
      /* See if there's a no-warn flag */
      blob_append(&versionedPathname, ".no-warn", -1);
      if( file_size(blob_str(&versionedPathname))>=0 ){
        noWarn = 1;
      }
    }
    blob_reset(&versionedPathname);
    if( found ){
      blob_trim(&setting); /* Avoid non-obvious problems with line endings
                           ** on boolean properties */
................................................................................
}
int db_lget_int(const char *zName, int dflt){
  return db_int(dflt, "SELECT value FROM vvar WHERE name=%Q", zName);
}
void db_lset_int(const char *zName, int value){
  db_multi_exec("REPLACE INTO vvar(name,value) VALUES(%Q,%d)", zName, value);
}



































#if INTERFACE
/* Manifest generation flags */
#define MFESTFLG_RAW  0x01
#define MFESTFLG_UUID 0x02
#define MFESTFLG_TAGS 0x04
#endif /* INTERFACE */
................................................................................
  }
  if( pSetting->versionable && g.localOpen ){
    /* Check to see if this is overridden by a versionable settings file */
    Blob versionedPathname;
    blob_zero(&versionedPathname);
    blob_appendf(&versionedPathname, "%s.fossil-settings/%s",
                 g.zLocalRoot, pSetting->name);
    if( file_size(blob_str(&versionedPathname))>=0 ){
      fossil_print("  (overridden by contents of file .fossil-settings/%s)\n",
                   pSetting->name);
    }
  }
  db_finalize(&q);
}

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

** Allows symbolic links in the repository when enabled.




*/
#endif
#if !defined(_WIN32)
/*
** SETTING: allow-symlinks  boolean default=on versionable

** Allows symbolic links in the repository when enabled.




*/
#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 malecious robots.
................................................................................
*/
/*
** SETTING: default-perms   width=16 default=u
** 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
** The value is an external command to run when performing a diff.
................................................................................
/*
** SETTING: exec-rel-paths   boolean default=off
** When executing certain external commands (e.g. diff and
** gdiff), use relative paths.
*/
#endif
/*
** SETTING; gdiff-command    width=40 default=gdiff
** 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
** The value is a graphical merge conflict resolver command operating
** on four files.  Examples:
................................................................................
** If enabled, the "mv" and "rename" commands will also move
** the associated files within the checkout -AND- the "rm"
** and "delete" commands will also remove the associated
** files from within the checkout.
*/
#endif
/*
** SETTING; pgp-command      width=40
** DEFAULT: gpg --clearsign -o
**
** Command used to clear-sign manifests at check-in.

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







>







|







>



>







 







>







 







>







 







>







 







|









|







 







|







 







|







 







|







 







|







 







|


>
|




|







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







|







 







>
|
>
>
>
>





>
|
>
>
>
>







 







>
|







 







|







 







|
<
<

>







52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
..
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
...
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
...
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
....
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
....
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
....
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
....
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
....
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
....
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
....
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
....
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
....
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
....
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
....
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
....
3066
3067
3068
3069
3070
3071
3072
3073


3074
3075
3076
3077
3078
3079
3080
3081
3082
** structure.
*/
struct Stmt {
  Blob sql;               /* The SQL for this statement */
  sqlite3_stmt *pStmt;    /* The results of sqlite3_prepare_v2() */
  Stmt *pNext, *pPrev;    /* List of all unfinalized statements */
  int nStep;              /* Number of sqlite3_step() calls */
  int rc;                 /* Error from db_vprepare() */
};

/*
** Copy this to initialize a Stmt object to a clean/empty state. This
** is useful to help avoid assertions when performing cleanup in some
** error handling cases.
*/
#define empty_Stmt_m {BLOB_INITIALIZER,NULL, NULL, NULL, 0, 0}
#endif /* INTERFACE */
const struct Stmt empty_Stmt = empty_Stmt_m;

/*
** Call this routine when a database error occurs.
*/
static void db_err(const char *zFormat, ...){
  static int rcLooping = 0;
  va_list ap;
  char *z;
  int rc = 1;
  if( rcLooping ) exit(rcLooping);
  va_start(ap, zFormat);
  z = vmprintf(zFormat, ap);
  va_end(ap);
#ifdef FOSSIL_ENABLE_JSON
  if( g.json.isJsonMode ){
    json_err( 0, z, 1 );
    if( g.isHTTP ){
................................................................................
    g.cgiOutput = 0;
    cgi_printf("<h1>Database Error</h1>\n<p>%h</p>\n", z);
    cgi_reply();
  }else{
    fprintf(stderr, "%s: %s\n", g.argv[0], z);
  }
  free(z);
  rcLooping = rc;
  db_force_rollback();
  fossil_exit(rc);
}

/*
** All static variable that a used by only this file are gathered into
** the following structure.
................................................................................
  }
  rc = sqlite3_prepare_v3(g.db, zSql, -1, prepFlags, &pStmt->pStmt, 0);
  if( rc!=0 && (flags & DB_PREPARE_IGNORE_ERROR)!=0 ){
    db_err("%s\n%s", sqlite3_errmsg(g.db), zSql);
  }
  pStmt->pNext = pStmt->pPrev = 0;
  pStmt->nStep = 0;
  pStmt->rc = rc;
  return rc;
}
int db_prepare(Stmt *pStmt, const char *zFormat, ...){
  int rc;
  va_list ap;
  va_start(ap, zFormat);
  rc = db_vprepare(pStmt, 0, zFormat, ap);
................................................................................

/*
** Step the SQL statement.  Return either SQLITE_ROW or an error code
** or SQLITE_OK if the statement finishes successfully.
*/
int db_step(Stmt *pStmt){
  int rc;
  if( pStmt->pStmt==0 ) return pStmt->rc;
  rc = sqlite3_step(pStmt->pStmt);
  pStmt->nStep++;
  return rc;
}

/*
** Print warnings if a query is inefficient.
................................................................................
  }
  if( zHome==0 ){
    if( isOptional ) return 0;
    fossil_fatal("cannot locate home directory - please set the "
                 "FOSSIL_HOME or HOME environment variables");
  }
#endif
  if( file_isdir(zHome, ExtFILE)!=1 ){
    if( isOptional ) return 0;
    fossil_fatal("invalid home directory: %s", zHome);
  }
#if defined(_WIN32) || defined(__CYGWIN__)
  /* . filenames give some window systems problems and many apps problems */
  zDbName = mprintf("%//_fossil", zHome);
#else
  zDbName = mprintf("%s/.fossil", zHome);
#endif
  if( file_size(zDbName, ExtFILE)<1024*3 ){
    if( file_access(zHome, W_OK) ){
      if( isOptional ) return 0;
      fossil_fatal("home directory %s must be writeable", zHome);
    }
    db_init_database(zDbName, zConfigSchema, (char*)0);
  }
  if( file_access(zDbName, W_OK) ){
................................................................................
** 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;

  /* If the "isexe" column is missing from the vfile table, then
................................................................................
    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( file_access(zDbName, F_OK) ){
#ifdef FOSSIL_ENABLE_JSON
      g.json.resultCode = FSL_JSON_E_DB_NOT_FOUND;
#endif
      fossil_panic("repository does not exist or"
                   " is in an unreadable directory: %s", zDbName);
    }else if( file_access(zDbName, R_OK) ){
................................................................................
** option to locate the repository.  If no such option is available, then
** use the repository of the open checkout if there is one.
**
** Error out if the repository cannot be opened.
*/
void db_find_and_open_repository(int bFlags, int nArgUsed){
  const char *zRep = find_repository_option();
  if( zRep && file_isdir(zRep, ExtFILE)==1 ){
    goto rep_not_found;
  }
  if( zRep==0 && nArgUsed && g.argc==nArgUsed+1 ){
    zRep = g.argv[nArgUsed];
  }
  if( zRep==0 ){
    if( db_open_local(0)==0 ){
................................................................................
  /* 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");
................................................................................
      blob_append(&versionedPathname, ".no-warn", -1);
      blob_zero(&noWarnFile);
      if( historical_blob(g.zOpenRevision, blob_str(&versionedPathname),
          &noWarnFile, 0) ){
        noWarn = 1;
      }
      blob_reset(&noWarnFile);
    }else if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){
      /* File exists, and contains the value for this setting. Load from
      ** the file. */
      const char *zFile = blob_str(&versionedPathname);
      if( blob_read_from_file(&setting, zFile, ExtFILE)>=0 ){
        found = 1;
      }
      /* See if there's a no-warn flag */
      blob_append(&versionedPathname, ".no-warn", -1);
      if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){
        noWarn = 1;
      }
    }
    blob_reset(&versionedPathname);
    if( found ){
      blob_trim(&setting); /* Avoid non-obvious problems with line endings
                           ** on boolean properties */
................................................................................
}
int db_lget_int(const char *zName, int dflt){
  return db_int(dflt, "SELECT value FROM vvar WHERE name=%Q", zName);
}
void db_lset_int(const char *zName, int value){
  db_multi_exec("REPLACE INTO vvar(name,value) VALUES(%Q,%d)", zName, value);
}

/* Va-args versions of db_get(), db_set(), and db_unset()
*/
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);
  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);
  fossil_free(zName);
}



#if INTERFACE
/* Manifest generation flags */
#define MFESTFLG_RAW  0x01
#define MFESTFLG_UUID 0x02
#define MFESTFLG_TAGS 0x04
#endif /* INTERFACE */
................................................................................
  }
  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 ){
      fossil_print("  (overridden by contents of file .fossil-settings/%s)\n",
                   pSetting->name);
    }
  }
  db_finalize(&q);
}

................................................................................
**
** 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
**
** 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)
/*
** 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
** 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 malecious robots.
................................................................................
*/
/*
** SETTING: default-perms   width=16 default=u
** 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
** The value is an external command to run when performing a diff.
................................................................................
/*
** SETTING: exec-rel-paths   boolean default=off
** When executing certain external commands (e.g. diff and
** gdiff), use relative paths.
*/
#endif
/*
** SETTING: gdiff-command    width=40 default=gdiff
** 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
** The value is a graphical merge conflict resolver command operating
** on four files.  Examples:
................................................................................
** If enabled, the "mv" and "rename" commands will also move
** the associated files within the checkout -AND- the "rm"
** and "delete" commands will also remove the associated
** files from within the checkout.
*/
#endif
/*
** SETTING: pgp-command      width=40


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

Added 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
// 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;
}
table.browser {
  width: 100%;
  border: 0;
}
td.browser {
  width: 24%;
  vertical-align: top;
}
.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;
}
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 {
  float: left;
  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;
}
.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;
}

Changes to src/delta.c.

569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
...
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
  int lenSrc,            /* Length of the source file */
  const char *zDelta,    /* Delta to apply to the pattern */
  int lenDelta,          /* Length of the delta */
  char *zOut             /* Write the output into this preallocated buffer */
){
  unsigned int limit;
  unsigned int total = 0;
#ifndef FOSSIL_OMIT_DELTA_CKSUM_TEST
  char *zOrigOut = zOut;
#endif

  limit = getInt(&zDelta, &lenDelta);
  if( *zDelta!='\n' ){
    /* ERROR: size integer not terminated by "\n" */
    return -1;
................................................................................
        zDelta += cnt;
        lenDelta -= cnt;
        break;
      }
      case ';': {
        zDelta++; lenDelta--;
        zOut[0] = 0;
#ifndef FOSSIL_OMIT_DELTA_CKSUM_TEST
        if( cnt!=checksum(zOrigOut, total) ){
          /* ERROR:  bad checksum */
          return -1;
        }
#endif
        if( total!=limit ){
          /* ERROR: generated size does not match predicted size */







|







 







|







569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
...
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
  int lenSrc,            /* Length of the source file */
  const char *zDelta,    /* Delta to apply to the pattern */
  int lenDelta,          /* Length of the delta */
  char *zOut             /* Write the output into this preallocated buffer */
){
  unsigned int limit;
  unsigned int total = 0;
#ifdef FOSSIL_ENABLE_DELTA_CKSUM_TEST
  char *zOrigOut = zOut;
#endif

  limit = getInt(&zDelta, &lenDelta);
  if( *zDelta!='\n' ){
    /* ERROR: size integer not terminated by "\n" */
    return -1;
................................................................................
        zDelta += cnt;
        lenDelta -= cnt;
        break;
      }
      case ';': {
        zDelta++; lenDelta--;
        zOut[0] = 0;
#ifdef FOSSIL_ENABLE_DELTA_CKSUM_TEST
        if( cnt!=checksum(zOrigOut, total) ){
          /* ERROR:  bad checksum */
          return -1;
        }
#endif
        if( total!=limit ){
          /* ERROR: generated size does not match predicted size */

Changes to src/deltacmd.c.

51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
..
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
...
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
...
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
** Store the result in DELTA.
*/
void delta_create_cmd(void){
  Blob orig, target, delta;
  if( g.argc!=5 ){
    usage("ORIGIN TARGET DELTA");
  }
  if( blob_read_from_file(&orig, g.argv[2])<0 ){
    fossil_fatal("cannot read %s", g.argv[2]);
  }
  if( blob_read_from_file(&target, g.argv[3])<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) ){
    fossil_fatal("cannot write %s", g.argv[4]);
  }
  blob_reset(&orig);
................................................................................
  Blob orig, target, delta;
  int nCopy = 0;
  int nInsert = 0;
  int sz1, sz2, sz3;
  if( g.argc!=4 ){
    usage("ORIGIN TARGET");
  }
  if( blob_read_from_file(&orig, g.argv[2])<0 ){
    fossil_fatal("cannot read %s", g.argv[2]);
  }
  if( blob_read_from_file(&target, g.argv[3])<0 ){
    fossil_fatal("cannot read %s", g.argv[3]);
  }
  blob_delta_create(&orig, &target, &delta);
  delta_analyze(blob_buffer(&delta), blob_size(&delta), &nCopy, &nInsert);
  sz1 = blob_size(&orig);
  sz2 = blob_size(&target);
  sz3 = blob_size(&delta);
................................................................................
** Apply DELTA to FILE1 and output the result.
*/
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])<0 ){
    fossil_fatal("cannot read %s", g.argv[2]);
  }
  if( blob_read_from_file(&delta, g.argv[3])<0 ){
    fossil_fatal("cannot read %s", g.argv[3]);
  }
  blob_delta_apply(&orig, &delta, &target);
  if( blob_write_to_file(&target, g.argv[4])<blob_size(&target) ){
    fossil_fatal("cannot write %s", g.argv[4]);
  }
  blob_reset(&orig);
................................................................................
** correctly recovered.
*/
void cmd_test_delta(void){
  Blob f1, f2;     /* Original file content */
  Blob d12, d21;   /* Deltas from f1->f2 and f2->f1 */
  Blob a1, a2;     /* Recovered file content */
  if( g.argc!=4 ) usage("FILE1 FILE2");
  blob_read_from_file(&f1, g.argv[2]);
  blob_read_from_file(&f2, g.argv[3]);
  blob_delta_create(&f1, &f2, &d12);
  blob_delta_create(&f2, &f1, &d21);
  blob_delta_apply(&f1, &d12, &a2);
  blob_delta_apply(&f2, &d21, &a1);
  if( blob_compare(&f1,&a1) || blob_compare(&f2, &a2) ){
    fossil_fatal("delta test failed");
  }
  fossil_print("ok\n");
}







|


|







 







|


|







 







|


|







 







|
|









51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
..
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
...
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
...
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
** Store the result in DELTA.
*/
void delta_create_cmd(void){
  Blob orig, target, delta;
  if( g.argc!=5 ){
    usage("ORIGIN TARGET DELTA");
  }
  if( blob_read_from_file(&orig, g.argv[2], ExtFILE)<0 ){
    fossil_fatal("cannot read %s", g.argv[2]);
  }
  if( blob_read_from_file(&target, g.argv[3], ExtFILE)<0 ){
    fossil_fatal("cannot read %s", g.argv[3]);
  }
  blob_delta_create(&orig, &target, &delta);
  if( blob_write_to_file(&delta, g.argv[4])<blob_size(&delta) ){
    fossil_fatal("cannot write %s", g.argv[4]);
  }
  blob_reset(&orig);
................................................................................
  Blob orig, target, delta;
  int nCopy = 0;
  int nInsert = 0;
  int sz1, sz2, sz3;
  if( g.argc!=4 ){
    usage("ORIGIN TARGET");
  }
  if( blob_read_from_file(&orig, g.argv[2], ExtFILE)<0 ){
    fossil_fatal("cannot read %s", g.argv[2]);
  }
  if( blob_read_from_file(&target, g.argv[3], ExtFILE)<0 ){
    fossil_fatal("cannot read %s", g.argv[3]);
  }
  blob_delta_create(&orig, &target, &delta);
  delta_analyze(blob_buffer(&delta), blob_size(&delta), &nCopy, &nInsert);
  sz1 = blob_size(&orig);
  sz2 = blob_size(&target);
  sz3 = blob_size(&delta);
................................................................................
** Apply DELTA to FILE1 and output the result.
*/
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_delta_apply(&orig, &delta, &target);
  if( blob_write_to_file(&target, g.argv[4])<blob_size(&target) ){
    fossil_fatal("cannot write %s", g.argv[4]);
  }
  blob_reset(&orig);
................................................................................
** correctly recovered.
*/
void cmd_test_delta(void){
  Blob f1, f2;     /* Original file content */
  Blob d12, d21;   /* Deltas from f1->f2 and f2->f1 */
  Blob a1, a2;     /* Recovered file content */
  if( g.argc!=4 ) usage("FILE1 FILE2");
  blob_read_from_file(&f1, g.argv[2], ExtFILE);
  blob_read_from_file(&f2, g.argv[3], ExtFILE);
  blob_delta_create(&f1, &f2, &d12);
  blob_delta_create(&f2, &f1, &d21);
  blob_delta_apply(&f1, &d12, &a2);
  blob_delta_apply(&f2, &d21, &a1);
  if( blob_compare(&f1,&a1) || blob_compare(&f2, &a2) ){
    fossil_fatal("delta test failed");
  }
  fossil_print("ok\n");
}

Changes to src/diff.c.

2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
....
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
....
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
....
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261



2262
2263
2264
2265
2266
2267
2268
....
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
....
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
....
2574
2575
2576
2577
2578
2579
2580


2581
2582
2583
2584
2585
2586
2587
....
2597
2598
2599
2600
2601
2602
2603


2604
2605
2606
2607
2608
2609
2610
2611
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]);
  for(i=3; i<g.argc; i++){
    if( i>3 ) fossil_print("-------------------------------\n");
    blob_read_from_file(&b, g.argv[i]);
    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);
  }
................................................................................
    const char *zErr = re_compile(&pRe, zRe, 0);
    if( zErr ) fossil_fatal("regex error: %s", zErr);
  }
  diffFlag = diff_options();
  verify_all_options();
  if( g.argc!=4 ) usage("FILE1 FILE2");
  diff_print_filenames(g.argv[2], g.argv[3], diffFlag);
  blob_read_from_file(&a, g.argv[2]);
  blob_read_from_file(&b, g.argv[3]);
  blob_zero(&out);
  text_diff(&a, &b, &out, pRe, diffFlag);
  blob_write_to_file(&out, "-");
  re_free(pRe);
}

/**************************************************************************
................................................................................
  const char *zRevision, /* Use the version of the file in this check-in */
  const char *zLimit,    /* Limit the number of versions analyzed */
  const char *zOrigin,   /* The origin check-in, or NULL for root-of-tree */
  u64 annFlags           /* Flags to alter the annotation */
){
  Blob toAnnotate;       /* Text of the final (mid) version of the file */
  Blob step;             /* Text of previous revision */
  Blob treename;         /* FILENAME translated to canonical form */
  int cid;               /* Selected check-in ID */
  int origid = 0;        /* The origin ID or zero */
  int rid;               /* Artifact ID of the file being annotated */
  int fnid;              /* Filename ID */
  Stmt q;                /* Query returning all ancestor versions */
  int cnt = 0;           /* Number of versions analyzed */
  int iLimit;            /* Maximum number of versions to analyze */
................................................................................
  if( origid ){
    path_shortest_stored_in_ancestor_table(origid, cid);
  }else{
    compute_direct_ancestors(cid);
  }

  /* Get filename ID */
  file_tree_name(zFilename, &treename, 0, 1);
  zFilename = blob_str(&treename);
  fnid = db_int(0, "SELECT fnid FROM filename WHERE name=%Q", zFilename);




  db_prepare(&q,
    "SELECT DISTINCT"
    "   (SELECT uuid FROM blob WHERE rid=mlink.fid),"
    "   (SELECT uuid FROM blob WHERE rid=mlink.mid),"
    "   date(event.mtime),"
    "   coalesce(event.euser,event.user),"
................................................................................
  if( zLimit ){
    url_add_parameter(&url, "limit", zLimit);
  }
  url_add_parameter(&url, "w", ignoreWs ? "1" : "0");
  url_add_parameter(&url, "log", showLog ? "1" : "0");
  url_add_parameter(&url, "filevers", fileVers ? "1" : "0");
  style_submenu_checkbox("w", "Ignore Whitespace", 0, 0);
  style_submenu_checkbox("log", "Log", 0, "toggle_annotation_log()");
  style_submenu_checkbox("filevers", "Link to Files", 0, 0);
  if( ann.bMoreToDo ){
    style_submenu_element("All Ancestors", "%s",
       url_render(&url, "limit", "none", 0, 0));
  }
  if( skin_detail_boolean("white-foreground") ){
    clr1 = 0xa04040;
................................................................................
    @ check-in %z(href("%R/info/%!S",p->zMUuid))%S(p->zMUuid)</a>
    @ artifact %z(href("%R/artifact/%!S",p->zFUuid))%S(p->zFUuid)</a>
    @ </span>
  }
  @ </ol>
  @ <hr />
  @ </div>
  @ <script>
  @ function toggle_annotation_log(){
  @   var w = gebi("annotation_log");
  @   var x = document.forms["f01"].elements["log"].checked
  @   w.style.display = x ? "block" : "none";
  @ }
  @ </script>

  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>
    @ from check-in %z(href("%R/info/%!S",zCI))%S(zCI)</a>:</h2>
  }else if( ann.origId>0 ){
................................................................................
  const char *zLimit;    /* The value to the -n|--limit option */
  const char *zOrig;     /* The value for -o|--origin */
  int showLog;           /* True to show the log */
  int fileVers;          /* Show file version instead of check-in versions */
  u64 annFlags = 0;      /* Flags to control annotation properties */
  int bBlame = 0;        /* True for BLAME output.  False for ANNOTATE. */
  int szHash;            /* Display size of a version hash */



  bBlame = g.argv[1][0]!='a';
  zRevision = find_option("r","revision",1);
  zLimit = find_option("limit","n",1);
  zOrig = find_option("origin","o",1);
  showLog = find_option("log","l",0)!=0;
  if( find_option("ignore-trailing-space","Z",0)!=0 ){
................................................................................
  verify_all_options();

  if( g.argc<3 ) {
    usage("FILENAME");
  }

  annFlags |= DIFF_STRIP_EOLCR;


  annotate_file(&ann, g.argv[2], zRevision, zLimit, zOrig, annFlags);
  if( showLog ){
    struct AnnVers *p;
    for(p=ann.aVers, i=0; i<ann.nVers; i++, p++){
      fossil_print("version %3d: %s %S file %S\n",
                   i+1, p->zDate, p->zMUuid, p->zFUuid);
    }
    fossil_print("---------------------------------------------------\n");







|


|







 







|
|







 







<







 







<
<

>
>
>







 







|







 







<
<
<
<
<
<
<







 







>
>







 







>
>
|







2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
....
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
....
2205
2206
2207
2208
2209
2210
2211

2212
2213
2214
2215
2216
2217
2218
....
2251
2252
2253
2254
2255
2256
2257


2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
....
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
....
2456
2457
2458
2459
2460
2461
2462







2463
2464
2465
2466
2467
2468
2469
....
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
....
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
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);
  }
................................................................................
    const char *zErr = re_compile(&pRe, zRe, 0);
    if( zErr ) fossil_fatal("regex error: %s", zErr);
  }
  diffFlag = diff_options();
  verify_all_options();
  if( g.argc!=4 ) usage("FILE1 FILE2");
  diff_print_filenames(g.argv[2], g.argv[3], diffFlag);
  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);
  blob_write_to_file(&out, "-");
  re_free(pRe);
}

/**************************************************************************
................................................................................
  const char *zRevision, /* Use the version of the file in this check-in */
  const char *zLimit,    /* Limit the number of versions analyzed */
  const char *zOrigin,   /* The origin check-in, or NULL for root-of-tree */
  u64 annFlags           /* Flags to alter the annotation */
){
  Blob toAnnotate;       /* Text of the final (mid) version of the file */
  Blob step;             /* Text of previous revision */

  int cid;               /* Selected check-in ID */
  int origid = 0;        /* The origin ID or zero */
  int rid;               /* Artifact ID of the file being annotated */
  int fnid;              /* Filename ID */
  Stmt q;                /* Query returning all ancestor versions */
  int cnt = 0;           /* Number of versions analyzed */
  int iLimit;            /* Maximum number of versions to analyze */
................................................................................
  if( origid ){
    path_shortest_stored_in_ancestor_table(origid, cid);
  }else{
    compute_direct_ancestors(cid);
  }

  /* Get filename ID */


  fnid = db_int(0, "SELECT fnid FROM filename WHERE name=%Q", zFilename);
  if( fnid==0 ){
    fossil_fatal("no such file: %Q", zFilename);
  }

  db_prepare(&q,
    "SELECT DISTINCT"
    "   (SELECT uuid FROM blob WHERE rid=mlink.fid),"
    "   (SELECT uuid FROM blob WHERE rid=mlink.mid),"
    "   date(event.mtime),"
    "   coalesce(event.euser,event.user),"
................................................................................
  if( zLimit ){
    url_add_parameter(&url, "limit", zLimit);
  }
  url_add_parameter(&url, "w", ignoreWs ? "1" : "0");
  url_add_parameter(&url, "log", showLog ? "1" : "0");
  url_add_parameter(&url, "filevers", fileVers ? "1" : "0");
  style_submenu_checkbox("w", "Ignore Whitespace", 0, 0);
  style_submenu_checkbox("log", "Log", 0, "toggle_annotation_log");
  style_submenu_checkbox("filevers", "Link to Files", 0, 0);
  if( ann.bMoreToDo ){
    style_submenu_element("All Ancestors", "%s",
       url_render(&url, "limit", "none", 0, 0));
  }
  if( skin_detail_boolean("white-foreground") ){
    clr1 = 0xa04040;
................................................................................
    @ check-in %z(href("%R/info/%!S",p->zMUuid))%S(p->zMUuid)</a>
    @ artifact %z(href("%R/artifact/%!S",p->zFUuid))%S(p->zFUuid)</a>
    @ </span>
  }
  @ </ol>
  @ <hr />
  @ </div>








  if( !ann.bMoreToDo ){
    assert( ann.origId==0 );  /* bMoreToDo always set for a point-to-point */
    @ <h2>Origin for each line in
    @ %z(href("%R/finfo?name=%h&ci=%!S", zFilename, zCI))%h(zFilename)</a>
    @ from check-in %z(href("%R/info/%!S",zCI))%S(zCI)</a>:</h2>
  }else if( ann.origId>0 ){
................................................................................
  const char *zLimit;    /* The value to the -n|--limit option */
  const char *zOrig;     /* The value for -o|--origin */
  int showLog;           /* True to show the log */
  int fileVers;          /* Show file version instead of check-in versions */
  u64 annFlags = 0;      /* Flags to control annotation properties */
  int bBlame = 0;        /* True for BLAME output.  False for ANNOTATE. */
  int szHash;            /* Display size of a version hash */
  Blob treename;         /* Name of file to be annotated */
  char *zFilename;       /* Name of file to be annotated */

  bBlame = g.argv[1][0]!='a';
  zRevision = find_option("r","revision",1);
  zLimit = find_option("limit","n",1);
  zOrig = find_option("origin","o",1);
  showLog = find_option("log","l",0)!=0;
  if( find_option("ignore-trailing-space","Z",0)!=0 ){
................................................................................
  verify_all_options();

  if( g.argc<3 ) {
    usage("FILENAME");
  }

  annFlags |= DIFF_STRIP_EOLCR;
  file_tree_name(g.argv[2], &treename, 0, 1);
  zFilename = blob_str(&treename);
  annotate_file(&ann, zFilename, zRevision, zLimit, zOrig, annFlags);
  if( showLog ){
    struct AnnVers *p;
    for(p=ann.aVers, i=0; i<ann.nVers; i++, p++){
      fossil_print("version %3d: %s %S file %S\n",
                   i+1, p->zDate, p->zMUuid, p->zFUuid);
    }
    fossil_print("---------------------------------------------------\n");

Changes to src/diff.tcl.

230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
...
393
394
395
396
397
398
399

400
401
402
403
404
405
406
wm title . $CFG(TITLE)
wm iconname . $CFG(TITLE)
# Keystroke bindings for on the top-level window for navigation and
# control also fire when those same keystrokes are pressed in the
# Search entry box.  Disable them, to prevent the diff screen from
# disappearing abruptly and unexpectedly when searching for "q".
#
# bind . <q> exit
# bind . <p> {catch searchPrev; break}
# bind . <n> {catch searchNext; break}
# bind . <Escape><Escape> exit
bind . <Destroy> {after 0 exit}
bind . <Tab> {cycleDiffs; break}
bind . <<PrevWindow>> {cycleDiffs 1; break}
bind . <Control-f> {searchOnOff; break}
bind . <Control-g> {catch searchNext; break}
bind . <Return> {
  event generate bb.files <1>
  event generate .bb.files <ButtonRelease-1>
  break
}
foreach {key axis args} {
  Up    y {scroll -5 units}
  k     y {scroll -5 units}
  Down  y {scroll 5 units}
................................................................................
}
proc searchOnOff {} {
  if {[info exists ::search]} {
    unset ::search
    .txtA tag remove search 1.0 end
    .txtB tag remove search 1.0 end
    pack forget .bb.sframe

  } else {
    set ::search .txtA
    if {![winfo exists .bb.sframe]} {
      frame .bb.sframe
      ::ttk::entry .bb.sframe.e -width 10
      pack .bb.sframe.e -side left -fill y -expand 1
      bind .bb.sframe.e <Return> {searchNext; break}







|
|
|
|






|







 







>







230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
...
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
wm title . $CFG(TITLE)
wm iconname . $CFG(TITLE)
# Keystroke bindings for on the top-level window for navigation and
# control also fire when those same keystrokes are pressed in the
# Search entry box.  Disable them, to prevent the diff screen from
# disappearing abruptly and unexpectedly when searching for "q".
#
bind . <Control-q> exit
bind . <Control-p> {catch searchPrev; break}
bind . <Control-n> {catch searchNext; break}
bind . <Escape><Escape> exit
bind . <Destroy> {after 0 exit}
bind . <Tab> {cycleDiffs; break}
bind . <<PrevWindow>> {cycleDiffs 1; break}
bind . <Control-f> {searchOnOff; break}
bind . <Control-g> {catch searchNext; break}
bind . <Return> {
  event generate .bb.files <1>
  event generate .bb.files <ButtonRelease-1>
  break
}
foreach {key axis args} {
  Up    y {scroll -5 units}
  k     y {scroll -5 units}
  Down  y {scroll 5 units}
................................................................................
}
proc searchOnOff {} {
  if {[info exists ::search]} {
    unset ::search
    .txtA tag remove search 1.0 end
    .txtB tag remove search 1.0 end
    pack forget .bb.sframe
    focus .
  } else {
    set ::search .txtA
    if {![winfo exists .bb.sframe]} {
      frame .bb.sframe
      ::ttk::entry .bb.sframe.e -width 10
      pack .bb.sframe.e -side left -fill y -expand 1
      bind .bb.sframe.e <Return> {searchNext; break}

Changes to src/diffcmd.c.

178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
...
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
...
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
...
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
  if( 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_wd_size(zFile2)<0 ){
      zName2 = NULL_DEVICE;
    }else{
      if( file_wd_islink(0) ){
        blob_read_link(&file2, zFile2);
      }else{
        blob_read_from_file(&file2, zFile2);
      }
      zName2 = zName;
    }

    /* Compute and output the differences */
    if( diffFlags & DIFF_BRIEF ){
      if( blob_compare(pFile1, &file2) ){
        fossil_print("CHANGED  %s\n", zName);
................................................................................
          fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY);
          glob_free(pBinary);
          return;
        }
        glob_free(pBinary);
      }
      blob_zero(&file2);
      if( file_wd_size(zFile2)>=0 ){
        if( file_wd_islink(0) ){
          blob_read_link(&file2, zFile2);
        }else{
          blob_read_from_file(&file2, zFile2);
        }
      }
      if( looks_like_binary(&file2) ){
        fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY);
        blob_reset(&file2);
        return;
      }
      blob_reset(&file2);
................................................................................
      if( !isNumStat ){ fossil_print("ADDED_BY_INTEGRATE %s\n", zPathname); }
      srcid = 0;
      if( !asNewFile ){ showDiff = 0; }
    }
    if( showDiff ){
      Blob content;
      int isBin;
      if( !isLink != !file_wd_islink(zFullName) ){
        diff_print_index(zPathname, diffFlags);
        diff_print_filenames(zPathname, zPathname, diffFlags);
        fossil_print("%s",DIFF_CANNOT_COMPUTE_SYMLINK);
        continue;
      }
      if( srcid>0 ){
        content_get(srcid, &content);
................................................................................
                      diffFlags, pFileDir);
  }
  if( pFileDir ){
    int i;
    for(i=0; pFileDir[i].zName; i++){
      if( pFileDir[i].nUsed==0
       && strcmp(pFileDir[0].zName,".")!=0
       && !file_wd_isdir(g.argv[i+2])
      ){
        fossil_fatal("not found: '%s'", g.argv[i+2]);
      }
      fossil_free(pFileDir[i].zName);
    }
    fossil_free(pFileDir);
  }







|


<
<
<
|
<







 







|
<
<
<
|
<







 







|







 







|







178
179
180
181
182
183
184
185
186
187



188

189
190
191
192
193
194
195
...
231
232
233
234
235
236
237
238



239

240
241
242
243
244
245
246
...
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
...
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
  if( 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 ){
      zName2 = NULL_DEVICE;
    }else{



      blob_read_from_file(&file2, zFile2, ExtFILE);

      zName2 = zName;
    }

    /* Compute and output the differences */
    if( diffFlags & DIFF_BRIEF ){
      if( blob_compare(pFile1, &file2) ){
        fossil_print("CHANGED  %s\n", zName);
................................................................................
          fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY);
          glob_free(pBinary);
          return;
        }
        glob_free(pBinary);
      }
      blob_zero(&file2);
      if( file_size(zFile2, ExtFILE)>=0 ){



        blob_read_from_file(&file2, zFile2, ExtFILE);

      }
      if( looks_like_binary(&file2) ){
        fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY);
        blob_reset(&file2);
        return;
      }
      blob_reset(&file2);
................................................................................
      if( !isNumStat ){ fossil_print("ADDED_BY_INTEGRATE %s\n", zPathname); }
      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);
        fossil_print("%s",DIFF_CANNOT_COMPUTE_SYMLINK);
        continue;
      }
      if( srcid>0 ){
        content_get(srcid, &content);
................................................................................
                      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);
  }

Changes to src/dispatch.c.

351
352
353
354
355
356
357

358
359
360
361
362

363
364
365
366
367
368
369
370

371
372
373
374
375
376
377
...
379
380
381
382
383
384
385

386
387
388
389
390
391
392
...
409
410
411
412
413
414
415

416
417
418
419
420
421
422
...
439
440
441
442
443
444
445

446
447
448
449
450
451
452
...
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
...
651
652
653
654
655
656
657
658
659
660
      }
    }
  }else{
    int i, j, n;

    style_header("Help");


    @ <h1>Available commands:</h1>
    @ <table border="0"><tr>
    for(i=j=0; i<MX_COMMAND; i++){
      const char *z = aCommand[i].zName;
      if( '/'==*z || strncmp(z,"test",4)==0 ) continue;

      j++;
    }
    n = (j+5)/6;
    for(i=j=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( j==0 ){
        @ <td valign="top"><ul>
      }
      @ <li><a href="%R/help?cmd=%s(z)">%s(zBoldOn)%s(z)%s(zBoldOff)</a></li>
      j++;
      if( j>=n ){
        @ </ul></td>
................................................................................
      }
    }
    if( j>0 ){
      @ </ul></td>
    }
    @ </tr></table>


    @ <h1>Available web UI pages:</h1>
    @ <table border="0"><tr>
    for(i=j=0; i<MX_COMMAND; i++){
      const char *z = aCommand[i].zName;
      if( '/'!=*z ) continue;
      j++;
    }
................................................................................
      }
    }
    if( j>0 ){
      @ </ul></td>
    }
    @ </tr></table>


    @ <h1>Unsupported commands:</h1>
    @ <table border="0"><tr>
    for(i=j=0; i<MX_COMMAND; i++){
      const char *z = aCommand[i].zName;
      if( strncmp(z,"test",4)!=0 ) continue;
      j++;
    }
................................................................................
      }
    }
    if( j>0 ){
      @ </ul></td>
    }
    @ </tr></table>


    @ <h1>Settings:</h1>
    @ <table border="0"><tr>
    for(i=j=0; i<MX_COMMAND; i++){
      if( (aCommand[i].eCmdFlags & CMDFLAG_SETTING)==0 ) continue;
      j++;
    }
    n = (j+4)/5;
................................................................................
**
** 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:
**
**    %fossil help                Show common commands
**    %fossil help -a|--all       Show both common and auxiliary commands
**    %fossil help -s|--settings  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
*/
void help_cmd(void){
  int rc;
  int isPage = 0;
................................................................................
/*
** 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]));
  return aSetting;
}







>





>








>







 







>







 







>







 







>







 







|







 







|


351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
...
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
...
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
...
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
...
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
...
657
658
659
660
661
662
663
664
665
666
      }
    }
  }else{
    int i, j, n;

    style_header("Help");

    @ <a name='commands'></a>
    @ <h1>Available commands:</h1>
    @ <table border="0"><tr>
    for(i=j=0; i<MX_COMMAND; i++){
      const char *z = aCommand[i].zName;
      if( '/'==*z || strncmp(z,"test",4)==0 ) continue;
      if( (aCommand[i].eCmdFlags & CMDFLAG_SETTING)!=0 ) continue;
      j++;
    }
    n = (j+5)/6;
    for(i=j=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;
      if( j==0 ){
        @ <td valign="top"><ul>
      }
      @ <li><a href="%R/help?cmd=%s(z)">%s(zBoldOn)%s(z)%s(zBoldOff)</a></li>
      j++;
      if( j>=n ){
        @ </ul></td>
................................................................................
      }
    }
    if( j>0 ){
      @ </ul></td>
    }
    @ </tr></table>

    @ <a name='webpages'></a>
    @ <h1>Available web UI pages:</h1>
    @ <table border="0"><tr>
    for(i=j=0; i<MX_COMMAND; i++){
      const char *z = aCommand[i].zName;
      if( '/'!=*z ) continue;
      j++;
    }
................................................................................
      }
    }
    if( j>0 ){
      @ </ul></td>
    }
    @ </tr></table>

    @ <a name='unsupported'></a>
    @ <h1>Unsupported commands:</h1>
    @ <table border="0"><tr>
    for(i=j=0; i<MX_COMMAND; i++){
      const char *z = aCommand[i].zName;
      if( strncmp(z,"test",4)!=0 ) continue;
      j++;
    }
................................................................................
      }
    }
    if( j>0 ){
      @ </ul></td>
    }
    @ </tr></table>

    @ <a name='settings'></a>
    @ <h1>Settings:</h1>
    @ <table border="0"><tr>
    for(i=j=0; i<MX_COMMAND; i++){
      if( (aCommand[i].eCmdFlags & CMDFLAG_SETTING)==0 ) continue;
      j++;
    }
    n = (j+4)/5;
................................................................................
**
** 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:
**
**    %fossil help                Show common commands
**    %fossil help -a|--all       Show both common and auxiliary commands
**    %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
*/
void help_cmd(void){
  int rc;
  int isPage = 0;
................................................................................
/*
** 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;
}

Changes to src/doc.c.

380
381
382
383
384
385
386
387

388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
...
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
...
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
void mimetype_list_page(void){
  int i;
  mimetype_verify();
  style_header("Mimetype List");
  @ <p>The Fossil <a href="%R/help?cmd=/doc">/doc</a> page uses filename
  @ suffixes and the following table to guess at the appropriate mimetype
  @ for each document.</p>
  @ <table id='mimeTable' border=1 cellpadding=0 class='mimetypetable'>

  @ <thead>
  @ <tr><th>Suffix<th>Mimetype
  @ </thead>
  @ <tbody>
  for(i=0; i<count(aMime); i++){
    @ <tr><td>%h(aMime[i].zSuffix)<td>%h(aMime[i].zMimetype)</tr>
  }
  @ </tbody></table>
  output_table_sorting_javascript("mimeTable","tt",1);
  style_footer();
}

/*
** Check to see if the file in the pContent blob is "embedded HTML".  Return
** true if it is, and fill pTitle with the document title.
**
................................................................................
** specifies the title of the document in that case.
**
** For fossil-doc documents and for markdown documents, text of the
** form:  "href='$ROOT/" or "action='$ROOT" has the $ROOT name expanded
** to the top-level of the repository.
*/
void doc_page(void){
  const char *zName;                /* Argument to the /doc page */
  const char *zOrigName = "?";      /* Original document name */
  const char *zMime;                /* Document MIME type */
  char *zCheckin = "tip";           /* The check-in holding the document */
  char *zPathSuffix = "";           /* Text to append to g.zPath */
  int vid = 0;                      /* Artifact of check-in */
  int rid = 0;                      /* Artifact of file */
  int i;                            /* Loop counter */
................................................................................
        zDfltTitle = zName;
      }
    }else if( fossil_strcmp(zCheckin,"ckout")==0 ){
      /* Read from the local checkout */
      char *zFullpath;
      db_must_be_within_tree();
      zFullpath = mprintf("%s/%s", g.zLocalRoot, zName);
      if( file_isfile(zFullpath)
       && blob_read_from_file(&filebody, zFullpath)>0 ){
        rid = 1;  /* Fake RID just to get the loop to end */
      }
      fossil_free(zFullpath);
    }else{
      vid = name_to_typed_rid(zCheckin, "ci");
      rid = doc_load_content(vid, zName, &filebody);
    }







|
>








|







 







|







 







|
|







380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
...
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
...
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
void mimetype_list_page(void){
  int i;
  mimetype_verify();
  style_header("Mimetype List");
  @ <p>The Fossil <a href="%R/help?cmd=/doc">/doc</a> page uses filename
  @ suffixes and the following table to guess at the appropriate mimetype
  @ for each document.</p>
  @ <table 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++){
    @ <tr><td>%h(aMime[i].zSuffix)<td>%h(aMime[i].zMimetype)</tr>
  }
  @ </tbody></table>
  style_table_sorter();
  style_footer();
}

/*
** Check to see if the file in the pContent blob is "embedded HTML".  Return
** true if it is, and fill pTitle with the document title.
**
................................................................................
** specifies the title of the document in that case.
**
** For fossil-doc documents and for markdown documents, text of the
** form:  "href='$ROOT/" or "action='$ROOT" has the $ROOT name expanded
** to the top-level of the repository.
*/
void doc_page(void){
  const char *zName = 0;            /* Argument to the /doc page */
  const char *zOrigName = "?";      /* Original document name */
  const char *zMime;                /* Document MIME type */
  char *zCheckin = "tip";           /* The check-in holding the document */
  char *zPathSuffix = "";           /* Text to append to g.zPath */
  int vid = 0;                      /* Artifact of check-in */
  int rid = 0;                      /* Artifact of file */
  int i;                            /* Loop counter */
................................................................................
        zDfltTitle = zName;
      }
    }else if( fossil_strcmp(zCheckin,"ckout")==0 ){
      /* Read from the local checkout */
      char *zFullpath;
      db_must_be_within_tree();
      zFullpath = mprintf("%s/%s", g.zLocalRoot, zName);
      if( file_isfile(zFullpath, RepoFILE)
       && blob_read_from_file(&filebody, zFullpath, RepoFILE)>0 ){
        rid = 1;  /* Fake RID just to get the loop to end */
      }
      fossil_free(zFullpath);
    }else{
      vid = name_to_typed_rid(zCheckin, "ci");
      rid = doc_load_content(vid, zName, &filebody);
    }

Changes to src/event.c.

341
342
343
344
345
346
347
348
349
350
351










352
353
354
355
356
357
358
359
360
361
362
363
364

365
366
367
368
369
370
371
372
...
405
406
407
408
409
410
411

412
413

414
415
416
417
418
419
420
421
...
440
441
442
443
444
445
446
447

448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
...
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
...
507
508
509
510
511
512
513
514


515
516
517
518
519
520
521
...
527
528
529
530
531
532
533

534

535
536

537
538
539
540
541
542
543

/*
** WEBPAGE: technoteedit
** WEBPAGE: eventedit
**
** Revise or create a technical note (formerly called an "event").
**
** Parameters:
**
**    name=ID           Hex hash ID of the tech-note. If omitted, a new
**                      tech-note is created.










*/
void eventedit_page(void){
  char *zTag;
  int rid = 0;
  Blob event;
  const char *zId;
  int n;
  const char *z;
  char *zBody = (char*)P("w");
  char *zETime = (char*)P("t");
  const char *zComment = P("c");
  const char *zTags = P("g");
  const char *zClr;

  const char *zMimetype = P("mimetype");
  int isNew = 0;

  if( zBody ){
    zBody = mprintf("%s", zBody);
  }
  login_check_credentials();
  zId = P("name");
................................................................................
  /* Figure out the color */
  if( rid ){
    zClr = db_text("", "SELECT bgcolor FROM event WHERE objid=%d", rid);
  }else{
    zClr = "";
    isNew = 1;
  }

  zClr = PD("clr",zClr);
  if( fossil_strcmp(zClr,"##")==0 ) zClr = PD("cclr","");



  /* If editing an existing event, extract the key fields to use as
  ** a starting point for the edit.
  */
  if( rid
   && (zBody==0 || zETime==0 || zComment==0 || zTags==0 || zMimetype==0)
  ){
................................................................................
      );
    }
  }
  zETime = db_text(0, "SELECT coalesce(datetime(%Q),datetime('now'))", zETime);
  if( P("submit")!=0 && (zBody!=0 && zComment!=0) ){
    login_verify_csrf_secret();
    if ( !event_commit_common(rid, zId, zBody, zETime,
                              zMimetype, zComment, zTags, zClr) ){

      style_header("Error");
      @ Internal error:  Fossil tried to make an invalid artifact for
      @ the edited technote.
      style_footer();
      return;
    }
    cgi_redirectf("technote?name=%T", zId);
  }
  if( P("cancel")!=0 ){
    cgi_redirectf("technote?name=%T", zId);
    return;
  }
  if( zBody==0 ){
    zBody = mprintf("Insert new content here...");
  }
  if( isNew ){
    style_header("New Tech-note %S", zId);
................................................................................
    style_header("Edit Tech-note %S", zId);
  }
  if( P("preview")!=0 ){
    Blob com;
    @ <p><b>Timeline comment preview:</b></p>
    @ <blockquote>
    @ <table border="0">
    if( zClr && zClr[0] ){
      @ <tr><td style="background-color: %h(zClr);">
    }else{
      @ <tr><td>
    }
    blob_zero(&com);
    blob_append(&com, zComment, -1);
    wiki_convert(&com, 0, WIKI_INLINE|WIKI_NOBADLINKS);
................................................................................
  @ <td valign="top">
  @ <textarea name="c" class="technoteedit" cols="80"
  @  rows="3" wrap="virtual">%h(zComment)</textarea>
  @ </td></tr>

  @ <tr><th align="right" valign="top">Timeline Background Color:</th>
  @ <td valign="top">
  render_color_chooser(0, zClr, 0, "clr", "cclr");


  @ </td></tr>

  @ <tr><th align="right" valign="top">Tags:</th>
  @ <td valign="top">
  @   <input type="text" name="g" size="40" value="%h(zTags)" />
  @ </td></tr>

................................................................................
  @ <tr><th align="right" valign="top">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="preview" value="Preview Your Changes" />

  @ <input type="submit" name="submit" value="Apply These Changes" />
  @ <input type="submit" name="cancel" value="Cancel" />

  @ </td></tr></table>
  @ </div></form>
  style_footer();
}

/*
** Add a new tech note to the repository.  The timestamp is







|

|

>
>
>
>
>
>
>
>
>
>








|
|
|
|
|
>
|







 







>
|
<
>
|







 







|
>






|


|







 







|







 







|
>
>







 







>
|
>
|
<
>







341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
...
416
417
418
419
420
421
422
423
424

425
426
427
428
429
430
431
432
433
...
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
...
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
...
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
...
542
543
544
545
546
547
548
549
550
551
552

553
554
555
556
557
558
559
560

/*
** WEBPAGE: technoteedit
** WEBPAGE: eventedit
**
** Revise or create a technical note (formerly called an "event").
**
** Required query parameter:
**
**    name=ID           Hex hash ID of the technote. If omitted, a new
**                      tech-note is created.
**
** POST parameters from the "Cancel", "Preview", or "Submit" buttons:
**
**    w=TEXT            Complete text of the technote.
**    t=TEXT            Time of the technote on the timeline (ISO 8601)
**    c=TEXT            Timeline comment
**    g=TEXT            Tags associated with this technote
**    mimetype=TEXT     Mimetype for w= text
**    newclr            Use a background color
**    clr=TEXT          Background color to use if newclr
*/
void eventedit_page(void){
  char *zTag;
  int rid = 0;
  Blob event;
  const char *zId;
  int n;
  const char *z;
  char *zBody = (char*)P("w");             /* Text of the technote */
  char *zETime = (char*)P("t");            /* Date this technote appears */
  const char *zComment = P("c");           /* Timeline comment */
  const char *zTags = P("g");              /* Tags added to this technote */
  const char *zClrFlag = "";               /* "checked" for bg color */
  const char *zClr;                        /* Name of the background color */
  const char *zMimetype = P("mimetype");   /* Mimetype of zBody */
  int isNew = 0;

  if( zBody ){
    zBody = mprintf("%s", zBody);
  }
  login_check_credentials();
  zId = P("name");
................................................................................
  /* Figure out the color */
  if( rid ){
    zClr = db_text("", "SELECT bgcolor FROM event WHERE objid=%d", rid);
  }else{
    zClr = "";
    isNew = 1;
  }
  if( P("newclr") ){
    zClr = PD("clr",zClr);

    if( zClr[0] ) zClrFlag = " checked";
  }

  /* If editing an existing event, extract the key fields to use as
  ** a starting point for the edit.
  */
  if( rid
   && (zBody==0 || zETime==0 || zComment==0 || zTags==0 || zMimetype==0)
  ){
................................................................................
      );
    }
  }
  zETime = db_text(0, "SELECT coalesce(datetime(%Q),datetime('now'))", zETime);
  if( P("submit")!=0 && (zBody!=0 && zComment!=0) ){
    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();
      return;
    }
    cgi_redirectf("%R/technote?name=%T", zId);
  }
  if( P("cancel")!=0 ){
    cgi_redirectf("%R/technote?name=%T", zId);
    return;
  }
  if( zBody==0 ){
    zBody = mprintf("Insert new content here...");
  }
  if( isNew ){
    style_header("New Tech-note %S", zId);
................................................................................
    style_header("Edit Tech-note %S", zId);
  }
  if( P("preview")!=0 ){
    Blob com;
    @ <p><b>Timeline comment preview:</b></p>
    @ <blockquote>
    @ <table border="0">
    if( zClrFlag[0] && zClr && zClr[0] ){
      @ <tr><td style="background-color: %h(zClr);">
    }else{
      @ <tr><td>
    }
    blob_zero(&com);
    blob_append(&com, zComment, -1);
    wiki_convert(&com, 0, WIKI_INLINE|WIKI_NOBADLINKS);
................................................................................
  @ <td valign="top">
  @ <textarea name="c" class="technoteedit" cols="80"
  @  rows="3" wrap="virtual">%h(zComment)</textarea>
  @ </td></tr>

  @ <tr><th align="right" valign="top">Timeline Background Color:</th>
  @ <td valign="top">
  @ <input type='checkbox' name='newclr'%s(zClrFlag) />
  @ Use custom color: \
  @ <input type='color' name='clr' value='%s(zClr[0]?zClr:"#c0f0ff")'>
  @ </td></tr>

  @ <tr><th align="right" valign="top">Tags:</th>
  @ <td valign="top">
  @   <input type="text" name="g" size="40" value="%h(zTags)" />
  @ </td></tr>

................................................................................
  @ <tr><th align="right" valign="top">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" />
  if( P("preview") ){
    @ <input type="submit" name="submit" value="Submit" />

  }
  @ </td></tr></table>
  @ </div></form>
  style_footer();
}

/*
** Add a new tech note to the repository.  The timestamp is

Changes to src/export.c.

33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
...
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
** struct mark_t
**   holds information for translating between git commits
**   and fossil commits.
**   -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 of artifact corresponding to rid.
*/
struct mark_t{
  char *name;
  int rid;
  char uuid[41];
};
#endif

/*
** Output a "committer" record for the given user.
** NOTE: the given user name may be an email itself.
*/
................................................................................
    }
    return create_mark(mark->rid, mark, &mid);
  }else{
    mark->name = fossil_strdup(cur_tok);
  }

  cur_tok = strtok(NULL, "\n");
  if( !cur_tok || strlen(cur_tok)!=40 ){
    free(mark->name);
    fossil_trace("Invalid SHA-1 in marks file: %s\n", cur_tok);
    return -1;
  }else{
    sqlite3_snprintf(sizeof(mark->uuid), mark->uuid, "%s", cur_tok);
  }

  /* make sure that rid corresponds to UUID */
  if( fast_uuid_to_rid(mark->uuid)!=mark->rid ){
    free(mark->name);
    fossil_trace("Non-existent SHA-1 in marks file: %s\n", mark->uuid);
    return -1;
  }

  /* insert a cross-ref into the 'xmark' table */
  insert_commit_xref(mark->rid, mark->name, mark->uuid);
  return 0;
}







|




|







 







|

|








|







33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
...
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
** struct mark_t
**   holds information for translating between git commits
**   and fossil commits.
**   -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];
};
#endif

/*
** Output a "committer" record for the given user.
** NOTE: the given user name may be an email itself.
*/
................................................................................
    }
    return create_mark(mark->rid, mark, &mid);
  }else{
    mark->name = fossil_strdup(cur_tok);
  }

  cur_tok = strtok(NULL, "\n");
  if( !cur_tok || (strlen(cur_tok)!=40 && strlen(cur_tok)!=64) ){
    free(mark->name);
    fossil_trace("Invalid SHA-1/SHA-3 in marks file: %s\n", cur_tok);
    return -1;
  }else{
    sqlite3_snprintf(sizeof(mark->uuid), mark->uuid, "%s", cur_tok);
  }

  /* make sure that rid corresponds to UUID */
  if( fast_uuid_to_rid(mark->uuid)!=mark->rid ){
    free(mark->name);
    fossil_trace("Non-existent SHA-1/SHA-3 in marks file: %s\n", mark->uuid);
    return -1;
  }

  /* insert a cross-ref into the 'xmark' table */
  insert_commit_xref(mark->rid, mark->name, mark->uuid);
  return 0;
}

Changes to src/file.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
..
40
41
42
43
44
45
46


























47
48
49
50
51
52
53
54
55
56



57
58
59
60
61
62
63
..
66
67
68
69
70
71
72



73
74
75
76
77
78
79
80
81
82




83



84
85
86
87
88








89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220


221
222
223
224
225
226
227
...
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
...
268
269
270
271
272
273
274





275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299


300
301
302
303

304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
...
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
...
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
...
501
502
503
504
505
506
507




508
509
510
511
512
513


514
515
516
517
518
519
520
...
563
564
565
566
567
568
569
570
571
572
573
574
575
576



577
578
579
580
581
582
583
...
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
...
620
621
622
623
624
625
626
627





628
629
630
631
632
633
634
635
636
637
638
639
640


641
642
643
644
645
646
647
...
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
...
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
....
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
....
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
....
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
....
1510
1511
1512
1513
1514
1515
1516



1517
1518
1519
1520
1521
1522
1523
/*
** Copyright (c) 2006 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the Simplified BSD License (also
** known as the "2-Clause License" or "FreeBSD License".)

** This program is distributed in the hope that it will be useful,
** but without any warranty; without even the implied warranty of
** merchantability or fitness for a particular purpose.
**
** Author contact information:
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*******************************************************************************
**
** File utilities.
**
** Functions named file_* are generic functions that always follow symlinks.
**
** Functions named file_wd_* are to be used for files inside working
** directories. They follow symlinks depending on 'allow-symlinks' setting.
*/
#include "config.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
................................................................................
# include <sys/utime.h>
#else
# include <sys/time.h>
#endif

#if INTERFACE



























#include <dirent.h>
#if defined(_WIN32)
# define DIR _WDIR
# define dirent _wdirent
# define opendir _wopendir
# define readdir _wreaddir
# define closedir _wclosedir
#endif /* _WIN32 */

#if defined(_WIN32) && (defined(__MSVCRT__) || defined(_MSC_VER))



struct fossilStat {
    i64 st_size;
    i64 st_mtime;
    int st_mode;
};
#endif

................................................................................
#else
# define fossil_isdirsep(a)    ((a) == '/')
#endif

#endif /* INTERFACE */

#if !defined(_WIN32) || !(defined(__MSVCRT__) || defined(_MSC_VER))



# define fossilStat stat
#endif

/*
** On Windows S_ISLNK always returns FALSE.
*/
#if !defined(S_ISLNK)
# define S_ISLNK(x) (0)
#endif
static int fileStatValid = 0;




static struct fossilStat fileStat;




/*
** Fill stat buf with information received from stat() or lstat().
** lstat() is called on Unix if isWd is TRUE and allow-symlinks setting is on.
**








*/
static int fossil_stat(
  const char *zFilename,  /* name of file or directory to inspect. */
  struct fossilStat *buf, /* pointer to buffer where info should go. */
  int isWd                /* non-zero to consider look at symlink itself. */
){
  int rc;
  void *zMbcs = fossil_utf8_to_path(zFilename, 0);
#if !defined(_WIN32)
  if( isWd && db_allow_symlinks() ){
    rc = lstat(zMbcs, buf);
  }else{
    rc = stat(zMbcs, buf);
  }
#else
  rc = win32_stat(zMbcs, buf, isWd);
#endif
  fossil_path_free(zMbcs);
  return rc;
}

/*
** Clears the fileStat variable and its associated validity flag.
*/
static void resetStat(){
  fileStatValid = 0;
  memset(&fileStat, 0, sizeof(struct fossilStat));
}

/*
** Fill in the fileStat variable for the file named zFilename.
** If zFilename==0, then use the previous value of fileStat if
** there is a previous value.
**
** If isWd is TRUE, do lstat() instead of stat() if allow-symlinks is on.
**
** Return the number of errors.  No error messages are generated.
*/
static int getStat(const char *zFilename, int isWd){
  int rc = 0;
  if( zFilename==0 ){
    if( fileStatValid==0 ) rc = 1;
  }else{
    if( fossil_stat(zFilename, &fileStat, isWd)!=0 ){
      fileStatValid = 0;
      rc = 1;
    }else{
      fileStatValid = 1;
      rc = 0;
    }
  }
  return rc;
}

/*
** Return the size of a file in bytes.  Return -1 if the file does not
** exist.  If zFilename is NULL, return the size of the most recently
** stat-ed file.
*/
i64 file_size(const char *zFilename){
  return getStat(zFilename, 0) ? -1 : fileStat.st_size;
}

/*
** Same as file_size(), but takes into account symlinks.
*/
i64 file_wd_size(const char *zFilename){
  return getStat(zFilename, 1) ? -1 : fileStat.st_size;
}

/*
** Return the modification time for a file.  Return -1 if the file
** does not exist.  If zFilename is NULL return the size of the most
** recently stat-ed file.
*/
i64 file_mtime(const char *zFilename){
  return getStat(zFilename, 0) ? -1 : fileStat.st_mtime;
}

/*
** Same as file_mtime(), but takes into account symlinks.
*/
i64 file_wd_mtime(const char *zFilename){
  return getStat(zFilename, 1) ? -1 : fileStat.st_mtime;
}

/*
** Return the mode bits for a file.  Return -1 if the file does not
** exist.  If zFilename is NULL return the size of the most recently
** stat-ed file.
*/
int file_mode(const char *zFilename){
  return getStat(zFilename, 0) ? -1 : fileStat.st_mode;
}

/*
** Same as file_mode(), but takes into account symlinks.
*/
int file_wd_mode(const char *zFilename){
  return getStat(zFilename, 1) ? -1 : fileStat.st_mode;
}

/*
** Return TRUE if the named file is an ordinary file or symlink
** and symlinks are allowed.
** Return false for directories, devices, fifos, etc.
*/
int file_wd_isfile_or_link(const char *zFilename){
  return getStat(zFilename, 1) ? 0 : S_ISREG(fileStat.st_mode) ||
                                     S_ISLNK(fileStat.st_mode);
}

/*
** Return TRUE if the named file is an ordinary file.  Return false
** for directories, devices, fifos, symlinks, etc.
*/
int file_isfile(const char *zFilename){
  return getStat(zFilename, 0) ? 0 : S_ISREG(fileStat.st_mode);
}

/*
** Same as file_isfile(), but takes into account symlinks.
*/
int file_wd_isfile(const char *zFilename){
  return getStat(zFilename, 1) ? 0 : S_ISREG(fileStat.st_mode);
}

/*
** Create symlink to file on Unix, or plain-text file with
** symlink target if "allow-symlinks" is off or we're on Windows.
**
** Arguments: target file (symlink will point to it), link file


**/
void symlink_create(const char *zTargetFile, const char *zLinkFile){
#if !defined(_WIN32)
  if( db_allow_symlinks() ){
    int i, nName;
    char *zName, zBuf[1000];

................................................................................
      zName = zBuf;
      memcpy(zName, zLinkFile, nName+1);
    }
    nName = file_simplify_name(zName, nName, 0);
    for(i=1; i<nName; i++){
      if( zName[i]=='/' ){
        zName[i] = 0;
          if( file_mkdir(zName, 1) ){
            fossil_fatal_recursive("unable to create directory %s", zName);
            return;
          }
        zName[i] = '/';
      }
    }
    if( symlink(zTargetFile, zName)!=0 ){
      fossil_fatal_recursive("unable to create symlink \"%s\"", zName);
    }
    if( zName!=zBuf ) free(zName);
................................................................................
}

/*
** Return file permissions (normal, executable, or symlink):
**   - PERM_EXE on Unix if file is executable;
**   - PERM_LNK on Unix if file is symlink and allow-symlinks option is on;
**   - PERM_REG for all other cases (regular file, directory, fifo, etc).





*/
int file_wd_perm(const char *zFilename){
#if !defined(_WIN32)
  if( !getStat(zFilename, 1) ){
     if( S_ISREG(fileStat.st_mode) && ((S_IXUSR)&fileStat.st_mode)!=0 )
      return PERM_EXE;
    else if( db_allow_symlinks() && S_ISLNK(fileStat.st_mode) )
      return PERM_LNK;
  }
#endif
  return PERM_REG;
}

/*
** Return TRUE if the named file is an executable.  Return false
** for directories, devices, fifos, symlinks, etc.
*/
int file_wd_isexe(const char *zFilename){
  return file_wd_perm(zFilename)==PERM_EXE;
}

/*
** Return TRUE if the named file is a symlink and symlinks are allowed.
** Return false for all other cases.
**


** On Windows, always return False.
*/
int file_wd_islink(const char *zFilename){
  return file_wd_perm(zFilename)==PERM_LNK;

}

/*
** 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 rc;

  if( zFilename ){
    char *zFN = mprintf("%s", zFilename);
    file_simplify_name(zFN, -1, 0);
    rc = getStat(zFN, 0);
    free(zFN);
  }else{
    rc = getStat(0, 0);
  }
  return rc ? 0 : (S_ISDIR(fileStat.st_mode) ? 1 : 2);
}

/*
** Same as file_isdir(), but takes into account symlinks.  Return 1 if
** zFilename is a directory -OR- a symlink that points to a directory.
** Return 0 if zFilename does not exist.  Return 2 if zFilename exists
** but is something other than a directory.
*/
int file_wd_isdir(const char *zFilename){
  int rc;
  char *zFN;

  zFN = mprintf("%s", zFilename);
  file_simplify_name(zFN, -1, 0);
  rc = getStat(zFN, 1);
  if( rc ){
    rc = 0; /* It does not exist at all. */
  }else if( S_ISDIR(fileStat.st_mode) ){
    rc = 1; /* It exists and is a real directory. */
  }else if( S_ISLNK(fileStat.st_mode) ){
    Blob content;
    blob_read_link(&content, zFN); /* It exists and is a link. */
    rc = file_wd_isdir(blob_str(&content)); /* Points to directory? */
    blob_reset(&content);
  }else{
    rc = 2; /* It exists and is something else. */
  }
  free(zFN);
  return rc;
}

................................................................................
** Space to hold the new filename is obtained form mprintf() and should
** be freed by the caller.
*/
char *file_newname(const char *zBase, const char *zSuffix, int relFlag){
  char *z = 0;
  int cnt = 0;
  z = mprintf("%s-%s", zBase, zSuffix);
  while( file_size(z)>=0 ){
    fossil_free(z);
    z = mprintf("%s-%s-%d", zBase, zSuffix, cnt++);
  }
  if( relFlag ){
    Blob x;
    file_relative_name(z, &x, 0);
    fossil_free(z);
................................................................................
*/
void file_copy(const char *zFrom, const char *zTo){
  FILE *in, *out;
  int got;
  char zBuf[8192];
  in = fossil_fopen(zFrom, "rb");
  if( in==0 ) fossil_fatal("cannot open \"%s\" for reading", zFrom);
  file_mkfolder(zTo, 0, 0);
  out = fossil_fopen(zTo, "wb");
  if( out==0 ) fossil_fatal("cannot open \"%s\" for writing", zTo);
  while( (got=fread(zBuf, 1, sizeof(zBuf), in))>0 ){
    fwrite(zBuf, 1, got, out);
  }
  fclose(in);
  fclose(out);
................................................................................
  }
  file_copy(g.argv[2], g.argv[3]);
}

/*
** Set or clear the execute bit on a file.  Return true if a change
** occurred and false if this routine is a no-op.




*/
int file_wd_setexe(const char *zFilename, int onoff){
  int rc = 0;
#if !defined(_WIN32)
  struct stat buf;
  if( fossil_stat(zFilename, &buf, 1)!=0 || S_ISLNK(buf.st_mode) ) return 0;


  if( onoff ){
    int targetMode = (buf.st_mode & 0444)>>2;
    if( (buf.st_mode & 0100)==0 ){
      chmod(zFilename, buf.st_mode | targetMode);
      rc = 1;
    }
  }else{
................................................................................
  if( g.argc!=4 ){
    usage("FILENAME DATE/TIME");
  }
  db_open_or_attach(":memory:", "mem");
  iMTime = db_int64(0, "SELECT strftime('%%s',%Q)", g.argv[3]);
  zFile = g.argv[2];
  file_set_mtime(zFile, iMTime);
  iMTime = file_wd_mtime(zFile);
  zDate = db_text(0, "SELECT datetime(%lld, 'unixepoch')", iMTime);
  fossil_print("Set mtime of \"%s\" to %s (%lld)\n", zFile, zDate, iMTime);
}

/*
** Delete a file.



**
** Returns zero upon success.
*/
int file_delete(const char *zFilename){
  int rc;
#ifdef _WIN32
  wchar_t *z = fossil_utf8_to_path(zFilename, 0);
................................................................................
  rc = unlink(zFilename);
#endif
  fossil_path_free(z);
  return rc;
}

/*
** Create the directory named in the argument, 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 forceFlag){
  int rc = file_wd_isdir(zName);
  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);
................................................................................
/*
** Create the tree of directories in which zFilename belongs, if that sequence
** of directories does not already exist.
**
** On success, return zero.  On error, return errorReturn if positive, otherwise
** print an error message and abort.
*/
int file_mkfolder(const char *zFilename, int forceFlag, int errorReturn){





  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 ){
    zName[nName-1] = 0;
    if( file_wd_isdir(zName)!=1 ){
      rc = file_mkfolder(zName, forceFlag, errorReturn);
      if( rc==0 ){
        if( file_mkdir(zName, forceFlag) && file_wd_isdir(zName)!=1 ){


          if( errorReturn <= 0 ){
            fossil_fatal_recursive("unable to create directory %s", zName);
          }
          rc = errorReturn;
        }
      }
    }
................................................................................
/*
** 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_wd_isdir(zName);
  if( rc==2 ) return 1; /* cannot remove normal file */
  if( rc==1 ){
#if defined(_WIN32)
    wchar_t *zMbcs = fossil_utf8_to_path(zName, 1);
    rc = _wrmdir(zMbcs);
#else
    char *zMbcs = fossil_utf8_to_path(zName, 1);
................................................................................
/*
** 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 raw,
  int slash,
  int reset
){
  char zBuf[100];

  Blob x;



  memset(zBuf, 0, sizeof(zBuf));
  blob_zero(&x);
  file_canonical_name(zPath, &x, slash);
  fossil_print("%s[%s] -> [%s]\n", raw ? "RAW " : "", zPath, blob_buffer(&x));
  blob_reset(&x);
  if( raw ){
    int rc;
    struct fossilStat testFileStat;
    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);

    sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", testFileStat.st_mtime);

    fossil_print("  stat_mtime   = %s\n", zBuf);
    fossil_print("  stat_mode    = %d\n", testFileStat.st_mode);
    memset(&testFileStat, 0, sizeof(struct fossilStat));
    rc = fossil_stat(zPath, &testFileStat, 1);
    fossil_print("  l_stat_rc    = %d\n", rc);
    sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", testFileStat.st_size);
    fossil_print("  l_stat_size  = %s\n", zBuf);

    sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", testFileStat.st_mtime);

    fossil_print("  l_stat_mtime = %s\n", zBuf);
    fossil_print("  l_stat_mode  = %d\n", testFileStat.st_mode);
  }else{
    if( reset ) resetStat();
    sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_wd_size(zPath));
    fossil_print("  file_size           = %s\n", zBuf);


    sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_wd_mtime(zPath));

    fossil_print("  file_mtime          = %s\n", zBuf);










    fossil_print("  file_mode           = %d\n", file_wd_mode(zPath));

    fossil_print("  file_isfile         = %d\n", file_wd_isfile(zPath));
    fossil_print("  file_isfile_or_link = %d\n", file_wd_isfile_or_link(zPath));
    fossil_print("  file_islink         = %d\n", file_wd_islink(zPath));
    fossil_print("  file_isexe          = %d\n", file_wd_isexe(zPath));
    fossil_print("  file_isdir          = %d\n", file_wd_isdir(zPath));
    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:
**

**     --open-config        Open the configuration database first.
**     --slash              Trailing slashes, if any, are retained.
**     --reset              Reset cached stat() info for each file.
*/
void cmd_test_file_environment(void){
  int i;
  int slashFlag = find_option("slash",0,0)!=0;
  int resetFlag = find_option("reset",0,0)!=0;

  if( find_option("open-config", 0, 0)!=0 ){
    Th_OpenConfig(1);
  }

  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());



  fossil_print("db_allow_symlinks() = %d\n", db_allow_symlinks());
  for(i=2; i<g.argc; i++){
    emitFileStat(g.argv[i], 1, slashFlag, resetFlag);
    emitFileStat(g.argv[i], 0, slashFlag, resetFlag);
  }
}

/*
** COMMAND: test-canonical-name
**
** Usage: %fossil test-canonical-name FILENAME...
................................................................................
  blob_zero(&x);
  for(i=2; i<g.argc; i++){
    char zBuf[100];
    const char *zName = g.argv[i];
    file_canonical_name(zName, &x, slashFlag);
    fossil_print("[%s] -> [%s]\n", zName, blob_buffer(&x));
    blob_reset(&x);
    sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_wd_size(zName));
    fossil_print("  file_size   = %s\n", zBuf);
    sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_wd_mtime(zName));
    fossil_print("  file_mtime  = %s\n", zBuf);
    fossil_print("  file_isfile = %d\n", file_wd_isfile(zName));
    fossil_print("  file_isfile_or_link = %d\n",file_wd_isfile_or_link(zName));
    fossil_print("  file_islink = %d\n", file_wd_islink(zName));
    fossil_print("  file_isexe  = %d\n", file_wd_isexe(zName));
    fossil_print("  file_isdir  = %d\n", file_wd_isdir(zName));
  }
}

/*
** Return TRUE if the given filename is canonical.
**
** Canonical names are full pathnames using "/" not "\" and which
................................................................................
  azDirs[2] = fossil_getenv("TMP");
#else
  azDirs[0] = fossil_getenv("TMPDIR");
#endif

  for(i=0; i<count(azDirs); i++){
    if( azDirs[i]==0 ) continue;
    if( !file_isdir(azDirs[i]) ) continue;
    zDir = azDirs[i];
    break;
  }

  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);
  }while( file_size(blob_str(pBuf))>=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 */
................................................................................
}


/*
** Return true if a file named zName exists and has identical content
** to the blob pContent.  If zName does not exist or if the content is
** different in any way, then return false.


*/
int file_is_the_same(Blob *pContent, const char *zName){
  i64 iSize;
  int rc;
  Blob onDisk;

  iSize = file_wd_size(zName);
  if( iSize<0 ) return 0;
  if( iSize!=blob_size(pContent) ) return 0;
  if( file_wd_islink(zName) ){
    blob_read_link(&onDisk, zName);
  }else{
    blob_read_from_file(&onDisk, zName);
  }
  rc = blob_compare(&onDisk, pContent);
  blob_reset(&onDisk);
  return rc==0;
}

/*
** Return the value of an environment variable as UTF8.
................................................................................
  /* fossil_free(zString); */
#endif
  return rc;
}

/*
** Like fopen() but always takes a UTF8 argument.



*/
FILE *fossil_fopen(const char *zName, const char *zMode){
#ifdef _WIN32
  wchar_t *uMode = fossil_utf8_to_unicode(zMode);
  wchar_t *uName = fossil_utf8_to_path(zName, 0);
  FILE *f = _wfopen(uName, uMode);
  fossil_path_free(uName);






|











<
<
<
<
<







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>










>
>
>







 







>
>
>









<
>
>
>
>
|
>
>
>


|
<

>
>
>
>
>
>
>
>




|




|





|






|


|
|



|
|


<
<


|


|

|
|


|











|
|
<
<
<
<
<
<
<







|
|
<
<
<
<
<
<
<







|
|



|
|
|
|
|
|
|
|
|
|
|
|
<
<






|
|



|
<
<
<
<
<
<
<
<

<
>
>







 







|
|
|
|







 







>
>
>
>
>

|

|
|

|










|
|






>
>


|
<
>







|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<





|


|

<
<
<
<
<







 







|







 







|







 







>
>
>
>

|



|
>
>







 







|






>
>
>







 







|
|




|
|







 







|
>
>
>
>
>









|
|

|
>
>







 







|







 







<



|
>

>
>
>



|

<
<
<
|
|
|
|
|
>
|
>
|
|
|
|
|
|
|
>
|
>
|
|
<
|
|
|
>
>
|
>
|
>
>
>
>
>
>
>
>
>
>
|
>
|
|
|
|
|
|
<












>
|
|
|





>



>




>
>
>


|
<







 







|
|
|
|
|
|
|
|
|







 







|













|







 







>
>






|


<
<
<
|
<







 







>
>
>







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





19
20
21
22
23
24
25
..
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
..
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108

109
110
111
112
113
114
115
116
117
118
119

120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162


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







188
189
190
191
192
193
194
195
196







197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220


221
222
223
224
225
226
227
228
229
230
231
232








233

234
235
236
237
238
239
240
241
242
...
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
...
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324

325
326
327
328
329
330
331
332
333




















334
335
336
337
338
339
340
341
342
343





344
345
346
347
348
349
350
...
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
...
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
...
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
...
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
...
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
...
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
...
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
...
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
....
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
....
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
....
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
....
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
/*
** Copyright (c) 2006 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the Simplified BSD License (also
** known as the "2-Clause License" or "FreeBSD License".)
**
** This program is distributed in the hope that it will be useful,
** but without any warranty; without even the implied warranty of
** merchantability or fitness for a particular purpose.
**
** Author contact information:
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*******************************************************************************
**
** File utilities.





*/
#include "config.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
................................................................................
# include <sys/utime.h>
#else
# include <sys/time.h>
#endif

#if INTERFACE

/* Many APIs take a 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.
**
** 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 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.
*/
#define ExtFILE    0  /* Always follow symlinks */
#define RepoFILE   1  /* Follow symlinks if and only if allow-symlinks is OFF */
#define SymFILE    2  /* Never follow symlinks */

#include <dirent.h>
#if defined(_WIN32)
# define DIR _WDIR
# define dirent _wdirent
# define opendir _wopendir
# define readdir _wreaddir
# define closedir _wclosedir
#endif /* _WIN32 */

#if defined(_WIN32) && (defined(__MSVCRT__) || defined(_MSC_VER))
/*
** File status information for windows systems.
*/
struct fossilStat {
    i64 st_size;
    i64 st_mtime;
    int st_mode;
};
#endif

................................................................................
#else
# define fossil_isdirsep(a)    ((a) == '/')
#endif

#endif /* INTERFACE */

#if !defined(_WIN32) || !(defined(__MSVCRT__) || defined(_MSC_VER))
/*
** File status information for unix systems
*/
# define fossilStat stat
#endif

/*
** On Windows S_ISLNK always returns FALSE.
*/
#if !defined(S_ISLNK)
# define S_ISLNK(x) (0)
#endif


/*
** Local state information for the file status routines
*/
static struct {
  struct fossilStat fileStat;  /* File status from last fossil_stat() */
  int fileStatValid;           /* True if fileStat is valid */
} fx;

/*
** Fill *buf with information about zFilename.

**
** If zFilename refers to a symbolic link:
**
**  (A) If allow-symlinks is on and eFType is RepoFILE, then fill
**      *buf with information about the symbolic link itself.
**
**  (B) If allow-symlinks is off or eFType is ExtFILE, then fill
**      *buf with information about the object that the symbolic link
**      points to.
*/
static int fossil_stat(
  const char *zFilename,  /* name of file or directory to inspect. */
  struct fossilStat *buf, /* pointer to buffer where info should go. */
  int eFType              /* Look at symlink itself if RepoFILE and enabled. */
){
  int rc;
  void *zMbcs = fossil_utf8_to_path(zFilename, 0);
#if !defined(_WIN32)
  if( eFType>=RepoFILE && (eFType==SymFILE || db_allow_symlinks()) ){
    rc = lstat(zMbcs, buf);
  }else{
    rc = stat(zMbcs, buf);
  }
#else
  rc = win32_stat(zMbcs, buf, eFType);
#endif
  fossil_path_free(zMbcs);
  return rc;
}

/*
** Clears the fx.fileStat variable and its associated validity flag.
*/
static void resetStat(){
  fx.fileStatValid = 0;
  memset(&fx.fileStat, 0, sizeof(struct fossilStat));
}

/*
** Fill in the fx.fileStat variable for the file named zFilename.
** If zFilename==0, then use the previous value of fx.fileStat if
** there is a previous value.
**


** Return the number of errors.  No error messages are generated.
*/
static int getStat(const char *zFilename, int eFType){
  int rc = 0;
  if( zFilename==0 ){
    if( fx.fileStatValid==0 ) rc = 1;
  }else{
    if( fossil_stat(zFilename, &fx.fileStat, eFType)!=0 ){
      fx.fileStatValid = 0;
      rc = 1;
    }else{
      fx.fileStatValid = 1;
      rc = 0;
    }
  }
  return rc;
}

/*
** Return the size of a file in bytes.  Return -1 if the file does not
** exist.  If zFilename is NULL, return the size of the most recently
** stat-ed file.
*/
i64 file_size(const char *zFilename, int eFType){
  return getStat(zFilename, eFType) ? -1 : fx.fileStat.st_size;







}

/*
** Return the modification time for a file.  Return -1 if the file
** does not exist.  If zFilename is NULL return the size of the most
** recently stat-ed file.
*/
i64 file_mtime(const char *zFilename, int eFType){
  return getStat(zFilename, eFType) ? -1 : fx.fileStat.st_mtime;







}

/*
** Return the mode bits for a file.  Return -1 if the file does not
** exist.  If zFilename is NULL return the size of the most recently
** stat-ed file.
*/
int file_mode(const char *zFilename, int eFType){
  return getStat(zFilename, eFType) ? -1 : fx.fileStat.st_mode;
}

/*
** Return TRUE if either of the following are true:
**
**   (1) zFilename is an ordinary file
**
**   (2) allow_symlinks is on and zFilename is a symbolic link to
**       a file, directory, or other object
*/
int file_isfile_or_link(const char *zFilename){
  if( getStat(zFilename, RepoFILE) ){
    return 0;  /* stat() failed.  Return false. */
  }
  return S_ISREG(fx.fileStat.st_mode) || S_ISLNK(fx.fileStat.st_mode);


}

/*
** Return TRUE if the named file is an ordinary file.  Return false
** for directories, devices, fifos, symlinks, etc.
*/
int file_isfile(const char *zFilename, int eFType){
  return getStat(zFilename, eFType) ? 0 : S_ISREG(fx.fileStat.st_mode);
}

/*
** Create a symbolic link named zLinkFile that points to zTargetFile.








**

** If allow-symlinks is off, create an ordinary file named zLinkFile
** with the name of zTargetFile as its content.
**/
void symlink_create(const char *zTargetFile, const char *zLinkFile){
#if !defined(_WIN32)
  if( db_allow_symlinks() ){
    int i, nName;
    char *zName, zBuf[1000];

................................................................................
      zName = zBuf;
      memcpy(zName, zLinkFile, nName+1);
    }
    nName = file_simplify_name(zName, nName, 0);
    for(i=1; i<nName; i++){
      if( zName[i]=='/' ){
        zName[i] = 0;
        if( file_mkdir(zName, ExtFILE, 1) ){
          fossil_fatal_recursive("unable to create directory %s", zName);
          return;
        }
        zName[i] = '/';
      }
    }
    if( symlink(zTargetFile, zName)!=0 ){
      fossil_fatal_recursive("unable to create symlink \"%s\"", zName);
    }
    if( zName!=zBuf ) free(zName);
................................................................................
}

/*
** Return file permissions (normal, executable, or symlink):
**   - PERM_EXE on Unix if file is executable;
**   - PERM_LNK on Unix if file is symlink and allow-symlinks option is on;
**   - PERM_REG for all other cases (regular file, directory, fifo, etc).
**
** If eFType is ExtFile then symbolic links are followed and so this
** routine can only return PERM_EXE and PERM_REG.
**
** On windows, this routine returns only PERM_REG.
*/
int file_perm(const char *zFilename, int eFType){
#if !defined(_WIN32)
  if( !getStat(zFilename, RepoFILE) ){
     if( S_ISREG(fx.fileStat.st_mode) && ((S_IXUSR)&fx.fileStat.st_mode)!=0 )
      return PERM_EXE;
    else if( db_allow_symlinks() && S_ISLNK(fx.fileStat.st_mode) )
      return PERM_LNK;
  }
#endif
  return PERM_REG;
}

/*
** Return TRUE if the named file is an executable.  Return false
** for directories, devices, fifos, symlinks, etc.
*/
int file_isexe(const char *zFilename, int eFType){
  return file_perm(zFilename, eFType)==PERM_EXE;
}

/*
** Return TRUE if the named file is a symlink and symlinks are allowed.
** Return false for all other cases.
**
** This routines 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;
}

/*
** Return 1 if zFilename is a directory.  Return 0 if zFilename
** does not exist.  Return 2 if zFilename exists but is something
** other than a directory.
*/
int file_isdir(const char *zFilename, int eFType){




















  int rc;
  char *zFN;

  zFN = mprintf("%s", zFilename);
  file_simplify_name(zFN, -1, 0);
  rc = getStat(zFN, eFType);
  if( rc ){
    rc = 0; /* It does not exist at all. */
  }else if( S_ISDIR(fx.fileStat.st_mode) ){
    rc = 1; /* It exists and is a real directory. */





  }else{
    rc = 2; /* It exists and is something else. */
  }
  free(zFN);
  return rc;
}

................................................................................
** Space to hold the new filename is obtained form mprintf() and should
** be freed by the caller.
*/
char *file_newname(const char *zBase, const char *zSuffix, int relFlag){
  char *z = 0;
  int cnt = 0;
  z = mprintf("%s-%s", zBase, zSuffix);
  while( file_size(z, ExtFILE)>=0 ){
    fossil_free(z);
    z = mprintf("%s-%s-%d", zBase, zSuffix, cnt++);
  }
  if( relFlag ){
    Blob x;
    file_relative_name(z, &x, 0);
    fossil_free(z);
................................................................................
*/
void file_copy(const char *zFrom, const char *zTo){
  FILE *in, *out;
  int got;
  char zBuf[8192];
  in = fossil_fopen(zFrom, "rb");
  if( in==0 ) fossil_fatal("cannot open \"%s\" for reading", zFrom);
  file_mkfolder(zTo, ExtFILE, 0, 0);
  out = fossil_fopen(zTo, "wb");
  if( out==0 ) fossil_fatal("cannot open \"%s\" for writing", zTo);
  while( (got=fread(zBuf, 1, sizeof(zBuf), in))>0 ){
    fwrite(zBuf, 1, got, out);
  }
  fclose(in);
  fclose(out);
................................................................................
  }
  file_copy(g.argv[2], g.argv[3]);
}

/*
** Set or clear the execute bit on a file.  Return true if a change
** occurred and false if this routine is a no-op.
**
** This routine assumes RepoFILE as the eFType.  In other words, if
** zFilename is a symbolic link, it is the object that zFilename points
** to that is modified.
*/
int file_setexe(const char *zFilename, int onoff){
  int rc = 0;
#if !defined(_WIN32)
  struct stat buf;
  if( fossil_stat(zFilename, &buf, RepoFILE)!=0 || S_ISLNK(buf.st_mode) ){
    return 0;
  }
  if( onoff ){
    int targetMode = (buf.st_mode & 0444)>>2;
    if( (buf.st_mode & 0100)==0 ){
      chmod(zFilename, buf.st_mode | targetMode);
      rc = 1;
    }
  }else{
................................................................................
  if( g.argc!=4 ){
    usage("FILENAME DATE/TIME");
  }
  db_open_or_attach(":memory:", "mem");
  iMTime = db_int64(0, "SELECT strftime('%%s',%Q)", g.argv[3]);
  zFile = g.argv[2];
  file_set_mtime(zFile, iMTime);
  iMTime = file_mtime(zFile, RepoFILE);
  zDate = db_text(0, "SELECT datetime(%lld, 'unixepoch')", iMTime);
  fossil_print("Set mtime of \"%s\" to %s (%lld)\n", zFile, zDate, iMTime);
}

/*
** Delete a file.
**
** If zFilename is a symbolic link, then it is the link itself that is
** removed, not the object that zFilename points to.
**
** Returns zero upon success.
*/
int file_delete(const char *zFilename){
  int rc;
#ifdef _WIN32
  wchar_t *z = fossil_utf8_to_path(zFilename, 0);
................................................................................
  rc = unlink(zFilename);
#endif
  fossil_path_free(z);
  return 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);
................................................................................
/*
** Create the tree of directories in which zFilename belongs, if that sequence
** of directories does not already exist.
**
** On success, return zero.  On error, return errorReturn if positive, otherwise
** print an error message and abort.
*/
int file_mkfolder(
  const char *zFilename,   /* Pathname showing directories to be created */
  int eFType,              /* Follow symlinks if ExtFILE */
  int forceFlag,           /* Delete non-directory objects in the way */
  int errorReturn          /* What to do when an error is seen */
){
  int nName, rc = 0;
  char *zName;

  nName = strlen(zFilename);
  zName = mprintf("%s", zFilename);
  nName = file_simplify_name(zName, nName, 0);
  while( nName>0 && zName[nName-1]!='/' ){ nName--; }
  if( nName ){
    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;
        }
      }
    }
................................................................................
/*
** 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);
    rc = _wrmdir(zMbcs);
#else
    char *zMbcs = fossil_utf8_to_path(zName, 1);
................................................................................
/*
** 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;
  int rc;
  sqlite3_int64 iMtime;
  struct fossilStat testFileStat;
  memset(zBuf, 0, sizeof(zBuf));
  blob_zero(&x);
  file_canonical_name(zPath, &x, slash);
  fossil_print("[%s] -> [%s]\n", zPath, blob_buffer(&x));
  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);
  z = db_text(0, "SELECT datetime(%lld, 'unixepoch')", testFileStat.st_mtime);
  sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld (%s)", testFileStat.st_mtime, z);
  fossil_free(z);
  fossil_print("  stat_mtime             = %s\n", zBuf);
  fossil_print("  stat_mode              = 0%o\n", testFileStat.st_mode);
  memset(&testFileStat, 0, sizeof(struct fossilStat));
  rc = fossil_stat(zPath, &testFileStat, 1);
  fossil_print("  l_stat_rc              = %d\n", rc);
  sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", testFileStat.st_size);
  fossil_print("  l_stat_size            = %s\n", zBuf);
  z = db_text(0, "SELECT datetime(%lld, 'unixepoch')", testFileStat.st_mtime);
  sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld (%s)", testFileStat.st_mtime, z);
  fossil_free(z);
  fossil_print("  l_stat_mtime           = %s\n", zBuf);
  fossil_print("  l_stat_mode            = 0%o\n", testFileStat.st_mode);

  if( reset ) resetStat();
  sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_size(zPath,ExtFILE));
  fossil_print("  file_size(ExtFILE)     = %s\n", zBuf);
  iMtime = file_mtime(zPath, ExtFILE);
  z = db_text(0, "SELECT datetime(%lld, 'unixepoch')", iMtime);
  sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld (%s)", iMtime, z);
  fossil_free(z);
  fossil_print("  file_mtime(ExtFILE)    = %s\n", zBuf);
  fossil_print("  file_mode(ExtFILE)     = 0%o\n", file_mode(zPath,ExtFILE));
  fossil_print("  file_isfile(ExtFILE)   = %d\n", file_isfile(zPath,ExtFILE));
  fossil_print("  file_isdir(ExtFILE)    = %d\n", file_isdir(zPath,ExtFILE));
  if( reset ) resetStat();
  sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_size(zPath,RepoFILE));
  fossil_print("  file_size(RepoFILE)    = %s\n", zBuf);
  iMtime = file_mtime(zPath,RepoFILE);
  z = db_text(0, "SELECT datetime(%lld, 'unixepoch')", iMtime);
  sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld (%s)", iMtime, z);
  fossil_free(z);
  fossil_print("  file_mtime(RepoFILE)   = %s\n", zBuf);
  fossil_print("  file_mode(RepoFILE)    = 0%o\n", file_mode(zPath,RepoFILE));
  fossil_print("  file_isfile(RepoFILE)  = %d\n", file_isfile(zPath,RepoFILE));
  fossil_print("  file_isfile_or_link    = %d\n", file_isfile_or_link(zPath));
  fossil_print("  file_islink            = %d\n", file_islink(zPath));
  fossil_print("  file_isexe(RepoFILE)   = %d\n", file_isexe(zPath,RepoFILE));
  fossil_print("  file_isdir(RepoFILE)   = %d\n", file_isdir(zPath,RepoFILE));
  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.
**     --slash                      Trailing slashes, if any, are retained.
**     --reset                      Reset cached stat() info for each file.
*/
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 *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);
  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);
  }
  fossil_print("db_allow_symlinks() = %d\n", db_allow_symlinks());
  for(i=2; i<g.argc; i++){
    emitFileStat(g.argv[i], slashFlag, resetFlag);

  }
}

/*
** COMMAND: test-canonical-name
**
** Usage: %fossil test-canonical-name FILENAME...
................................................................................
  blob_zero(&x);
  for(i=2; i<g.argc; i++){
    char zBuf[100];
    const char *zName = g.argv[i];
    file_canonical_name(zName, &x, slashFlag);
    fossil_print("[%s] -> [%s]\n", zName, blob_buffer(&x));
    blob_reset(&x);
    sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_size(zName,RepoFILE));
    fossil_print("  file_size           = %s\n", zBuf);
    sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_mtime(zName,RepoFILE));
    fossil_print("  file_mtime          = %s\n", zBuf);
    fossil_print("  file_isfile         = %d\n", file_isfile(zName,RepoFILE));
    fossil_print("  file_isfile_or_link = %d\n", file_isfile_or_link(zName));
    fossil_print("  file_islink         = %d\n", file_islink(zName));
    fossil_print("  file_isexe          = %d\n", file_isexe(zName,RepoFILE));
    fossil_print("  file_isdir          = %d\n", file_isdir(zName,RepoFILE));
  }
}

/*
** Return TRUE if the given filename is canonical.
**
** Canonical names are full pathnames using "/" not "\" and which
................................................................................
  azDirs[2] = fossil_getenv("TMP");
#else
  azDirs[0] = fossil_getenv("TMPDIR");
#endif

  for(i=0; i<count(azDirs); i++){
    if( azDirs[i]==0 ) continue;
    if( !file_isdir(azDirs[i], ExtFILE) ) continue;
    zDir = azDirs[i];
    break;
  }

  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);
  }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 */
................................................................................
}


/*
** Return true if a file named zName exists and has identical content
** to the blob pContent.  If zName does not exist or if the content is
** different in any way, then return false.
**
** This routine assumes RepoFILE
*/
int file_is_the_same(Blob *pContent, const char *zName){
  i64 iSize;
  int rc;
  Blob onDisk;

  iSize = file_size(zName, RepoFILE);
  if( iSize<0 ) return 0;
  if( iSize!=blob_size(pContent) ) return 0;



  blob_read_from_file(&onDisk, zName, RepoFILE);

  rc = blob_compare(&onDisk, pContent);
  blob_reset(&onDisk);
  return rc==0;
}

/*
** Return the value of an environment variable as UTF8.
................................................................................
  /* fossil_free(zString); */
#endif
  return rc;
}

/*
** 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);

Changes to src/finfo.c.

278
279
280
281
282
283
284

285
286
287
288
289
290
291
...
311
312
313
314
315
316
317




318
319
320
321
322










323
324
325
326
327
328

329
330
331
332
333
334
335
...
399
400
401
402
403
404
405




406
407
408
409
410
411
412
...
491
492
493
494
495
496
497





498
499
500
501
502
503
504
505
506
507
508
509
510















511
512
513
514
515

516
517






518
519
520
521
522
523
524
525
526

527









528













529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553

554
555
556
557
558
559
560
561

562
563
564
565
566
567
568
...
570
571
572
573
574
575
576






577
578
579
580
581
582
583
...
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600


601
602




603
604
605
606
607
608
609
...
629
630
631
632
633
634
635

636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
...
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
...
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
...
704
705
706
707
708
709
710

711
712
713

714
715
716

717
718
719
720
721
722
723
724
725
726
727
728
...
757
758
759
760
761
762
763
764
765
766
767
**
** Show the change history for a single file.
**
** Additional query parameters:
**
**    a=DATETIME Only show changes after DATETIME
**    b=DATETIME Only show changes before DATETIME

**    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
................................................................................
  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();





  login_check_credentials();
  if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
  style_header("File History");
  login_anonymous_available();










  url_initialize(&url, "finfo");
  if( brBg ) url_add_parameter(&url, "brbg", 0);
  if( uBg ) url_add_parameter(&url, "ubg", 0);
  baseCheckin = name_to_rid_www("ci");
  zPrevDate[0] = 0;
  zFilename = PD("name","");

  fnid = db_int(0, "SELECT fnid FROM filename WHERE name=%Q", zFilename);
  if( fnid==0 ){
    @ No such file: %h(zFilename)
    style_footer();
    return;
  }
  if( g.perm.Admin ){
................................................................................
    blob_append_sql(&sql, " LIMIT %d", n);
    url_add_parameter(&url, "n", P("n"));
  }
  db_prepare(&q, "%s", blob_sql_text(&sql));
  if( P("showsql")!=0 ){
    @ <p>SQL: %h(blob_str(&sql))</p>
  }




  blob_reset(&sql);
  blob_zero(&title);
  if( baseCheckin ){
    char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", baseCheckin);
    char *zLink = href("%R/info/%!S", zUuid);
    if( origCheckin ){
      blob_appendf(&title, "Changes to file ");
................................................................................
      sqlite3_snprintf(sizeof(zPrevDate), zPrevDate, "%.10s", zDate);
      @ <tr><td>
      @   <div class="divider timelineDate">%s(zPrevDate)</div>
      @ </td><td></td><td></td></tr>
    }
    memcpy(zTime, &zDate[11], 5);
    zTime[5] = 0;





    @ <tr><td class="timelineTime">
    @ %z(href("%R/timeline?c=%t",zDate))%s(zTime)</a></td>
    @ <td class="timelineGraph"><div id="m%d(gidx)" class="tl-nodemark"></div>
    @ </td>
    if( zBgClr && zBgClr[0] ){
      @ <td class="timelineTableCell" style="background-color: %h(zBgClr);">
    }else{
      @ <td class="timelineTableCell">
    }
    if( zUuid ){
      if( 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>

        }
      }






      @ %z(href("%R/artifact/%!S",zUuid))[%S(zUuid)]</a>
      if( fShowId ){
        int srcId = delta_source_rid(frid);
        if( srcId>0 ){
          @ (%d(frid)&larr;%d(srcId))
        }else{
          @ (%d(frid))
        }
      }

      @ part of check-in









    }else{













      char *zNewName;
      zNewName = db_text(0,
        "SELECT name FROM filename WHERE fnid = "
        "   (SELECT fnid FROM mlink"
        "     WHERE mid=%d"
        "       AND pfnid IN (SELECT fnid FROM filename WHERE name=%Q))",
        fmid, zFilename);
      if( zNewName ){
        @ <b>Renamed</b> to
        @ %z(href("%R/finfo?name=%t",zNewName))%h(zNewName)</a> by check-in
        fossil_free(zNewName);
      }else{
        @ <b>Deleted</b> by check-in
      }
    }
    hyperlink_to_uuid(zCkin);
    if( fShowId ){
      @ (%d(fmid))
    }
    @ %W(zCom) (user:
    hyperlink_to_user(zUser, zDate, ",");
    @ branch: %z(href("%R/timeline?t=%T&n=200",zBr))%h(zBr)</a>,
    @ size: %d(szFile))
    if( g.perm.Hyperlink && zUuid ){
      const char *z = zFilename;

      @ %z(href("%R/annotate?filename=%h&checkin=%s",z,zCkin))
      @ [annotate]</a>
      @ %z(href("%R/blame?filename=%h&checkin=%s",z,zCkin))
      @ [blame]</a>
      @ %z(href("%R/timeline?n=200&uf=%!S",zUuid))[check-ins&nbsp;using]</a>
      if( fpid>0 ){
        @ %z(href("%R/fdiff?sbs=1&v1=%!S&v2=%!S",zPUuid,zUuid))[diff]</a>
      }

    }
    if( fDebug & FINFO_DEBUG_MLINK ){
      int ii;
      char *zAncLink;
      @ <br />fid=%d(frid) pid=%d(fpid) mid=%d(fmid)
      if( nParent>0 ){
        @ parents=%d(aParent[0])
................................................................................
          @ %d(aParent[ii])
        }
      }
      zAncLink = href("%R/finfo?name=%T&ci=%!S&debug=1",zFilename,zCkin);
      @ %z(zAncLink)[ancestry]</a>
    }
    tag_private_status(frid);






    @ </td></tr>
  }
  db_finalize(&q);
  db_finalize(&qparent);
  if( pGraph ){
    graph_finish(pGraph, 1);
    if( pGraph->nErr ){
................................................................................
      graph_free(pGraph);
      pGraph = 0;
    }else{
      @ <tr class="timelineBottom"><td></td><td></td><td></td></tr>
    }
  }
  @ </table>
  timeline_output_graph_javascript(pGraph, 0, iTableId, 1);
  style_footer();
}

/*
** WEBPAGE: mlink
** URL: /mlink?name=FILENAME
** URL: /mlink?ci=NAME
**
** Show all MLINK table entries for a particular file, or for


** a particular check-in.  This screen is intended for use by developers
** in debugging Fossil.




*/
void mlink_page(void){
  const char *zFName = P("name");
  const char *zCI = P("ci");
  Stmt q;

  login_check_credentials();
................................................................................
       /* 8 */ "  (SELECT name FROM filename WHERE fnid=mlink.pfnid)"
       "  FROM mlink, event"
       " WHERE mlink.fnid=%d"
       "   AND event.objid=mlink.mid"
       " ORDER BY 1 DESC",
       fnid
    );

    @ <h1>MLINK table for file
    @ <a href='%R/finfo?name=%t(zFName)'>%h(zFName)</a></h1>
    @ <div class='brlist'>
    @ <table id='mlinktable'>
    @ <thead><tr>
    @ <th>Date</th>
    @ <th>Check-in</th>
    @ <th>Parent Check-in</th>
    @ <th>Merge?</th>
    @ <th>New</th>
    @ <th>Old</th>
    @ <th>Exe Bit?</th>
    @ <th>Prior Name</th>
    @ </tr></thead>
    @ <tbody>
    while( db_step(&q)==SQLITE_ROW ){
      const char *zDate = db_column_text(&q,0);
      const char *zCkin = db_column_text(&q,1);
      const char *zParent = db_column_text(&q,2);
      int isMerge = db_column_int(&q,3);
................................................................................
      const char *zPid = db_column_text(&q,5);
      int isExe = db_column_int(&q,7);
      const char *zPrior = db_column_text(&q,8);
      @ <tr>
      @ <td><a href='%R/timeline?c=%!S(zCkin)'>%s(zDate)</a></td>
      @ <td><a href='%R/info/%!S(zCkin)'>%S(zCkin)</a></td>
      if( zParent ){
        @ <td><a href='%R/info/%!S(zPid)'>%S(zParent)</a></td>
      }else{
        @ <td><i>(New)</i></td>
      }
      @ <td align='center'>%s(isMerge?"&#x2713;":"")</td>
      if( zFid ){
        @ <td><a href='%R/info/%!S(zFid)'>%S(zFid)</a></td>
      }else{
................................................................................
      }
      @ </tr>
    }
    db_finalize(&q);
    @ </tbody>
    @ </table>
    @ </div>
    output_table_sorting_javascript("mlinktable","tttxtttt",1);
  }else{
    int mid = name_to_rid_www("ci");
    db_prepare(&q,
       "SELECT"
       /* 0 */ "  (SELECT name FROM filename WHERE fnid=mlink.fnid),"
       /* 1 */ "  (SELECT uuid FROM blob WHERE rid=mlink.fid),"
       /* 2 */ "  pid,"
................................................................................
       /* 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);

    @ <hr />
    @ <div class='brlist'>
    @ <table id='mlinktable'>

    @ <thead><tr>
    @ <th>File</th>
    @ <th>From</th>

    @ <th>Merge?</th>
    @ <th>New</th>
    @ <th>Old</th>
    @ <th>Exe Bit?</th>
    @ <th>Prior Name</th>
    @ </tr></thead>
    @ <tbody>
    while( db_step(&q)==SQLITE_ROW ){
      const char *zName = db_column_text(&q,0);
      const char *zFid = db_column_text(&q,1);
      const char *zPid = db_column_text(&q,3);
      const char *zPrior = db_column_text(&q,4);
................................................................................
      }
      @ </tr>
    }
    db_finalize(&q);
    @ </tbody>
    @ </table>
    @ </div>
    output_table_sorting_javascript("mlinktable","ttxtttt",1);
  }
  style_footer();
}







>







 







>
>
>
>





>
>
>
>
>
>
>
>
>
>






>







 







>
>
>
>







 







>
>
>
>
>
|
|



|

|

|
|
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
<
<
<
<
>
|
|
>
>
>
>
>
>
|



|

|


>
|
>
>
>
>
>
>
>
>
>

>
>
>
>
>
>
>
>
>
>
>
>
>









|


|


<
<
<
<
<
<
<
<


>




|

|

>







 







>
>
>
>
>
>







 







|









>
>
|
|
>
>
>
>







 







>



|



|



|
|







 







|







 







<







 







>


<
>


<
>



|
|







 







<



278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
...
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
...
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
...
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551




552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609








610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
...
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
...
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
...
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
...
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
...
759
760
761
762
763
764
765

766
767
768
769
770
771
772
...
776
777
778
779
780
781
782
783
784
785

786
787
788

789
790
791
792
793
794
795
796
797
798
799
800
801
...
830
831
832
833
834
835
836

837
838
839
**
** Show the change history for a single file.
**
** 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
................................................................................
  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 */

  login_check_credentials();
  if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
  style_header("File History");
  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{
    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");
  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();
    return;
  }
  if( g.perm.Admin ){
................................................................................
    blob_append_sql(&sql, " LIMIT %d", n);
    url_add_parameter(&url, "n", P("n"));
  }
  db_prepare(&q, "%s", blob_sql_text(&sql));
  if( P("showsql")!=0 ){
    @ <p>SQL: %h(blob_str(&sql))</p>
  }
  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);
    char *zLink = href("%R/info/%!S", zUuid);
    if( origCheckin ){
      blob_appendf(&title, "Changes to file ");
................................................................................
      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>
    @ <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( (tmFlags & TIMELINE_VERBOSE)!=0 && zUuid ){
        hyperlink_to_uuid(zUuid);
        @ part of check-in \
        hyperlink_to_uuid(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>
      if( fShowId ){
        int srcId = delta_source_rid(frid);
        if( srcId>0 ){
          @ id:&nbsp;%d(frid)&larr;%d(srcId)
        }else{
          @ id:&nbsp;%d(frid)
        }
      }
    }
    @ check-in:&nbsp;\
    hyperlink_to_uuid(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>,
    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;
      @ <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>
      if( fpid>0 ){
        @ %z(href("%R/fdiff?v1=%!S&v2=%!S",zPUuid,zUuid))[diff]</a>
      }
      @ </span></span>
    }
    if( fDebug & FINFO_DEBUG_MLINK ){
      int ii;
      char *zAncLink;
      @ <br />fid=%d(frid) pid=%d(fpid) mid=%d(fmid)
      if( nParent>0 ){
        @ parents=%d(aParent[0])
................................................................................
          @ %d(aParent[ii])
        }
      }
      zAncLink = href("%R/finfo?name=%T&ci=%!S&debug=1",zFilename,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);
    if( pGraph->nErr ){
................................................................................
      graph_free(pGraph);
      pGraph = 0;
    }else{
      @ <tr class="timelineBottom"><td></td><td></td><td></td></tr>
    }
  }
  @ </table>
  timeline_output_graph_javascript(pGraph, TIMELINE_FILEDIFF, iTableId);
  style_footer();
}

/*
** WEBPAGE: mlink
** URL: /mlink?name=FILENAME
** URL: /mlink?ci=NAME
**
** Show all MLINK table entries for a particular file, or for
** a particular check-in.
**
** This screen is intended for use by Fossil developers to help
** in debugging Fossil itself.  Ordinary Fossil users are not
** expected to know what the MLINK table is or why it is important.
**
** To avoid confusing ordinary users, this page is only available
** to administrators.
*/
void mlink_page(void){
  const char *zFName = P("name");
  const char *zCI = P("ci");
  Stmt q;

  login_check_credentials();
................................................................................
       /* 8 */ "  (SELECT name FROM filename WHERE fnid=mlink.pfnid)"
       "  FROM mlink, event"
       " WHERE mlink.fnid=%d"
       "   AND event.objid=mlink.mid"
       " ORDER BY 1 DESC",
       fnid
    );
    style_table_sorter();
    @ <h1>MLINK table for file
    @ <a href='%R/finfo?name=%t(zFName)'>%h(zFName)</a></h1>
    @ <div class='brlist'>
    @ <table class='sortable' data-column-types='tttxtttt' data-init-sort='1'>
    @ <thead><tr>
    @ <th>Date</th>
    @ <th>Check-in</th>
    @ <th>Parent<br>Check-in</th>
    @ <th>Merge?</th>
    @ <th>New</th>
    @ <th>Old</th>
    @ <th>Exe<br>Bit?</th>
    @ <th>Prior<br>Name</th>
    @ </tr></thead>
    @ <tbody>
    while( db_step(&q)==SQLITE_ROW ){
      const char *zDate = db_column_text(&q,0);
      const char *zCkin = db_column_text(&q,1);
      const char *zParent = db_column_text(&q,2);
      int isMerge = db_column_int(&q,3);
................................................................................
      const char *zPid = db_column_text(&q,5);
      int isExe = db_column_int(&q,7);
      const char *zPrior = db_column_text(&q,8);
      @ <tr>
      @ <td><a href='%R/timeline?c=%!S(zCkin)'>%s(zDate)</a></td>
      @ <td><a href='%R/info/%!S(zCkin)'>%S(zCkin)</a></td>
      if( zParent ){
        @ <td><a href='%R/info/%!S(zParent)'>%S(zParent)</a></td>
      }else{
        @ <td><i>(New)</i></td>
      }
      @ <td align='center'>%s(isMerge?"&#x2713;":"")</td>
      if( zFid ){
        @ <td><a href='%R/info/%!S(zFid)'>%S(zFid)</a></td>
      }else{
................................................................................
      }
      @ </tr>
    }
    db_finalize(&q);
    @ </tbody>
    @ </table>
    @ </div>

  }else{
    int mid = name_to_rid_www("ci");
    db_prepare(&q,
       "SELECT"
       /* 0 */ "  (SELECT name FROM filename WHERE fnid=mlink.fnid),"
       /* 1 */ "  (SELECT uuid FROM blob WHERE rid=mlink.fid),"
       /* 2 */ "  pid,"
................................................................................
       /* 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);
    style_table_sorter();
    @ <hr />
    @ <div class='brlist'>

    @ <table class='sortable' data-column-types='ttxtttt' data-init-sort='1'>
    @ <thead><tr>
    @ <th>File</th>

    @ <th>Parent<br>Check-in</th>
    @ <th>Merge?</th>
    @ <th>New</th>
    @ <th>Old</th>
    @ <th>Exe<br>Bit?</th>
    @ <th>Prior<br>Name</th>
    @ </tr></thead>
    @ <tbody>
    while( db_step(&q)==SQLITE_ROW ){
      const char *zName = db_column_text(&q,0);
      const char *zFid = db_column_text(&q,1);
      const char *zPid = db_column_text(&q,3);
      const char *zPrior = db_column_text(&q,4);
................................................................................
      }
      @ </tr>
    }
    db_finalize(&q);
    @ </tbody>
    @ </table>
    @ </div>

  }
  style_footer();
}

Changes to src/fusefs.c.

321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
  int doDebug = find_option("debug","d",0)!=0;

  db_find_and_open_repository(0,0);
  verify_all_options();
  blob_init(&fusefs.content, 0, 0);
  if( g.argc!=3 ) usage("DIRECTORY");
  zMountPoint = g.argv[2];
  if( file_mkdir(zMountPoint, 0) ){
    fossil_fatal("cannot make directory [%s]", zMountPoint);
  }
  azNewArgv[0] = g.argv[0];
  azNewArgv[1] = doDebug ? "-d" : "-f";
  azNewArgv[2] = "-s";
  azNewArgv[3] = zMountPoint;
  azNewArgv[4] = 0;







|







321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
  int doDebug = find_option("debug","d",0)!=0;

  db_find_and_open_repository(0,0);
  verify_all_options();
  blob_init(&fusefs.content, 0, 0);
  if( g.argc!=3 ) usage("DIRECTORY");
  zMountPoint = g.argv[2];
  if( file_mkdir(zMountPoint, ExtFILE, 0) ){
    fossil_fatal("cannot make directory [%s]", zMountPoint);
  }
  azNewArgv[0] = g.argv[0];
  azNewArgv[1] = doDebug ? "-d" : "-f";
  azNewArgv[2] = "-s";
  azNewArgv[3] = zMountPoint;
  azNewArgv[4] = 0;

Changes to src/graph.c.

25
26
27
28
29
30
31



32
33
34
35
36
37
38
39
40
41
42
...
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
...
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
...
453
454
455
456
457
458
459

460
461
462
463
464
465
466
...
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504

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



*/
struct GraphRow {
  int rid;                    /* The rid for the check-in */
  i8 nParent;                 /* Number of parents */
  int *aParent;               /* Array of parents.  0 element is primary .*/
  char *zBranch;              /* Branch name */
  char *zBgClr;               /* Background Color */
  char zUuid[41];             /* Check-in for file ID */

  GraphRow *pNext;            /* Next row down in the list of all rows */
  GraphRow *pPrev;            /* Previous row */
................................................................................
){
  GraphRow *pRow;
  int nByte;
  static int nRow = 0;

  if( p->nErr ) return 0;
  nByte = sizeof(GraphRow);
  nByte += sizeof(pRow->aParent[0])*nParent;
  pRow = (GraphRow*)safeMalloc( nByte );
  pRow->aParent = (int*)&pRow[1];
  pRow->rid = rid;
  pRow->nParent = nParent;
  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);
  memcpy(pRow->aParent, aParent, sizeof(aParent[0])*nParent);
  if( p->pFirst==0 ){
    p->pFirst = pRow;
  }else{
    p->pLast->pNext = pRow;
  }
  p->pLast = pRow;
  p->nRow++;
................................................................................
  ** pChild.
  **
  ** In the case of a fork, choose the pChild that results in the
  ** longest rail.
  */
  for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
    if( pRow->isDup ) continue;
    if( pRow->nParent==0 ) continue;                   /* Root node */
    pParent = hashFind(p, pRow->aParent[0]);
    if( pParent==0 ) continue;                         /* Parent off-screen */
    if( pParent->zBranch!=pRow->zBranch ) continue;    /* Different branch */
    if( pParent->idx <= pRow->idx ){
       pParent->timeWarp = 1;
       continue;                                       /* Time-warp */
    }
................................................................................
  **
  ** 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( pRow->isDup ) 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 ){
................................................................................
      if( pRow->pChild==0 && !pRow->timeWarp ){
        if( !omitDescenders && count_nonbranch_children(pRow->rid)!=0 ){
          riser_to_top(pRow);
        }
      }
      continue;
    }
    if( pRow->isDup ){
      continue;
    }else{
      assert( pRow->nParent>0 );
      parentRid = pRow->aParent[0];
      pParent = hashFind(p, parentRid);
      if( pParent==0 ){
        pRow->iRail = ++p->mxRail;







>
>
>



|







 







|

|









|







 







|







 







>







 







|







25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
...
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
...
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
...
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
...
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508

#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 .*/
  char *zBranch;              /* Branch name */
  char *zBgClr;               /* Background Color */
  char zUuid[41];             /* Check-in for file ID */

  GraphRow *pNext;            /* Next row down in the list of all rows */
  GraphRow *pPrev;            /* Previous row */
................................................................................
){
  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->rid = rid;
  pRow->nParent = nParent;
  pRow->zBranch = persistBranchName(p, zBranch);
  if( zUuid==0 ) zUuid = "";
  sqlite3_snprintf(sizeof(pRow->zUuid), pRow->zUuid, "%s", zUuid);
  pRow->isLeaf = isLeaf;
  memset(pRow->aiRiser, -1, sizeof(pRow->aiRiser));
  if( zBgClr==0 ) zBgClr = "";
  pRow->zBgClr = persistBranchName(p, zBgClr);
  if( nParent>0 ) memcpy(pRow->aParent, aParent, sizeof(aParent[0])*nParent);
  if( p->pFirst==0 ){
    p->pFirst = pRow;
  }else{
    p->pLast->pNext = pRow;
  }
  p->pLast = pRow;
  p->nRow++;
................................................................................
  ** pChild.
  **
  ** In the case of a fork, choose the pChild that results in the
  ** longest rail.
  */
  for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
    if( pRow->isDup ) continue;
    if( pRow->nParent<=0 ) continue;                   /* Root node */
    pParent = hashFind(p, pRow->aParent[0]);
    if( pParent==0 ) continue;                         /* Parent off-screen */
    if( pParent->zBranch!=pRow->zBranch ) continue;    /* Different branch */
    if( pParent->idx <= pRow->idx ){
       pParent->timeWarp = 1;
       continue;                                       /* Time-warp */
    }
................................................................................
  **
  ** 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( 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 ){
................................................................................
      if( pRow->pChild==0 && !pRow->timeWarp ){
        if( !omitDescenders && count_nonbranch_children(pRow->rid)!=0 ){
          riser_to_top(pRow);
        }
      }
      continue;
    }
    if( pRow->isDup || pRow->nParent<0 ){
      continue;
    }else{
      assert( pRow->nParent>0 );
      parentRid = pRow->aParent[0];
      pParent = hashFind(p, parentRid);
      if( pParent==0 ){
        pRow->iRail = ++p->mxRail;

Added src/graph.js.























































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
/* This module contains javascript needed to render timeline graphs in Fossil.
**
** 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
**     "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.
**   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.
**   mu:  The id of the row which is the top of the merge-out arrow.
**    u:  Draw a thick child-line out of the top of this node and up to
**        the node with an id equal to this value.  0 if it is straight to
**        the top of the page, -1 if there is no thick-line riser.
**    f:  0x01: a leaf node.
**   au:  An array of integers that define thick-line risers for branches.
**        The integers are in pairs.  For each pair, the first integer is
**        is the rail on which the riser should run and the second integer
**        is the id of the node upto which the riser should run.
**   mi:  "merge-in".  An array of integer 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.
**    h:  The artifact hash of the object being graphed
*/
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;
}

function TimelineGraph(tx){
  var topObj = document.getElementById("timelineTable"+tx.iTableId);
  amendCss(tx.circleNodes, tx.showArrowheads);
  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"
    ];
    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;
    wArrow = elems.arrow_warp;
    wLine = elems.line_warp;
  
    var minRailPitch = Math.ceil((node.w+line.w)/2 + mArrow.w + 1);
    if( tx.iRailPitch>0 ){
      railPitch = tx.iRailPitch;
    }else{
      railPitch = elems.rail.w;
      railPitch -= Math.floor((tx.nrail-1)*(railPitch-minRailPitch)/21);
    }
    railPitch = Math.max(railPitch, minRailPitch);
  
    if( tx.nomo ){
      mergeOffset = 0;
    }else{
      mergeOffset = railPitch-minRailPitch-mLine.w;
      mergeOffset = Math.min(mergeOffset, elems.mergeoffset.w);
      mergeOffset = mergeOffset>0 ? mergeOffset + line.w/2 : 0;
    }
  
    var canvasWidth = (tx.nrail-1)*railPitch + node.w;
    canvasDiv.style.width = canvasWidth + "px";
    canvasDiv.style.position = "relative";
  }
  function drawBox(cls,color,x0,y0,x1,y1){
    var n = document.createElement("div");
    x0 = Math.floor(x0);
    y0 = Math.floor(y0);
    x1 = x1 || x1===0 ? Math.floor(x1) : x0;
    y1 = y1 || y1===0 ? Math.floor(y1) : y0;
    if( x0>x1 ){ var t=x0; x0=x1; x1=t; }
    if( y0>y1 ){ var t=y0; y0=y1; y1=t; }
    var w = x1-x0;
    var h = y1-y0;
    n.style.position = "absolute";
    n.style.left = x0+"px";
    n.style.top = y0+"px";
    if( w ) n.style.width = w+"px";
    if( h ) n.style.height = h+"px";
    if( color ) n.style.backgroundColor = color;
    n.className = "tl-"+cls;
    canvasDiv.appendChild(n);
    return n;
  }
  function absoluteY(obj){
    var top = 0;
    if( obj.offsetParent ){
      do{
        top += obj.offsetTop;
      }while( obj = obj.offsetParent );
    }
    return top;
  }
  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);
  }
  function drawUpArrow(from,to,color){
    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);
    x = to.x + (node.w-arw.w)/2;
    var n = drawBox(arw.cls,null,x,y);
    if(color) n.style.borderBottomColor = color;
  }
  function drawMergeLine(x0,y0,x1,y1){
    drawLine(mLine,null,x0,y0,x1,y1);
  }
  function drawMergeArrow(p,rail){
    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 = "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);
    var cls = node.cls;
    if( p.mi.length ) cls += " merge";
    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.style.zIndex = 10;
    if( !tx.omitDescenders ){
      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 ){
      var x0 = p.x + node.w/2;
      var x1 = p.mo*railPitch + node.w/2;
      var u = tx.rowinfo[p.mu-tx.iTopRow];
      var y1 = miLineY(u);
      if( p.u<0 || p.mo!=p.r ){
        x1 += mergeLines[p.mo] = -mLine.w/2;
        var y0 = p.y+2;
        if( p.r!=p.mo ) drawMergeLine(x0,y0,x1+(x0<x1 ? mLine.w : 0),null);
        drawMergeLine(x1,y0+mLine.w,null,y1);
      }else if( mergeOffset ){
        mergeLines[p.mo] = u.r<p.r ? -mergeOffset-mLine.w : mergeOffset;
        x1 += mergeLines[p.mo];
        drawMergeLine(x1,p.y+node.h/2,null,y1);
      }else{
        delete mergeLines[p.mo];
      }
    }
    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);
    }
  }
  var mergeLines;
  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");
    if( tlBtm.offsetHeight<node.h ){
      tlBtm.style.height = node.h + "px";
    }
    var btm = absoluteY(tlBtm) - canvasY + tlBtm.offsetHeight;
    for( var i=tx.rowinfo.length-1; i>=0; i-- ){
      drawNode(tx.rowinfo[i], btm);
    }
  }
  var selRow;
  function clickOnNode(){
    var p = tx.rowinfo[parseInt(this.id.match(/\d+$/)[0], 10)-tx.iTopRow];
    if( !selRow ){
      selRow = p;
      this.className += " sel";
      canvasDiv.className += " sel";
    }else if( selRow==p ){
      selRow = null;
      this.className = this.className.replace(" sel", "");
      canvasDiv.className = canvasDiv.className.replace(" sel", "");
    }else{
      if( tx.fileDiff ){
        location.href=tx.baseUrl + "/fdiff?v1="+selRow.h+"&v2="+p.h
      }else{
        location.href=tx.baseUrl + "/vdiff?from="+selRow.h+"&to="+p.h
      }
    }
  }
  function changeDisplay(selector,value){
    var x = document.getElementsByClassName(selector);
    var n = x.length;
    for(var i=0; i<n; i++) {x[i].style.display = value;}
  }
  function changeDisplayById(id,value){
    var x = document.getElementById(id);
    if(x) x.style.display=value;
  }
  function toggleDetail(){
    var id = parseInt(this.getAttribute('data-id'))
    var x = document.getElementById("detail-"+id);
    if( x.style.display=="inline" ){
      x.style.display="none";
      changeDisplayById("ellipsis-"+id,"inline");
      changeDisplayById("links-"+id,"none");
    }else{
      x.style.display="inline";
      changeDisplayById("ellipsis-"+id,"none");
      changeDisplayById("links-"+id,"inline");
    }
    checkHeight();
  }
  function scrollToSelected(){
    var x = document.getElementsByClassName('timelineSelected');
    if(x[0]){
      var h = window.innerHeight;
      var y = absoluteY(x[0]) - h/2;
      if( y>0 ) window.scrollTo(0, y);
    }
  }
  if( tx.rowinfo ){
    var lastRow = 
       document.getElementById("m"+tx.rowinfo[tx.rowinfo.length-1].id);
    var lastY = 0;
    function checkHeight(){
      var h = absoluteY(lastRow);
      if( h!=lastY ){
        renderGraph();
        lastY = h;
      }
      setTimeout(checkHeight, 1000);
    }
    initGraph();
    checkHeight();
  }else{
    function checkHeight(){}
  }
  if( tx.scrollToSelect ){
    scrollToSelected();
  }

  /* Set the onclick= attributes for elements of the "Compact" display
  ** mode so that clicking turns the details on and off.
  */
  var lx = topObj.getElementsByClassName('timelineEllipsis');
  var i;
  for(i=0; i<lx.length; i++){
    if( lx[i].hasAttribute('data-id') ) lx[i].onclick = toggleDetail;
  }
  lx = topObj.getElementsByClassName('timelineCompactComment');
  for(i=0; i<lx.length; i++){
    if( lx[i].hasAttribute('data-id') ) lx[i].onclick = toggleDetail;
  }
}
  
/* 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.

130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
*/
void test_gzip_cmd(void){
  Blob b;
  char *zOut;
  if( g.argc!=3 ) usage("FILENAME");
  sqlite3_open(":memory:", &g.db);
  gzip_begin(-1);
  blob_read_from_file(&b, g.argv[2]);
  zOut = mprintf("%s.gz", g.argv[2]);
  gzip_step(blob_buffer(&b), blob_size(&b));
  blob_reset(&b);
  gzip_finish(&b);
  blob_write_to_file(&b, zOut);
  blob_reset(&b);
  fossil_free(zOut);
}







|








130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
*/
void test_gzip_cmd(void){
  Blob b;
  char *zOut;
  if( g.argc!=3 ) usage("FILENAME");
  sqlite3_open(":memory:", &g.db);
  gzip_begin(-1);
  blob_read_from_file(&b, g.argv[2], ExtFILE);
  zOut = mprintf("%s.gz", g.argv[2]);
  gzip_step(blob_buffer(&b), blob_size(&b));
  blob_reset(&b);
  gzip_finish(&b);
  blob_write_to_file(&b, zOut);
  blob_reset(&b);
  fossil_free(zOut);
}

Changes to src/hname.c.

131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
** if the hash does not match.
*/
int hname_verify_file_hash(const char *zFile, const char *zHash, int nHash){
  int id = HNAME_ERROR;
  switch( nHash ){
    case HNAME_LEN_SHA1: {
      Blob hash;
      sha1sum_file(zFile, &hash);
      if( memcmp(blob_buffer(&hash),zHash,HNAME_LEN_SHA1)==0 ) id = HNAME_SHA1;
      blob_reset(&hash);
      break;
    }
    case HNAME_LEN_K256: {
      Blob hash;
      sha3sum_file(zFile, 256, &hash);
      if( memcmp(blob_buffer(&hash),zHash,64)==0 ) id = HNAME_LEN_K256;
      blob_reset(&hash);
      break;
    }
  }
  return id;
}







|






|







131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
** if the hash does not match.
*/
int hname_verify_file_hash(const char *zFile, const char *zHash, int nHash){
  int id = HNAME_ERROR;
  switch( nHash ){
    case HNAME_LEN_SHA1: {
      Blob hash;
      if( sha1sum_file(zFile, RepoFILE, &hash) ) break;
      if( memcmp(blob_buffer(&hash),zHash,HNAME_LEN_SHA1)==0 ) id = HNAME_SHA1;
      blob_reset(&hash);
      break;
    }
    case HNAME_LEN_K256: {
      Blob hash;
      if( sha3sum_file(zFile, RepoFILE, 256, &hash) ) break;
      if( memcmp(blob_buffer(&hash),zHash,64)==0 ) id = HNAME_LEN_K256;
      blob_reset(&hash);
      break;
    }
  }
  return id;
}

Added src/href.js.





























































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/* As an anti-robot defense, <a> elements are initially coded with the
** href= set to the honeypot, and <form> elements are initialized with
** action= set to the login page.  The real values for href= and action=
** are held in data-href= and data-action=.  The following code moves
** data-href= into href= and data-action= into action= for all
** <a> and <form> elements, after delay and maybe also after mouse
** movement is seen.
**
** Before sourcing this script, create a separate <script> element
** (with type='application/json' to avoid Content Security Policy issues)
** containing:
**
**     {"delay":MILLISECONDS, "mouseover":BOOLEAN}
**
** The <script> must have an id='href-data'.  DELAY is the number 
** milliseconds delay prior to populating href= and action=.  If the
** mouseover boolean is true, then the timer does not start until a
** mouse motion event occurs over top of the document.
*/
function setAllHrefs(){
  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(){
      setTimeout(setAllHrefs, g.delay);
    }
  }else{
    setTimeout(setAllHrefs, g.delay);
  }
}
antiRobotDefense()

Changes to src/http_socket.c.

117
118
119
120
121
122
123

124
125
126
127
128
129
130
/*
** Close the currently open socket.  If no socket is open, this routine
** is a no-op.
*/
void socket_close(void){
  if( iSocket>=0 ){
#if defined(_WIN32)

    closesocket(iSocket);
#else
    close(iSocket);
#endif
    iSocket = -1;
  }
}







>







117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/*
** Close the currently open socket.  If no socket is open, this routine
** is a no-op.
*/
void socket_close(void){
  if( iSocket>=0 ){
#if defined(_WIN32)
    if( shutdown(iSocket,1)==0 ) shutdown(iSocket,0);
    closesocket(iSocket);
#else
    close(iSocket);
#endif
    iSocket = -1;
  }
}

Changes to src/http_ssl.c.

107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
    /* Set up acceptable CA root certificates */
    zCaSetting = db_get("ssl-ca-location", 0);
    if( zCaSetting==0 || zCaSetting[0]=='\0' ){
      /* CA location not specified, use platform's default certificate store */
      X509_STORE_set_default_paths(SSL_CTX_get_cert_store(sslCtx));
    }else{
      /* User has specified a CA location, make sure it exists and use it */
      switch( file_isdir(zCaSetting) ){
        case 0: { /* doesn't exist */
          fossil_fatal("ssl-ca-location is set to '%s', "
              "but is not a file or directory", zCaSetting);
          break;
        }
        case 1: { /* directory */
          zCaDirectory = zCaSetting;







|







107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
    /* Set up acceptable CA root certificates */
    zCaSetting = db_get("ssl-ca-location", 0);
    if( zCaSetting==0 || zCaSetting[0]=='\0' ){
      /* CA location not specified, use platform's default certificate store */
      X509_STORE_set_default_paths(SSL_CTX_get_cert_store(sslCtx));
    }else{
      /* User has specified a CA location, make sure it exists and use it */
      switch( file_isdir(zCaSetting, ExtFILE) ){
        case 0: { /* doesn't exist */
          fossil_fatal("ssl-ca-location is set to '%s', "
              "but is not a file or directory", zCaSetting);
          break;
        }
        case 1: { /* directory */
          zCaDirectory = zCaSetting;

Changes to src/info.c.

199
200
201
202
203
204
205

206


207
208
209
210
211
212
213
...
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
...
332
333
334
335
336
337
338
339

340
341
342
343
344
345
346
...
362
363
364
365
366
367
368
369

370
371
372
373
374
375
376
...
394
395
396
397
398
399
400
401

402
403
404
405
406
407
408
...
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
...
578
579
580
581
582
583
584
585
586



















































































































587
588
589
590
591
592
593
594
595
596
597
598
599
600



601
602
603
604
605
606
607
608
609
610
611
612
613
614
...
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645


646
647
648
649
650

651
652
653
654
655
656
657
658
659
660
661
662
663
664

665
666
667

668
669



























































670
671
672
673
674
675

676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696







697
698
699
700
701
702
703
...
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768




769
770
771
772
773
774
775
776
777
778
779
780
781
782
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
...
846
847
848
849
850
851
852
853

854
855
856
857
858
859
860
....
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
....
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
....
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
....
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
....
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352

1353
1354
1355
1356
1357
1358
1359
....
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
....
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621






1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
....
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
....
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
....
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
....
2204
2205
2206
2207
2208
2209
2210

2211
2212
2213
2214
2215
2216
2217
2218
....
2234
2235
2236
2237
2238
2239
2240
2241

2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
....
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
....
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
....
2706
2707
2708
2709
2710
2711
2712

2713
2714
2715

2716
2717

2718
2719
2720
2721
2722
2723
2724
....
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
....
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
....
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
....
2925
2926
2927
2928
2929
2930
2931

2932

2933
2934

2935
2936
2937

2938
2939
2940
2941
2942
2943
2944
....
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
void info_cmd(void){
  i64 fsize;
  int verboseFlag = find_option("verbose","v",0)!=0;
  if( !verboseFlag ){
    verboseFlag = find_option("detail","l",0)!=0; /* deprecated */
  }


  if( g.argc==3 && (fsize = file_size(g.argv[2]))>0 && (fsize&0x1ff)==0 ){


    db_open_config(0, 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();
................................................................................
    if( rid==0 ){
      fossil_fatal("no such object: %s", g.argv[2]);
    }
    show_common_info(rid, "uuid:", 1, 1);
  }
}

/*
** Show information about all tags on a given check-in.
*/
static void showTags(int rid){
  Stmt q;
  int cnt = 0;
  db_prepare(&q,
    "SELECT tag.tagid, tagname, "
    "       (SELECT uuid FROM blob WHERE rid=tagxref.srcid AND rid!=%d),"
    "       value, datetime(tagxref.mtime,toLocal()), tagtype,"
    "       (SELECT uuid FROM blob WHERE rid=tagxref.origid AND rid!=%d)"
    "  FROM tagxref JOIN tag ON tagxref.tagid=tag.tagid"
    " WHERE tagxref.rid=%d"
    " ORDER BY tagname /*sort*/", rid, rid, rid
  );
  while( db_step(&q)==SQLITE_ROW ){
    const char *zTagname = db_column_text(&q, 1);
    const char *zSrcUuid = db_column_text(&q, 2);
    const char *zValue = db_column_text(&q, 3);
    const char *zDate = db_column_text(&q, 4);
    int tagtype = db_column_int(&q, 5);
    const char *zOrigUuid = db_column_text(&q, 6);
    cnt++;
    if( cnt==1 ){
      @ <div class="section">Tags And Properties</div>
      @ <ul>
    }
    @ <li>
    if( tagtype==0 ){
      @ <span class="infoTagCancelled">%h(zTagname)</span> cancelled
    }else if( zValue ){
      @ <span class="infoTag">%h(zTagname)=%h(zValue)</span>
    }else {
      @ <span class="infoTag">%h(zTagname)</span>
    }
    if( tagtype==2 ){
      if( zOrigUuid && zOrigUuid[0] ){
        @ inherited from
        hyperlink_to_uuid(zOrigUuid);
      }else{
        @ propagates to descendants
      }
#if 0
      if( zValue && fossil_strcmp(zTagname,"branch")==0 ){
        @ &nbsp;&nbsp;
        @ %z(href("%R/timeline?r=%T",zValue))branch timeline</a>
      }
#endif
    }
    if( zSrcUuid && zSrcUuid[0] ){
      if( tagtype==0 ){
        @ by
      }else{
        @ added by
      }
      hyperlink_to_uuid(zSrcUuid);
      @ on
      hyperlink_to_date(zDate,0);
    }
    @ </li>
  }
  db_finalize(&q);
  if( cnt ){
    @ </ul>
  }
}

/*
** Show the context graph (immediate parents and children) for
** check-in rid.
*/
void render_checkin_context(int rid, int parentsOnly){
  Blob sql;
  Stmt q;
................................................................................
  if( !parentsOnly ){
    db_multi_exec(
      "INSERT OR IGNORE INTO ok SELECT cid FROM plink WHERE pid=%d;", rid
    );
  }
  blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
  db_prepare(&q, "%s", blob_sql_text(&sql));
  www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH, 0, 0, rid, 0);

  db_finalize(&q);
}

/*
** 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
................................................................................
  );
  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, 0, 0, 0, 0);

  db_finalize(&q);
}

/*
** WEBPAGE: test-backlinks
**
** Show a timeline of all check-ins and other events that have entries
................................................................................
     " 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, 0, 0, 0, 0);

  db_finalize(&q);
  style_footer();
}


/*
** Append the difference between artifacts to the output
................................................................................
    }
    if( diffFlags ){
      append_diff(zOld, zNew, diffFlags, pRe);
    }
  }else{
    if( zOld && zNew ){
      if( fossil_strcmp(zOld, zNew)!=0 ){
        @ Modified %z(href("%R/finfo?name=%T",zName))%h(zName)</a>
        @ from %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a>
        @ to %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>.
      }else if( zOldName!=0 && fossil_strcmp(zName,zOldName)!=0 ){
        @ Name change
        @ from %z(href("%R/finfo?name=%T",zOldName))%h(zOldName)</a>
        @ to %z(href("%R/finfo?name=%T",zName))%h(zName)</a>.
      }else{
        @ %z(href("%R/finfo?name=%T",zName))%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",zName))%h(zName)</a>
      @ version %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a>.
    }else{
      @ Added %z(href("%R/finfo?name=%T",zName))%h(zName)</a>
      @ version %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>.
    }
    if( diffFlags ){
      append_diff(zOld, zNew, diffFlags, pRe);
    }else if( zOld && zNew && fossil_strcmp(zOld,zNew)!=0 ){
      @ &nbsp;&nbsp;
      @ %z(href("%R/fdiff?v1=%!S&v2=%!S&sbs=1",zOld,zNew))[diff]</a>
    }
  }
  @ </p>
}

/*
** Generate javascript to enhance HTML diffs.
*/
void append_diff_javascript(int sideBySide){
  if( !sideBySide ) return;
  @ <script>(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]);
  @ }
  @ }())</script>
}

/*
** Construct an appropriate diffFlag for text_diff() based on query
** parameters and the to boolean arguments.
*/
u64 construct_diff_flags(int verboseFlag, int sideBySide){
  u64 diffFlags = 0;  /* Zero means do not show any diff */
  if( verboseFlag!=0 ){
    int x;
    if( sideBySide ){
      diffFlags = DIFF_SIDEBYSIDE;

      /* "dw" query parameter determines width of each column */
      x = atoi(PD("dw","80"))*(DIFF_CONTEXT_MASK+1);
      if( x<0 || x>DIFF_WIDTH_MASK ) x = DIFF_WIDTH_MASK;
      diffFlags += x;
    }
................................................................................

    /* The "noopt" parameter disables diff optimization */
    if( PD("noopt",0)!=0 ) diffFlags |= DIFF_NOOPT;
    diffFlags |= DIFF_STRIP_EOLCR;
  }
  return diffFlags;
}

/*



















































































































** WEBPAGE: vinfo
** WEBPAGE: ci
** URL:  /ci?name=ARTIFACTID
** URL:  /vinfo?name=ARTIFACTID
**
** Display information about a particular check-in.
**
** We also jump here from /info if the name is a check-in
**
** If the /ci page is used (instead of /vinfo or /info) then the
** default behavior is to show unified diffs of all file changes.
** With /vinfo and /info, only a list of the changed files are
** shown, without diffs.  This behavior is inverted if the
** "show-version-diffs" setting is turned on.



*/
void ci_page(void){
  Stmt q1, q2, q3;
  int rid;
  int isLeaf;
  int verboseFlag;     /* True to show diffs */
  int sideBySide;      /* True for side-by-side diffs */
  u64 diffFlags;       /* Flag parameter for text_diff() */
  const char *zName;   /* Name of the check-in to be displayed */
  const char *zUuid;   /* UUID of zName */
  const char *zParent; /* UUID of the parent check-in (if any) */
  const char *zRe;     /* regex parameter */
  ReCompiled *pRe = 0; /* regex */
  const char *zW;      /* URL param for ignoring whitespace */
................................................................................
  if( zRe ) re_compile(&pRe, zRe, 0);
  zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
  zParent = db_text(0,
    "SELECT uuid FROM plink, blob"
    " WHERE plink.cid=%d AND blob.rid=plink.pid AND plink.isprim",
    rid
  );
  isLeaf = is_a_leaf(rid);
  db_prepare(&q1,
     "SELECT uuid, datetime(mtime,toLocal()), user, comment,"
     "       datetime(omtime,toLocal()), mtime"
     "  FROM blob, event"
     " WHERE blob.rid=%d"
     "   AND event.objid=%d",
     rid, rid
  );
  sideBySide = !is_false(PD("sbs","1"));


  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 *zComment;
    const char *zDate;
    const char *zOrigDate;

    style_header("Check-in [%S]", zUuid);
    login_anonymous_available();
    zEUser = db_text(0,
                   "SELECT value FROM tagxref"
                   " WHERE tagid=%d AND rid=%d AND tagtype>0",
                    TAG_USER, rid);
    zEComment = db_text(0,
                   "SELECT value FROM tagxref WHERE tagid=%d AND rid=%d",
                   TAG_COMMENT, rid);
    zUser = db_column_text(&q1, 2);

    zComment = db_column_text(&q1, 3);
    zDate = db_column_text(&q1,1);
    zOrigDate = db_column_text(&q1, 4);

    @ <div class="section">Overview</div>
    @ <table class="label-value">



























































    @ <tr><th>%s(hname_alg(nUuid)):</th><td>%s(zUuid)
    if( g.perm.Setup ){
      @ (Record ID: %d(rid))
    }
    @ </td></tr>
    @ <tr><th>Date:</th><td>

    hyperlink_to_date(zDate, "</td></tr>");
    if( zOrigDate && fossil_strcmp(zDate, zOrigDate)!=0 ){
      @ <tr><th>Original&nbsp;Date:</th><td>
      hyperlink_to_date(zOrigDate, "</td></tr>");
    }
    if( zEUser ){
      @ <tr><th>Edited&nbsp;User:</th><td>
      hyperlink_to_user(zEUser,zDate,"</td></tr>");
      @ <tr><th>Original&nbsp;User:</th><td>
      hyperlink_to_user(zUser,zDate,"</td></tr>");
    }else{
      @ <tr><th>User:</th><td>
      hyperlink_to_user(zUser,zDate,"</td></tr>");
    }
    if( zEComment ){
      @ <tr><th>Edited&nbsp;Comment:</th>
      @     <td class="infoComment">%!W(zEComment)</td></tr>
      @ <tr><th>Original&nbsp;Comment:</th>
      @     <td class="infoComment">%!W(zComment)</td></tr>
    }else{
      @ <tr><th>Comment:</th><td class="infoComment">%!W(zComment)</td></tr>







    }
    if( g.perm.Admin ){
      db_prepare(&q2,
         "SELECT rcvfrom.ipaddr, user.login, datetime(rcvfrom.mtime)"
         "  FROM blob JOIN rcvfrom USING(rcvid) LEFT JOIN user USING(uid)"
         " WHERE blob.rid=%d",
         rid
................................................................................
        if( zUser==0 || zUser[0]==0 ) zUser = "unknown";
        @ <tr><th>Received&nbsp;From:</th>
        @ <td>%h(zUser) @ %h(zIpAddr) on %s(zDate)</td></tr>
      }
      db_finalize(&q2);
    }
    if( g.perm.Hyperlink ){
      char *zPJ = db_get("short-project-name", 0);
      Blob projName;
      int jj;
      if( zPJ==0 ) zPJ = db_get("project-name", "unnamed");
      blob_zero(&projName);
      blob_append(&projName, zPJ, -1);
      blob_trim(&projName);
      zPJ = blob_str(&projName);
      for(jj=0; zPJ[jj]; jj++){
        if( (zPJ[jj]>0 && zPJ[jj]<' ') || strchr("\"*/:<>?\\|", zPJ[jj]) ){
          zPJ[jj] = '_';
        }
      }
      @ <tr><th>Timelines:</th><td>
      @   %z(href("%R/timeline?f=%!S&unhide",zUuid))family</a>
      if( zParent ){
        @ | %z(href("%R/timeline?p=%!S&unhide",zUuid))ancestors</a>
      }
      if( !isLeaf ){
        @ | %z(href("%R/timeline?d=%!S&unhide",zUuid))descendants</a>
      }
      if( zParent && !isLeaf ){
        @ | %z(href("%R/timeline?dp=%!S&unhide",zUuid))both</a>
      }
      db_prepare(&q2,"SELECT substr(tag.tagname,5) FROM tagxref, tag "
                     " WHERE rid=%d AND tagtype>0 "
                     "   AND tag.tagid=tagxref.tagid "
                     "   AND +tag.tagname GLOB 'sym-*'", rid);
      while( db_step(&q2)==SQLITE_ROW ){
        const char *zTagName = db_column_text(&q2, 0);
        @  | %z(href("%R/timeline?r=%T&unhide",zTagName))%h(zTagName)</a>
      }
      db_finalize(&q2);


      /* The Download: line */
      if( g.anon.Zip ){
        char *zUrl = mprintf("%R/tarball/%t-%S.tar.gz?uuid=%s",
                             zPJ, zUuid, zUuid);
        @ </td></tr>
        @ <tr><th>Downloads:</th><td>
        @ %z(href("%s",zUrl))Tarball</a>
        @ | %z(href("%R/zip/%t-%S.zip?uuid=%!S",zPJ,zUuid,zUuid))
        @         ZIP archive</a>
        fossil_free(zUrl);
      }
      @ </td></tr>
      @ <tr><th>Other&nbsp;Links:</th>
      @   <td>
      @     %z(href("%R/tree?ci=%!S",zUuid))files</a>
      @   | %z(href("%R/fileage?name=%!S",zUuid))file ages</a>
      @   | %z(href("%R/tree?nofiles&type=tree&ci=%!S",zUuid))folders</a>
      @   | %z(href("%R/artifact/%!S",zUuid))manifest</a>




      if( g.anon.Write ){
        @   | %z(href("%R/ci_edit?r=%!S",zUuid))edit</a>
      }
      @   </td>
      @ </tr>
      blob_reset(&projName);
    }
    @ </table>
  }else{
    style_header("Check-in Information");
    login_anonymous_available();
  }
  db_finalize(&q1);
  render_backlink_graph(zUuid, "<div class=\"section\">References</div>\n");
  showTags(rid);
  @ <div class="section">Context</div>
  render_checkin_context(rid, 0);
  @ <div class="section">Changes</div>
  @ <div class="sectionmenu">
  verboseFlag = g.zPath[0]!='c';
  if( db_get_boolean("show-version-diffs", 0)==0 ){
    verboseFlag = !verboseFlag;
    zPage = "ci";
    zPageHide = "vinfo";
  }
  diffFlags = construct_diff_flags(verboseFlag, sideBySide);
  zW = (diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
  if( verboseFlag ){
    @ %z(xhref("class='button'","%R/%s/%T",zPageHide,zName))


    @ Hide&nbsp;Diffs</a>
    if( sideBySide ){
      @ %z(xhref("class='button'","%R/%s/%T?sbs=0%s",zPage,zName,zW))



      @ Unified&nbsp;Diffs</a>
    }else{
      @ %z(xhref("class='button'","%R/%s/%T?sbs=1%s",zPage,zName,zW))



      @ Side-by-Side&nbsp;Diffs</a>
    }

    if( *zW ){
      @ %z(xhref("class='button'","%R/%s/%T?sbs=%d",zPage,zName,sideBySide))

      @ Show&nbsp;Whitespace&nbsp;Changes</a>
    }else{
      @ %z(xhref("class='button'","%R/%s/%T?sbs=%d&w",zPage,zName,sideBySide))

      @ Ignore&nbsp;Whitespace</a>
    }
  }else{
    @ %z(xhref("class='button'","%R/%s/%T?sbs=0",zPage,zName))
    @ Show&nbsp;Unified&nbsp;Diffs</a>
    @ %z(xhref("class='button'","%R/%s/%T?sbs=1",zPage,zName))
    @ Show&nbsp;Side-by-Side&nbsp;Diffs</a>
  }
  if( zParent ){
    @ %z(xhref("class='button'","%R/vpatch?from=%!S&to=%!S",zParent,zUuid))
    @ Patch</a>
  }
  if( g.perm.Admin ){
    @ %z(xhref("class='button'","%R/mlink?ci=%!S",zUuid))MLink Table</a>
  }
  @</div>
  if( pRe ){
    @ <p><b>Only differences that match regular expression "%h(zRe)"
    @ are shown.</b></p>
  }
  db_prepare(&q3,
................................................................................
    int mperm = db_column_int(&q3, 1);
    const char *zOld = db_column_text(&q3,2);
    const char *zNew = db_column_text(&q3,3);
    const char *zOldName = db_column_text(&q3, 4);
    append_file_change_line(zName, zOld, zNew, zOldName, diffFlags,pRe,mperm);
  }
  db_finalize(&q3);
  append_diff_javascript(sideBySide);

  style_footer();
}

/*
** WEBPAGE: winfo
** URL:  /winfo?name=UUID
**
................................................................................
** 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
**   v=BOOLEAN       Default true.  If false, only list files that have changed
**   sbs=BOOLEAN     Side-by-side diff if true.  Unified diff if false
**   glob=STRING     only diff files matching this glob
**   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
**
**
** Show all differences between two check-ins.
*/
void vdiff_page(void){
  int ridFrom, ridTo;
  int verboseFlag;
  int sideBySide;
  u64 diffFlags = 0;
  Manifest *pFrom, *pTo;
  ManifestFile *pFileFrom, *pFileTo;
  const char *zBranch;
  const char *zFrom;
  const char *zTo;
  const char *zRe;
  const char *zW;
  const char *zVerbose;
  const char *zGlob;
  ReCompiled *pRe = 0;
  login_check_credentials();
  if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
  login_anonymous_available();



  zRe = P("regex");
  if( zRe ) re_compile(&pRe, zRe, 0);
  zBranch = P("branch");
  if( zBranch && zBranch[0] ){
    cgi_replace_parameter("from", mprintf("root:%s", zBranch));
    cgi_replace_parameter("to", zBranch);
  }
  pTo = vdiff_parse_manifest("to", &ridTo);
  if( pTo==0 ) return;
  pFrom = vdiff_parse_manifest("from", &ridFrom);
  if( pFrom==0 ) return;
  sideBySide = !is_false(PD("sbs","1"));
  zVerbose = P("v");
  if( !zVerbose ){
    zVerbose = P("verbose");
  }
  if( !zVerbose ){
    zVerbose = P("detail"); /* deprecated */
  }
  verboseFlag = (zVerbose!=0) && !is_false(zVerbose);
  if( !verboseFlag && sideBySide ) verboseFlag = 1;
  zGlob = P("glob");
  zFrom = P("from");
  zTo = P("to");
  if(zGlob && !*zGlob){
    zGlob = NULL;
  }
  diffFlags = construct_diff_flags(verboseFlag, sideBySide);
  zW = (diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
  style_submenu_element("Path", "%R/timeline?me=%T&you=%T", zFrom, zTo);
  if( sideBySide || verboseFlag ){
    style_submenu_element("Hide Diff", "%R/vdiff?from=%T&to=%T&sbs=0%s%T%s",
                          zFrom, zTo,
                          zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW);
  }
  if( !sideBySide ){
    style_submenu_element("Side-by-Side Diff",
                          "%R/vdiff?from=%T&to=%T&sbs=1%s%T%s",
                          zFrom, zTo,
                          zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW);
  }
  if( sideBySide || !verboseFlag ) {
    style_submenu_element("Unified Diff",
                          "%R/vdiff?from=%T&to=%T&sbs=0&v%s%T%s",
                          zFrom, zTo,
                          zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW);
  }
  style_submenu_element("Invert",
                        "%R/vdiff?from=%T&to=%T&sbs=%d%s%s%T%s", zTo, zFrom,
                        sideBySide, (verboseFlag && !sideBySide)?"&v":"",
                        zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW);
  if( zGlob ){
    style_submenu_element("Clear glob",
                          "%R/vdiff?from=%T&to=%T&sbs=%d%s%s", zFrom, zTo,
                          sideBySide, (verboseFlag && !sideBySide)?"&v":"", zW);
  }else{
    style_submenu_element("Patch", "%R/vpatch?from=%T&to=%T%s", zFrom, zTo, zW);
  }
  if( sideBySide || verboseFlag ){
    style_submenu_checkbox("w", "Ignore Whitespace", 0, 0);
  }
  style_header("Check-in Differences");
  if( P("nohdr")==0 ){
    @ <h2>Difference From:</h2><blockquote>
    checkin_description(ridFrom);
    @ </blockquote><h2>To:</h2><blockquote>
................................................................................
      }
      pFileFrom = manifest_file_next(pFrom, 0);
      pFileTo = manifest_file_next(pTo, 0);
    }
  }
  manifest_destroy(pFrom);
  manifest_destroy(pTo);
  append_diff_javascript(sideBySide);
  style_footer();
}

#if INTERFACE
/*
** Possible return values from object_description()
*/
................................................................................

  db_prepare(&q,
    "SELECT filename.name, datetime(event.mtime,toLocal()),"
    "       coalesce(event.ecomment,event.comment),"
    "       coalesce(event.euser,event.user),"
    "       b.uuid, mlink.mperm,"
    "       coalesce((SELECT value FROM tagxref"
                    "  WHERE tagid=%d AND tagtype>0 AND rid=mlink.mid),'trunk'),"
    "       a.size"
    "  FROM mlink, filename, event, blob a, blob b"
    " WHERE filename.fnid=mlink.fnid"
    "   AND event.objid=mlink.mid"
    "   AND a.rid=mlink.fid"
    "   AND b.rid=mlink.mid"
    "   AND mlink.fid=%d"
................................................................................
      }else if( mPerm==PERM_EXE ){
        @ <li>Executable file
        objType |= OBJTYPE_EXE;
      }else{
        @ <li>File
      }
      objType |= OBJTYPE_CONTENT;
      @ %z(href("%R/finfo?name=%T",zName))%h(zName)</a>
      tag_private_status(rid);
      if( showDetail ){
        @ <ul>
      }
      prevName = fossil_strdup(zName);
    }
    if( showDetail ){
................................................................................
    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/finfo?name=%T&ci=%!S",zName,zVers))[ancestry]</a>
      @ %z(href("%R/annotate?filename=%T&checkin=%!S",zName,zVers))
      @ [annotate]</a>
      @ %z(href("%R/blame?filename=%T&checkin=%!S",zName,zVers))
      @ [blame]</a>

    }
    cnt++;
    if( pDownloadName && blob_size(pDownloadName)==0 ){
      blob_append(pDownloadName, zName, -1);
    }
  }
  if( prevName && showDetail ){
................................................................................
**      sbs=BOOLEAN      Turn side-by-side diffs on and off (default: on)
**      verbose=BOOLEAN  Show more detail when describing artifacts
**      w=BOOLEAN        Ignore whitespace
*/
void diff_page(void){
  int v1, v2;
  int isPatch = P("patch")!=0;
  int sideBySide = PB("sbs");
  int verbose = PB("verbose");
  char *zV1;
  char *zV2;
  const char *zRe;
  ReCompiled *pRe = 0;
  u64 diffFlags;
  u32 objdescFlags = 0;


  login_check_credentials();
  if( !g.perm.Read ){ login_needed(g.anon.Read); return; }



  if( P("from") && P("to") ){
    v1 = artifact_from_ci_and_filename(0, "from");
    v2 = artifact_from_ci_and_filename(0, "to");
  }else{
    Stmt q;
    v1 = name_to_rid_www("v1");
    v2 = name_to_rid_www("v2");
................................................................................
    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(1, sideBySide) | DIFF_HTML;

  style_header("Diff");
  style_submenu_checkbox("w", "Ignore Whitespace", 0, 0);
  style_submenu_checkbox("sbs", "Side-by-Side Diff", 0, 0);






  style_submenu_checkbox("verbose", "Verbose", 0, 0);
  style_submenu_element("Patch", "%s/fdiff?v1=%T&v2=%T&patch",
                        g.zTop, 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
................................................................................
  }
  if( pRe ){
    @ <b>Only differences that match regular expression "%h(zRe)"
    @ are shown.</b>
  }
  @ <hr />
  append_diff(zV1, zV2, diffFlags, pRe);
  append_diff_javascript(sideBySide);
  style_footer();
}

/*
** WEBPAGE: raw
** URL: /raw?name=ARTIFACTID&m=TYPE
** URL: /raw?ci=BRANCH&filename=NAME
................................................................................
      " WHERE iStart <= %d AND iEnd >= %d", n, n);
    if( db_step(&q)==SQLITE_ROW ){
      iStart = db_column_int(&q, 0);
      iEnd = db_column_int(&q, 1);
    }
    db_finalize(&q);
    for(i=0; z[i] && z[i]!='\n'; i++){}
    if( n==iTop ) cgi_append_content("<span id=\"topln\">", -1);
    if( n==iStart ){
      cgi_append_content("<div class=\"selectedText\">",-1);
    }
    cgi_printf("%6d  ", n);
    if( i>0 ){
      char *zHtml = htmlize(z, i);
      cgi_append_content(zHtml, -1);
................................................................................
    else cgi_append_content("\n", 1);
    z += i;
    if( z[0]=='\n' ) z++;
  }
  if( n<iEnd ) cgi_printf("</div>");
  @ </pre>
  if( db_int(0, "SELECT EXISTS(SELECT 1 FROM lnos)") ){
    @ <script>gebi('topln').scrollIntoView(true);</script>
  }
}


/*
** WEBPAGE: artifact
** WEBPAGE: file
................................................................................
      }
    }
    if( strcmp(zModAction,"approve")==0 ){
      moderation_approve(rid);
    }
  }
  zTktTitle = db_table_has_column("repository", "ticket", "title" )

      ? db_text("(No title)", "SELECT title FROM ticket WHERE tkt_uuid=%Q", zTktName)
      : 0;
  style_header("Ticket Change Details");
  style_submenu_element("Raw", "%R/artifact/%s", zUuid);
  style_submenu_element("History", "%R/tkthistory/%s", zTktName);
  style_submenu_element("Page", "%R/tktview/%t", zTktName);
  style_submenu_element("Timeline", "%R/tkttimeline/%t", zTktName);
  if( P("plaintext") ){
................................................................................
  }
  @ <tr><th>Ticket:</th>
  @ <td>%z(href("%R/tktview/%s",zTktName))%s(zTktName)</a>
  if( zTktTitle ){
        @<br />%h(zTktTitle)
  }
  @</td></tr>
  @ <tr><th>Date:</th><td>

  hyperlink_to_date(zDate, "</td></tr>");
  @ <tr><th>User:</th><td>
  hyperlink_to_user(pTktChng->zUser, zDate, "</td></tr>");
  @ </table>
  free(zDate);
  free(zTktTitle);

  if( g.perm.ModTkt && modPending ){
    @ <div class="section">Moderation</div>
    @ <blockquote>
................................................................................
    ainfo_page();
  }else
  {
    artifact_page();
  }
}

/*
** Generate HTML that will present the user with a selection of
** potential background colors for timeline entries.
*/
void render_color_chooser(
  int fPropagate,             /* Default value for propagation */
  const char *zDefaultColor,  /* The current default color */
  const char *zIdPropagate,   /* ID of form element checkbox.  NULL for none */
  const char *zId,            /* The ID of the form element */
  const char *zIdCustom       /* ID of text box for custom color */
){
  static const struct SampleColors {
     const char *zCName;
     const char *zColor;
  } aColor[] = {
     { "(none)",  "" },
     { "#f2dcdc", 0 },
     { "#bde5d6", 0 },
     { "#a0a0a0", 0 },
     { "#b0b0b0", 0 },
     { "#c0c0c0", 0 },
     { "#d0d0d0", 0 },
     { "#e0e0e0", 0 },

     { "#c0fff0", 0 },
     { "#c0f0ff", 0 },
     { "#d0c0ff", 0 },
     { "#ffc0ff", 0 },
     { "#ffc0d0", 0 },
     { "#fff0c0", 0 },
     { "#f0ffc0", 0 },
     { "#c0ffc0", 0 },

     { "#a8d3c0", 0 },
     { "#a8c7d3", 0 },
     { "#aaa8d3", 0 },
     { "#cba8d3", 0 },
     { "#d3a8bc", 0 },
     { "#d3b5a8", 0 },
     { "#d1d3a8", 0 },
     { "#b1d3a8", 0 },

     { "#8eb2a1", 0 },
     { "#8ea7b2", 0 },
     { "#8f8eb2", 0 },
     { "#ab8eb2", 0 },
     { "#b28e9e", 0 },
     { "#b2988e", 0 },
     { "#b0b28e", 0 },
     { "#95b28e", 0 },

     { "#80d6b0", 0 },
     { "#80bbd6", 0 },
     { "#8680d6", 0 },
     { "#c680d6", 0 },
     { "#d680a6", 0 },
     { "#d69b80", 0 },
     { "#d1d680", 0 },
     { "#91d680", 0 },


     { "custom",  "##" },
  };
  int nColor = count(aColor)-1;
  int stdClrFound = 0;
  int i;

  if( zIdPropagate ){
    @ <div><label>
    if( fPropagate ){
      @ <input type="checkbox" name="%s(zIdPropagate)" checked="checked" />
    }else{
      @ <input type="checkbox" name="%s(zIdPropagate)" />
    }
    @ Propagate color to descendants</label></div>
  }
  @ <table border="0" cellpadding="0" cellspacing="1" class="colorpicker">
  @ <tr>
  for(i=0; i<nColor; i++){
    const char *zClr = aColor[i].zColor;
    if( zClr==0 ) zClr = aColor[i].zCName;
    if( zClr[0] ){
      @ <td style="background-color: %h(zClr);">
    }else{
      @ <td>
    }
    @ <label>
    if( fossil_strcmp(zDefaultColor, zClr)==0 ){
      @ <input type="radio" name="%s(zId)" value="%h(zClr)"
      @  checked="checked" />
      stdClrFound=1;
    }else{
      @ <input type="radio" name="%s(zId)" value="%h(zClr)" />
    }
    @ %h(aColor[i].zCName)</label></td>
    if( (i%8)==7 && i+1<nColor ){
      @ </tr><tr>
    }
  }
  @ </tr><tr>
  if( stdClrFound ){
    @ <td colspan="6"><label>
    @ <input type="radio" name="%s(zId)" value="%h(aColor[nColor].zColor)"
    @  onclick="gebi('%s(zIdCustom)').select();" />
  }else{
    @ <td style="background-color: %h(zDefaultColor);" colspan="6"><label>
    @ <input type="radio" name="%s(zId)" value="%h(aColor[nColor].zColor)"
    @  checked="checked" onclick="gebi('%s(zIdCustom)').select();" />
  }
  @ %h(aColor[i].zCName)</label>&nbsp;
  @ <input type="text" name="%s(zIdCustom)"
  @  id="%s(zIdCustom)" class="checkinUserColor"
  @  value="%h(stdClrFound?"":zDefaultColor)"
  @  onfocus="this.form.elements['%s(zId)'][%d(nColor)].checked = true;"
  @  onload="this.blur();"
  @  onblur="this.parentElement.style.backgroundColor = this.value ? ('#'+this.value.replace('#','')) : '';" />
  @ </td>
  @ </tr>
  @ </table>
}

/*
** Do a comment comparison.
**
** +  Leading and trailing whitespace are ignored.
** +  \r\n characters compare equal to \n
**
** Return true if equal and false if not equal.
................................................................................
*/
int is_datetime(const char* zDate){
  return db_int(0, "SELECT datetime(%Q) NOT NULL", zDate);
}

/*
** WEBPAGE: ci_edit
** URL:  /ci_edit?r=RID&c=NEWCOMMENT&u=NEWUSER



**
** Present a dialog for updating properties of a check-in.

**
**     *  The check-in user




**     *  The check-in comment
**     *  The check-in time and date

**     *  The background color.
**     *  Add and remove tags














*/
void ci_edit_page(void){
  int rid;
  const char *zComment;         /* Current comment on the check-in */
  const char *zNewComment;      /* Revised check-in comment */
  const char *zUser;            /* Current user for the check-in */
  const char *zNewUser;         /* Revised user */
  const char *zDate;            /* Current date of the check-in */
  const char *zNewDate;         /* Revised check-in date */

  const char *zColor;
  const char *zNewColor;
  const char *zNewTagFlag;
  const char *zNewTag;
  const char *zNewBrFlag;
  const char *zNewBranch;
  const char *zCloseFlag;
  const char *zHideFlag;
  int fPropagateColor;          /* True if color propagates before edit */
  int fNewPropagateColor;       /* True if color propagates after edit */
  int fHasHidden = 0;           /* True if hidden tag already set */
  int fHasClosed = 0;           /* True if closed tag already set */
  const char *zChngTime = 0;     /* Value of chngtime= query param, if any */
  char *zUuid;
  Blob comment;
  char *zBranchName = 0;
  Stmt q;

  login_check_credentials();
  if( !g.perm.Write ){ login_needed(g.anon.Write); return; }
  rid = name_to_typed_rid(P("r"), "ci");
  zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
  zComment = db_text(0, "SELECT coalesce(ecomment,comment)"
                        "  FROM event WHERE objid=%d", rid);
  if( zComment==0 ) fossil_redirect_home();
  if( P("cancel") ){
    cgi_redirectf("ci?name=%s", zUuid);
  }
  if( g.perm.Setup ) zChngTime = P("chngtime");
  zNewComment = PD("c",zComment);
  zUser = db_text(0, "SELECT coalesce(euser,user)"
                     "  FROM event WHERE objid=%d", rid);
  if( zUser==0 ) fossil_redirect_home();
  zNewUser = PDT("u",zUser);
................................................................................
  zDate = db_text(0, "SELECT datetime(mtime)"
                     "  FROM event WHERE objid=%d", rid);
  if( zDate==0 ) fossil_redirect_home();
  zNewDate = PDT("dt",zDate);
  zColor = db_text("", "SELECT bgcolor"
                        "  FROM event WHERE objid=%d", rid);
  zNewColor = PDT("clr",zColor);
  if( fossil_strcmp(zNewColor,"##")==0 ){
    zNewColor = PT("clrcust");
  }
  fPropagateColor = db_int(0, "SELECT tagtype FROM tagxref"
                              " WHERE rid=%d AND tagid=%d",
                              rid, TAG_BGCOLOR)==2;
  fNewPropagateColor = P("clr")!=0 ? P("pclr")!=0 : fPropagateColor;

  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") ){
................................................................................
    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( zNewColor[0]
     && (fPropagateColor!=fNewPropagateColor
             || fossil_strcmp(zColor,zNewColor)!=0)

    ) add_color(zNewColor,fNewPropagateColor);
    if( zNewColor[0]==0 && zColor[0]!=0 ) cancel_color();

    if( comment_compare(zComment,zNewComment)==0 ) add_comment(zNewComment);
    if( fossil_strcmp(zDate,zNewDate)!=0 ) add_date(zNewDate);
    if( fossil_strcmp(zUser,zNewUser)!=0 ) add_user(zNewUser);
    db_prepare(&q,
       "SELECT tag.tagid, tagname FROM tagxref, tag"
       " WHERE tagxref.rid=%d AND tagtype>0 AND tagxref.tagid=tag.tagid",
       rid
................................................................................
    }
    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);
    cgi_redirectf("ci?name=%s", zUuid);
  }
  blob_zero(&comment);
  blob_append(&comment, zNewComment, -1);
  zUuid[10] = 0;
  style_header("Edit Check-in [%s]", zUuid);
  /*
  ** chgcbn/chgbn: Handle change of (checkbox for) branch name in
  ** remaining of form.
  */
  @ <script>
  @ function chgcbn(checked, branch){
  @   val = gebi('brname').value.trim();
  @   if( !val || !checked ) val = branch;
  @   if( checked ) gebi('brname').select();
  @   gebi('hbranch').textContent = val;
  @   cidbrid = document.getElementById('cbranch');
  @   if( cidbrid ) cidbrid.textContent = val;
  @ }
  @ function chgbn(val, branch){
  @   if( !val ) val = branch;
  @   gebi('newbr').checked = (val!=branch);
  @   gebi('hbranch').textContent = val;
  @   cidbrid = document.getElementById('cbranch');
  @   if( cidbrid ) cidbrid.textContent = val;
  @ }
  @ </script>
  if( P("preview") ){
    Blob suffix;
    int nTag = 0;
    @ <b>Preview:</b>
    @ <blockquote>
    @ <table border=0>
    if( zNewColor && zNewColor[0] ){
      @ <tr><td style="background-color: %h(zNewColor);">


    }else{
      @ <tr><td>
    }
    @ %!W(blob_str(&comment))
    blob_zero(&suffix);
    blob_appendf(&suffix, "(user: %h", zNewUser);
    db_prepare(&q, "SELECT substr(tagname,5) FROM tagxref, tag"
................................................................................
  if( zChngTime ){
    @ <tr><th align="right" valign="top">Timestamp of this change:</th>
    @ <td valign="top">
    @   <input type="text" name="chngtime" size="20" value="%h(zChngTime)" />
    @ </td></tr>
  }

  @ <tr><th align="right" valign="top">Background Color:</th>
  @ <td valign="top">
  render_color_chooser(fNewPropagateColor, zNewColor, "pclr", "clr", "clrcust");










  @ </td></tr>

  @ <tr><th align="right" valign="top">Tags:</th>
  @ <td valign="top">
  @ <label><input type="checkbox" id="newtag" name="newtag"%s(zNewTagFlag) />
  @ Add the following new tag name to this check-in:</label>
  @ <input type="text" style="width:15;" name="tagname" value="%h(zNewTag)"
  @ onkeyup="gebi('newtag').checked=!!this.value" />
  zBranchName = db_text(0, "SELECT value FROM tagxref, tag"
     " WHERE tagxref.rid=%d AND tagtype>0 AND tagxref.tagid=tag.tagid"
     " AND tagxref.tagid=%d", rid, TAG_BRANCH);
  db_prepare(&q,
     "SELECT tag.tagid, tagname, tagxref.value FROM tagxref, tag"
     " WHERE tagxref.rid=%d AND tagtype>0 AND tagxref.tagid=tag.tagid"
     " ORDER BY CASE WHEN tagname GLOB 'sym-*' THEN substr(tagname,5)"
................................................................................
    zBranchName = db_get("main-branch", "trunk");
  }
  if( !zNewBranch || !zNewBranch[0]){
    zNewBranch = zBranchName;
  }
  @ <tr><th align="right" valign="top">Branching:</th>
  @ <td valign="top">
  @ <label><input id="newbr" type="checkbox" name="newbr"%s(zNewBrFlag)
  @ onchange="chgcbn(this.checked,'%h(zBranchName)')" />
  @ Make this check-in the start of a new branch named:</label>
  @ <input id="brname" type="text" style="width:15;" name="brname"
  @ value="%h(zNewBranch)"
  @ onkeyup="chgbn(this.value.trim(),'%h(zBranchName)')" /></td></tr>
  if( !fHasHidden ){
    @ <tr><th align="right" valign="top">Branch Hiding:</th>
    @ <td valign="top">
    @ <label><input type="checkbox" id="hidebr" name="hide"%s(zHideFlag) />
    @ Hide branch
    @ <span style="font-weight:bold" id="hbranch">%h(zBranchName)</span>
    @ from the timeline starting from this check-in</label>
................................................................................
      @ </td></tr>
    }
  }
  if( zBranchName ) fossil_free(zBranchName);


  @ <tr><td colspan="2">

  @ <input type="submit" name="preview" value="Preview" />

  @ <input type="submit" name="apply" value="Apply Changes" />
  @ <input type="submit" name="cancel" value="Cancel" />

  @ </td></tr>
  @ </table>
  @ </div></form>

  style_footer();
}

/*
** 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.
................................................................................
    cancel_color();
  }
  if( fEditComment ){
    prepare_amend_comment(&comment, zComment, zUuid);
    zNewComment = blob_str(&comment);
  }else if( zComFile ){
    blob_zero(&comment);
    blob_read_from_file(&comment, zComFile);
    blob_to_utf8_no_bom(&comment, 1);
    zNewComment = blob_str(&comment);
  }
  if( zNewComment && zNewComment[0]
      && comment_compare(zComment,zNewComment)==0 ) add_comment(zNewComment);
  if( zNewDate && zNewDate[0] && fossil_strcmp(zDate,zNewDate)!=0 ){
    if( is_datetime(zNewDate) ){







>
|
>
>







 







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







 







|
>







 







|
>







 







|
>







 







|




|
|

|










|


|






|










|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






|

|

|







 









>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>









<
<
<
<
<
>
>
>





|
<







 







|








|
>
>





>













|
>



>


>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>





|
>

<
<
<
<
<
<
<
<
<
<
<
<
<

<
<


<
<
>
>
>
>
>
>
>







 







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


<
<
<
|
>
>
>
>





<








<




<
<
<
<
<
<
|

<
<
>
>

<
<
>
>
>
|
<
<
>
>
>
|
|
>

<
>


<
>


<
<
<
<
<


|



|







 







|
>







 







|
<










|
<








<





>
>
>











<
<
<
<
<
<
<
<
<
<






|


|
|



|

|



|

|




|
<



|
<



|







 







|







 







|







 







|







 







<




>







 







|
<






>



>
>
>







 







|



|
>
>
>
>
>
>

|
|







 







|







 







|







 







|







 







>
|







 







|
>

<
<







 







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







 







|
>
>
>

<
>

<
>
>
>
>
|
<
>
|
<
>
>
>
>
>
>
>
>
>
>
>
>
>
>









>
|
|
|
|
|
|
|
|




|













|







 







<
<
<




>







 







>
|


>
|
<
>







 







|





<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






|

>
>







 







|

|
>
>
>
>
>
>
>
>
>
>






|
|







 







|
|

|
|
<







 







>

>
|
<
>



>







 







|







199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
...
245
246
247
248
249
250
251



































































252
253
254
255
256
257
258
...
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
...
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
...
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
...
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464


























465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
...
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623





624
625
626
627
628
629
630
631
632

633
634
635
636
637
638
639
...
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766













767


768
769


770
771
772
773
774
775
776
777
778
779
780
781
782
783
...
789
790
791
792
793
794
795















































796
797



798
799
800
801
802
803
804
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
...
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
....
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
....
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
....
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
....
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
....
1354
1355
1356
1357
1358
1359
1360

1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
....
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
....
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
....
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
....
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
....
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
....
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
....
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266


2267
2268
2269
2270
2271
2272
2273
....
2369
2370
2371
2372
2373
2374
2375

























































































































2376
2377
2378
2379
2380
2381
2382
....
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
....
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637

2638
2639
2640
2641
2642
2643
2644
2645
....
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
....
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
....
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811

2812
2813
2814
2815
2816
2817
2818
....
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846

2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
....
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
void info_cmd(void){
  i64 fsize;
  int verboseFlag = find_option("verbose","v",0)!=0;
  if( !verboseFlag ){
    verboseFlag = find_option("detail","l",0)!=0; /* deprecated */
  }

  if( g.argc==3
   && (fsize = file_size(g.argv[2], 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();
................................................................................
    if( rid==0 ){
      fossil_fatal("no such object: %s", g.argv[2]);
    }
    show_common_info(rid, "uuid:", 1, 1);
  }
}




































































/*
** Show the context graph (immediate parents and children) for
** check-in rid.
*/
void render_checkin_context(int rid, int parentsOnly){
  Blob sql;
  Stmt q;
................................................................................
  if( !parentsOnly ){
    db_multi_exec(
      "INSERT OR IGNORE INTO ok SELECT cid FROM plink WHERE pid=%d;", rid
    );
  }
  blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
  db_prepare(&q, "%s", blob_sql_text(&sql));
  www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|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
................................................................................
  );
  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);
}

/*
** WEBPAGE: test-backlinks
**
** Show a timeline of all check-ins and other events that have entries
................................................................................
     " 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);
  db_finalize(&q);
  style_footer();
}


/*
** Append the difference between artifacts to the output
................................................................................
    }
    if( diffFlags ){
      append_diff(zOld, zNew, diffFlags, pRe);
    }
  }else{
    if( zOld && zNew ){
      if( fossil_strcmp(zOld, zNew)!=0 ){
        @ 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>.
      }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>.
      }else{
        @ %z(href("%R/finfo?name=%T&m=%!S",zName,zNew))%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>.
    }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>.
    }
    if( diffFlags ){
      append_diff(zOld, zNew, diffFlags, pRe);
    }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");


























}

/*
** Construct an appropriate diffFlag for text_diff() based on query
** parameters and the to boolean arguments.
*/
u64 construct_diff_flags(int diffType){
  u64 diffFlags = 0;  /* Zero means do not show any diff */
  if( diffType>0 ){
    int x;
    if( diffType==2 ){
      diffFlags = DIFF_SIDEBYSIDE;

      /* "dw" query parameter determines width of each column */
      x = atoi(PD("dw","80"))*(DIFF_CONTEXT_MASK+1);
      if( x<0 || x>DIFF_WIDTH_MASK ) x = DIFF_WIDTH_MASK;
      diffFlags += x;
    }
................................................................................

    /* The "noopt" parameter disables diff optimization */
    if( PD("noopt",0)!=0 ) diffFlags |= DIFF_NOOPT;
    diffFlags |= DIFF_STRIP_EOLCR;
  }
  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;

  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();
    return;
  }
  zHash = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
  style_header("Tags and Properties");
  @ <h1>Tags and Properties for Check-In \
  @ %z(href("%R/ci/%!S",zHash))%S(zHash)</a></h1>
  db_prepare(&q,
    "SELECT tag.tagid, tagname, "
    "       (SELECT uuid FROM blob WHERE rid=tagxref.srcid AND rid!=%d),"
    "       value, datetime(tagxref.mtime,toLocal()), tagtype,"
    "       (SELECT uuid FROM blob WHERE rid=tagxref.origid AND rid!=%d)"
    "  FROM tagxref JOIN tag ON tagxref.tagid=tag.tagid"
    " WHERE tagxref.rid=%d"
    " ORDER BY tagname /*sort*/", rid, rid, rid
  );
  while( db_step(&q)==SQLITE_ROW ){
    const char *zTagname = db_column_text(&q, 1);
    const char *zSrcUuid = db_column_text(&q, 2);
    const char *zValue = db_column_text(&q, 3);
    const char *zDate = db_column_text(&q, 4);
    int tagtype = db_column_int(&q, 5);
    const char *zOrigUuid = db_column_text(&q, 6);
    cnt++;
    if( cnt==1 ){
      @ <ul>
    }
    @ <li>
    if( tagtype==0 ){
      @ <span class="infoTagCancelled">%h(zTagname)</span> cancelled
    }else if( zValue ){
      @ <span class="infoTag">%h(zTagname)=%h(zValue)</span>
    }else {
      @ <span class="infoTag">%h(zTagname)</span>
    }
    if( tagtype==2 ){
      if( zOrigUuid && zOrigUuid[0] ){
        @ inherited from
        hyperlink_to_uuid(zOrigUuid);
      }else{
        @ propagates to descendants
      }
    }
    if( zSrcUuid && zSrcUuid[0] ){
      if( tagtype==0 ){
        @ by
      }else{
        @ added by
      }
      hyperlink_to_uuid(zSrcUuid);
      @ on
      hyperlink_to_date(zDate,0);
    }
    @ </li>
  }
  db_finalize(&q);
  if( cnt ){
    @ </ul>
  }
  @ <div class="section">Context</div>
  db_multi_exec(
     "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY);"
     "DELETE FROM ok;"
     "INSERT INTO ok VALUES(%d);"
     "INSERT OR IGNORE INTO ok "
     " SELECT tagxref.srcid"
     "   FROM tagxref JOIN tag ON tagxref.tagid=tag.tagid"
     "  WHERE tagxref.rid=%d;"
     "INSERT OR IGNORE INTO ok "
     " SELECT tagxref.origid"
     "   FROM tagxref JOIN tag ON tagxref.tagid=tag.tagid"
     "  WHERE tagxref.rid=%d;",
     rid, rid, rid
  );
  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
  );
  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);
  db_finalize(&q);
  style_footer();
}

/*
** WEBPAGE: vinfo
** WEBPAGE: ci
** URL:  /ci?name=ARTIFACTID
** URL:  /vinfo?name=ARTIFACTID
**
** Display information about a particular check-in.
**
** We also jump here from /info if the name is 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
** 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 *zRe;     /* regex parameter */
  ReCompiled *pRe = 0; /* regex */
  const char *zW;      /* URL param for ignoring whitespace */
................................................................................
  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
  );
  
  cookie_link_parameter("diff","diff","2");
  diffType = atoi(PD("diff","2"));
  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;

    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>
    @ <table class="label-value">
    @ <tr><th>Comment:</th><td class="infoComment">\
    @ %!W(zEComment?zEComment:zComment)</td></tr>

    /* The Download: line */
    if( g.perm.Zip  ){
      char *zPJ = db_get("short-project-name", 0);
      char *zUrl;
      Blob projName;
      int jj;
      if( zPJ==0 ) zPJ = db_get("project-name", "unnamed");
      blob_zero(&projName);
      blob_append(&projName, zPJ, -1);
      blob_trim(&projName);
      zPJ = blob_str(&projName);
      for(jj=0; zPJ[jj]; jj++){
        if( (zPJ[jj]>0 && zPJ[jj]<' ') || strchr("\"*/:<>?\\|", zPJ[jj]) ){
          zPJ[jj] = '_';
        }
      }
      zUrl = mprintf("%R/tarball/%t-%S.tar.gz?r=%s", zPJ, zUuid, zUuid);
      @ <tr><th>Downloads:</th><td>
      @ %z(href("%s",zUrl))Tarball</a>
      @ | %z(href("%R/zip/%t-%S.zip?r=%!S",zPJ,zUuid,zUuid))ZIP archive</a>
      @ | %z(href("%R/sqlar/%t-%S.sqlar?r=%!S",zPJ,zUuid,zUuid))\
      @ SQL archive</a></td></tr>
      fossil_free(zUrl);
      blob_reset(&projName);
    }

    @ <tr><th>Timelines:</th><td>
    @   %z(href("%R/timeline?f=%!S&unhide",zUuid))family</a>
    if( zParent ){
      @ | %z(href("%R/timeline?p=%!S&unhide",zUuid))ancestors</a>
    }
    if( !isLeaf ){
      @ | %z(href("%R/timeline?d=%!S&unhide",zUuid))descendants</a>
    }
    if( zParent && !isLeaf ){
      @ | %z(href("%R/timeline?dp=%!S&unhide",zUuid))both</a>
    }
    db_prepare(&q2,"SELECT substr(tag.tagname,5) FROM tagxref, tag "
                   " WHERE rid=%d AND tagtype>0 "
                   "   AND tag.tagid=tagxref.tagid "
                   "   AND +tag.tagname GLOB 'sym-*'", rid);
    while( db_step(&q2)==SQLITE_ROW ){
      const char *zTagName = db_column_text(&q2, 0);
      @  | %z(href("%R/timeline?r=%T&unhide",zTagName))%h(zTagName)</a>
    }
    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)
    if( g.perm.Setup ){
      @ (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)"
         "  FROM blob JOIN rcvfrom USING(rcvid) LEFT JOIN user USING(uid)"
         " WHERE blob.rid=%d",
         rid
................................................................................
        if( zUser==0 || zUser[0]==0 ) zUser = "unknown";
        @ <tr><th>Received&nbsp;From:</th>
        @ <td>%h(zUser) @ %h(zIpAddr) on %s(zDate)</td></tr>
      }
      db_finalize(&q2);
    }
    if( g.perm.Hyperlink ){















































      @ <tr><th>Other&nbsp;Links:</th>
      @   <td>



      @   %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>
  }else{
    style_header("Check-in Information");
    login_anonymous_available();
  }
  db_finalize(&q1);
  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>
  @ <div class="sectionmenu">






  diffFlags = construct_diff_flags(diffType);
  zW = (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>


  }
  if( diffType!=2 ){
    @ %z(chref("button","%R/%s/%T?diff=2%s",zPage,zName,zW))\
    @ Side-by-Side&nbsp;Diffs</a>
  }
  if( diffType!=0 ){
    if( *zW ){

      @ %z(chref("button","%R/%s/%T",zPage,zName))
      @ Show&nbsp;Whitespace&nbsp;Changes</a>
    }else{

      @ %z(chref("button","%R/%s/%T?w",zPage,zName))
      @ Ignore&nbsp;Whitespace</a>
    }





  }
  if( zParent ){
    @ %z(chref("button","%R/vpatch?from=%!S&to=%!S",zParent,zUuid))
    @ Patch</a>
  }
  if( g.perm.Admin ){
    @ %z(chref("button","%R/mlink?ci=%!S",zUuid))MLink Table</a>
  }
  @</div>
  if( pRe ){
    @ <p><b>Only differences that match regular expression "%h(zRe)"
    @ are shown.</b></p>
  }
  db_prepare(&q3,
................................................................................
    int mperm = db_column_int(&q3, 1);
    const char *zOld = db_column_text(&q3,2);
    const char *zNew = db_column_text(&q3,3);
    const char *zOldName = db_column_text(&q3, 4);
    append_file_change_line(zName, zOld, zNew, zOldName, diffFlags,pRe,mperm);
  }
  db_finalize(&q3);
  append_diff_javascript(diffType==2);
  cookie_render();
  style_footer();
}

/*
** WEBPAGE: winfo
** URL:  /winfo?name=UUID
**
................................................................................
** 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
**
**
** 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;
  ReCompiled *pRe = 0;
  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"));
  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));
    cgi_replace_parameter("to", zBranch);
  }
  pTo = vdiff_parse_manifest("to", &ridTo);
  if( pTo==0 ) return;
  pFrom = vdiff_parse_manifest("from", &ridFrom);
  if( pFrom==0 ) return;










  zGlob = P("glob");
  zFrom = P("from");
  zTo = P("to");
  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( diffType!=0 ){
    style_submenu_element("Hide Diff", "%R/vdiff?from=%T&to=%T&diff=0%s%T%s",
                          zFrom, zTo,
                          zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW);
  }
  if( diffType!=2 ){
    style_submenu_element("Side-by-Side Diff",
                          "%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",
                          "%R/vdiff?from=%T&to=%T&diff=1%s%T%s",
                          zFrom, zTo,
                          zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW);
  }
  style_submenu_element("Invert",
                        "%R/vdiff?from=%T&to=%T&%s%T%s", zTo, zFrom,

                        zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW);
  if( zGlob ){
    style_submenu_element("Clear glob",
                          "%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);
  }
  if( diffType!=0 ){
    style_submenu_checkbox("w", "Ignore Whitespace", 0, 0);
  }
  style_header("Check-in Differences");
  if( P("nohdr")==0 ){
    @ <h2>Difference From:</h2><blockquote>
    checkin_description(ridFrom);
    @ </blockquote><h2>To:</h2><blockquote>
................................................................................
      }
      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();
}

#if INTERFACE
/*
** Possible return values from object_description()
*/
................................................................................

  db_prepare(&q,
    "SELECT filename.name, datetime(event.mtime,toLocal()),"
    "       coalesce(event.ecomment,event.comment),"
    "       coalesce(event.euser,event.user),"
    "       b.uuid, mlink.mperm,"
    "       coalesce((SELECT value FROM tagxref"
                  "  WHERE tagid=%d AND tagtype>0 AND rid=mlink.mid),'trunk'),"
    "       a.size"
    "  FROM mlink, filename, event, blob a, blob b"
    " WHERE filename.fnid=mlink.fnid"
    "   AND event.objid=mlink.mid"
    "   AND a.rid=mlink.fid"
    "   AND b.rid=mlink.mid"
    "   AND mlink.fid=%d"
................................................................................
      }else if( mPerm==PERM_EXE ){
        @ <li>Executable file
        objType |= OBJTYPE_EXE;
      }else{
        @ <li>File
      }
      objType |= OBJTYPE_CONTENT;
      @ %z(href("%R/finfo?name=%T&m=%!S",zName,zUuid))%h(zName)</a>
      tag_private_status(rid);
      if( showDetail ){
        @ <ul>
      }
      prevName = fossil_strdup(zName);
    }
    if( showDetail ){
................................................................................
    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>
    }
    cnt++;
    if( pDownloadName && blob_size(pDownloadName)==0 ){
      blob_append(pDownloadName, zName, -1);
    }
  }
  if( prevName && showDetail ){
................................................................................
**      sbs=BOOLEAN      Turn side-by-side diffs on and off (default: on)
**      verbose=BOOLEAN  Show more detail when describing artifacts
**      w=BOOLEAN        Ignore whitespace
*/
void diff_page(void){
  int v1, v2;
  int isPatch = P("patch")!=0;
  int diffType;          /* 0: none, 1: unified,  2: side-by-side */

  char *zV1;
  char *zV2;
  const char *zRe;
  ReCompiled *pRe = 0;
  u64 diffFlags;
  u32 objdescFlags = 0;
  int verbose = PB("verbose");

  login_check_credentials();
  if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
  cookie_link_parameter("diff","diff","2");
  diffType = atoi(PD("diff","2"));
  cookie_render();
  if( P("from") && P("to") ){
    v1 = artifact_from_ci_and_filename(0, "from");
    v2 = artifact_from_ci_and_filename(0, "to");
  }else{
    Stmt q;
    v1 = name_to_rid_www("v1");
    v2 = name_to_rid_www("v2");
................................................................................
    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;

  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
................................................................................
  }
  if( pRe ){
    @ <b>Only differences that match regular expression "%h(zRe)"
    @ are shown.</b>
  }
  @ <hr />
  append_diff(zV1, zV2, diffFlags, pRe);
  append_diff_javascript(diffType);
  style_footer();
}

/*
** WEBPAGE: raw
** URL: /raw?name=ARTIFACTID&m=TYPE
** URL: /raw?ci=BRANCH&filename=NAME
................................................................................
      " WHERE iStart <= %d AND iEnd >= %d", n, n);
    if( db_step(&q)==SQLITE_ROW ){
      iStart = db_column_int(&q, 0);
      iEnd = db_column_int(&q, 1);
    }
    db_finalize(&q);
    for(i=0; z[i] && z[i]!='\n'; i++){}
    if( n==iTop ) cgi_append_content("<span id=\"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_append_content(zHtml, -1);
................................................................................
    else cgi_append_content("\n", 1);
    z += i;
    if( z[0]=='\n' ) z++;
  }
  if( n<iEnd ) cgi_printf("</div>");
  @ </pre>
  if( db_int(0, "SELECT EXISTS(SELECT 1 FROM lnos)") ){
    style_load_one_js_file("scroll.js");
  }
}


/*
** WEBPAGE: artifact
** WEBPAGE: file
................................................................................
      }
    }
    if( strcmp(zModAction,"approve")==0 ){
      moderation_approve(rid);
    }
  }
  zTktTitle = db_table_has_column("repository", "ticket", "title" )
      ? db_text("(No title)", 
                "SELECT title FROM ticket WHERE tkt_uuid=%Q", zTktName)
      : 0;
  style_header("Ticket Change Details");
  style_submenu_element("Raw", "%R/artifact/%s", zUuid);
  style_submenu_element("History", "%R/tkthistory/%s", zTktName);
  style_submenu_element("Page", "%R/tktview/%t", zTktName);
  style_submenu_element("Timeline", "%R/tkttimeline/%t", zTktName);
  if( P("plaintext") ){
................................................................................
  }
  @ <tr><th>Ticket:</th>
  @ <td>%z(href("%R/tktview/%s",zTktName))%s(zTktName)</a>
  if( 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>
................................................................................
    ainfo_page();
  }else
  {
    artifact_page();
  }
}


























































































































/*
** Do a comment comparison.
**
** +  Leading and trailing whitespace are ignored.
** +  \r\n characters compare equal to \n
**
** Return true if equal and false if not equal.
................................................................................
*/
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:
**

**     rid=INTEGER        Record ID of the check-in to edit (REQUIRED)
**
** POST parameters after pressing "Perview", "Cancel", or "Apply":
**
**     c=TEXT             New check-in comment

**     u=TEXT             New user name
**     newclr             Apply a background color

**     clr=TEXT           New background color (only if newclr)
**     pclr               Propagate new background color (only if newclr)
**     dt=TEXT            New check-in date/time (ISO8610 format)
**     newtag             Add a new tag to the check-in
**     tagname=TEXT       Name of the new tag to be added (only if newtag)
**     newbr              Put the check-in on a new branch
**     brname=TEXT        Name of the new branch (only if newbr)
**     close              Close this check-in
**     hide               Hide this check-in
**     cNNN               Cancel tag with tagid=NNN
**
**     cancel             Cancel the edit.  Return to the check-in view
**     preview            Show a preview of the edited check-in comment
**     apply              Apply changes
*/
void ci_edit_page(void){
  int rid;
  const char *zComment;         /* Current comment on the check-in */
  const char *zNewComment;      /* Revised check-in comment */
  const char *zUser;            /* Current user for the check-in */
  const char *zNewUser;         /* Revised user */
  const char *zDate;            /* Current date of the check-in */
  const char *zNewDate;         /* Revised check-in date */
  const char *zNewColorFlag;    /* "checked" if "Change color" is checked */
  const char *zColor;           /* Current background color */
  const char *zNewColor;        /* Revised background color */
  const char *zNewTagFlag;      /* "checked" if "Add tag" is checked */
  const char *zNewTag;          /* Name of the new tag */
  const char *zNewBrFlag;       /* "checked" if "New branch" is checked */
  const char *zNewBranch;       /* Name of the new branch */
  const char *zCloseFlag;       /* "checked" if "Close" is checked */
  const char *zHideFlag;        /* "checked" if "Hide" is checked */
  int fPropagateColor;          /* True if color propagates before edit */
  int fNewPropagateColor;       /* True if color propagates after edit */
  int fHasHidden = 0;           /* True if hidden tag already set */
  int fHasClosed = 0;           /* True if closed tag already set */
  const char *zChngTime = 0;    /* Value of chngtime= query param, if any */
  char *zUuid;
  Blob comment;
  char *zBranchName = 0;
  Stmt q;

  login_check_credentials();
  if( !g.perm.Write ){ login_needed(g.anon.Write); return; }
  rid = name_to_typed_rid(P("r"), "ci");
  zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
  zComment = db_text(0, "SELECT coalesce(ecomment,comment)"
                        "  FROM event WHERE objid=%d", rid);
  if( zComment==0 ) fossil_redirect_home();
  if( P("cancel") ){
    cgi_redirectf("%R/ci/%S", zUuid);
  }
  if( g.perm.Setup ) zChngTime = P("chngtime");
  zNewComment = PD("c",zComment);
  zUser = db_text(0, "SELECT coalesce(euser,user)"
                     "  FROM event WHERE objid=%d", rid);
  if( zUser==0 ) fossil_redirect_home();
  zNewUser = PDT("u",zUser);
................................................................................
  zDate = db_text(0, "SELECT datetime(mtime)"
                     "  FROM event WHERE objid=%d", rid);
  if( zDate==0 ) fossil_redirect_home();
  zNewDate = PDT("dt",zDate);
  zColor = db_text("", "SELECT bgcolor"
                        "  FROM event WHERE objid=%d", rid);
  zNewColor = PDT("clr",zColor);



  fPropagateColor = db_int(0, "SELECT tagtype FROM tagxref"
                              " WHERE rid=%d AND tagid=%d",
                              rid, TAG_BGCOLOR)==2;
  fNewPropagateColor = P("clr")!=0 ? P("pclr")!=0 : fPropagateColor;
  zNewColorFlag = P("newclr") ? " checked" : "";
  zNewTagFlag = P("newtag") ? " checked" : "";
  zNewTag = PDT("tagname","");
  zNewBrFlag = P("newbr") ? " checked" : "";
  zNewBranch = PDT("brname","");
  zCloseFlag = P("close") ? " checked" : "";
  zHideFlag = P("hide") ? " checked" : "";
  if( P("apply") ){
................................................................................
    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
             || fossil_strcmp(zColor,zNewColor)!=0)
    ){
      add_color(zNewColor,fNewPropagateColor);

    }
    if( comment_compare(zComment,zNewComment)==0 ) add_comment(zNewComment);
    if( fossil_strcmp(zDate,zNewDate)!=0 ) add_date(zNewDate);
    if( fossil_strcmp(zUser,zNewUser)!=0 ) add_user(zNewUser);
    db_prepare(&q,
       "SELECT tag.tagid, tagname FROM tagxref, tag"
       " WHERE tagxref.rid=%d AND tagtype>0 AND tagxref.tagid=tag.tagid",
       rid
................................................................................
    }
    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);
    cgi_redirectf("%R/ci/%S", zUuid);
  }
  blob_zero(&comment);
  blob_append(&comment, zNewComment, -1);
  zUuid[10] = 0;
  style_header("Edit Check-in [%s]", zUuid);





















  if( P("preview") ){
    Blob suffix;
    int nTag = 0;
    @ <b>Preview:</b>
    @ <blockquote>
    @ <table border=0>
    if( zNewColorFlag[0] && zNewColor && zNewColor[0] ){
      @ <tr><td style="background-color: %h(zNewColor);">
    }else if( zColor[0] ){
      @ <tr><td style="background-color: %h(zColor);">
    }else{
      @ <tr><td>
    }
    @ %!W(blob_str(&comment))
    blob_zero(&suffix);
    blob_appendf(&suffix, "(user: %h", zNewUser);
    db_prepare(&q, "SELECT substr(tagname,5) FROM tagxref, tag"
................................................................................
  if( zChngTime ){
    @ <tr><th align="right" valign="top">Timestamp of this change:</th>
    @ <td valign="top">
    @   <input type="text" name="chngtime" size="20" value="%h(zChngTime)" />
    @ </td></tr>
  }

  @ <tr><th align="right" valign="top">Background&nbsp;Color:</th>
  @ <td valign="top">
  @ <div><label><input type='checkbox' name='newclr'%s(zNewColorFlag) />
  @ Change background color: \
  @ <input type='color' name='clr'\
  @ value='%s(zNewColor[0]?zNewColor:"#808080")'></label></div>
  @ <div><label>
  if( fNewPropagateColor ){
    @ <input type="checkbox" name="pclr" checked="checked" />
  }else{
    @ <input type="checkbox" name="pclr" />
  }
  @ Propagate color to descendants</label></div>
  @ </td></tr>

  @ <tr><th align="right" valign="top">Tags:</th>
  @ <td valign="top">
  @ <label><input type="checkbox" id="newtag" name="newtag"%s(zNewTagFlag) />
  @ Add the following new tag name to this check-in:</label>
  @ <input type="text" size='15' name="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)"
................................................................................
    zBranchName = db_get("main-branch", "trunk");
  }
  if( !zNewBranch || !zNewBranch[0]){
    zNewBranch = zBranchName;
  }
  @ <tr><th align="right" valign="top">Branching:</th>
  @ <td valign="top">
  @ <label><input id="newbr" type="checkbox" name="newbr" \
  @ data-branch='%h(zBranchName)'%s(zNewBrFlag) />
  @ Make this check-in the start of a new branch named:</label>
  @ <input id="brname" type="text" style="width:15;" name="brname" \
  @ value="%h(zNewBranch)" /></td></tr>

  if( !fHasHidden ){
    @ <tr><th align="right" valign="top">Branch Hiding:</th>
    @ <td valign="top">
    @ <label><input type="checkbox" id="hidebr" name="hide"%s(zHideFlag) />
    @ Hide branch
    @ <span style="font-weight:bold" id="hbranch">%h(zBranchName)</span>
    @ from the timeline starting from this check-in</label>
................................................................................
      @ </td></tr>
    }
  }
  if( zBranchName ) fossil_free(zBranchName);


  @ <tr><td colspan="2">
  @ <input type="submit" name="cancel" value="Cancel" />
  @ <input type="submit" name="preview" value="Preview" />
  if( P("preview") ){
    @ <input type="submit" name="apply" value="Apply Changes" />

  }
  @ </td></tr>
  @ </table>
  @ </div></form>
  style_load_one_js_file("ci_edit.js");
  style_footer();
}

/*
** 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.
................................................................................
    cancel_color();
  }
  if( fEditComment ){
    prepare_amend_comment(&comment, zComment, zUuid);
    zNewComment = blob_str(&comment);
  }else if( zComFile ){
    blob_zero(&comment);
    blob_read_from_file(&comment, zComFile, ExtFILE);
    blob_to_utf8_no_bom(&comment, 1);
    zNewComment = blob_str(&comment);
  }
  if( zNewComment && zNewComment[0]
      && comment_compare(zComment,zNewComment)==0 ) add_comment(zNewComment);
  if( zNewDate && zNewDate[0] && fossil_strcmp(zDate,zNewDate)!=0 ){
    if( is_datetime(zNewDate) ){

Changes to src/json.c.

1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
....
1926
1927
1928
1929
1930
1931
1932
1933

1934
1935
1936
1937
1938
1939
1940
1941
      }
      assert( 0 && "Alloc error.");
      return NULL;
    }
  }
  cson_value_free(colNamesV);
  if(warnMsg){
    json_warn( FSL_JSON_W_ROW_TO_JSON_FAILED, warnMsg );
  }
  return cson_array_value(a);
}

/*
** Works just like json_stmt_to_array_of_obj(), but each row in the
** result set is represented as an Array of values instead of an
................................................................................
  zTmp = db_get("project-name",NULL);
  cson_object_set(jo, "projectName", json_new_string(zTmp));
  free(zTmp);
  zTmp = db_get("project-description",NULL);
  cson_object_set(jo, "projectDescription", json_new_string(zTmp));
  free(zTmp);
  zTmp = NULL;
  fsize = file_size(g.zRepositoryName);

  cson_object_set(jo, "repositorySize", cson_value_new_integer((cson_int_t)fsize));

  if(full){
    n = db_int(0, "SELECT count(*) FROM blob");
    m = db_int(0, "SELECT count(*) FROM delta");
    cson_object_set(jo, "blobCount", cson_value_new_integer((cson_int_t)n));
    cson_object_set(jo, "deltaCount", cson_value_new_integer((cson_int_t)m));
    if( n>0 ){







|







 







|
>
|







1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
....
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
      }
      assert( 0 && "Alloc error.");
      return NULL;
    }
  }
  cson_value_free(colNamesV);
  if(warnMsg){
    json_warn( FSL_JSON_W_ROW_TO_JSON_FAILED, "%s", warnMsg );
  }
  return cson_array_value(a);
}

/*
** Works just like json_stmt_to_array_of_obj(), but each row in the
** result set is represented as an Array of values instead of an
................................................................................
  zTmp = db_get("project-name",NULL);
  cson_object_set(jo, "projectName", json_new_string(zTmp));
  free(zTmp);
  zTmp = db_get("project-description",NULL);
  cson_object_set(jo, "projectDescription", json_new_string(zTmp));
  free(zTmp);
  zTmp = NULL;
  fsize = file_size(g.zRepositoryName, ExtFILE);
  cson_object_set(jo, "repositorySize", 
                  cson_value_new_integer((cson_int_t)fsize));

  if(full){
    n = db_int(0, "SELECT count(*) FROM blob");
    m = db_int(0, "SELECT count(*) FROM delta");
    cson_object_set(jo, "blobCount", cson_value_new_integer((cson_int_t)n));
    cson_object_set(jo, "deltaCount", cson_value_new_integer((cson_int_t)m));
    if( n>0 ){

Changes to src/json_branch.c.

138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
...
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
      cson_array_append(list,v);
    }else if(!sawConversionError){
      sawConversionError = mprintf("Column-to-json failed @ %s:%d",
                                   __FILE__,__LINE__);
    }
  }
  if( sawConversionError ){
    json_warn(FSL_JSON_W_COL_TO_JSON_FAILED,sawConversionError);
    free(sawConversionError);
  }
  return payV;
}

/*
** Parameters for the create-branch operation.
................................................................................
    }else{
      opt.isPrivate = 0;
    }
  }

  rc = json_branch_new( &opt, &rid );
  if(rc){
    json_set_err(rc, opt.rcErrMsg);
    goto error;
  }
  assert(0 != rid);
  payV = cson_value_new_object();
  pay = cson_value_get_object(payV);

  cson_object_set(pay,"name",json_new_string(opt.zName));







|







 







|







138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
...
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
      cson_array_append(list,v);
    }else if(!sawConversionError){
      sawConversionError = mprintf("Column-to-json failed @ %s:%d",
                                   __FILE__,__LINE__);
    }
  }
  if( sawConversionError ){
    json_warn(FSL_JSON_W_COL_TO_JSON_FAILED,"%s",sawConversionError);
    free(sawConversionError);
  }
  return payV;
}

/*
** Parameters for the create-branch operation.
................................................................................
    }else{
      opt.isPrivate = 0;
    }
  }

  rc = json_branch_new( &opt, &rid );
  if(rc){
    json_set_err(rc, "%s", opt.rcErrMsg);
    goto error;
  }
  assert(0 != rid);
  payV = cson_value_new_object();
  pay = cson_value_get_object(payV);

  cson_object_set(pay,"name",json_new_string(opt.zName));

Changes to src/json_dir.c.

23
24
25
26
27
28
29

30
31
32
33

34
35
36
37
38
39
40
#include "json_detail.h"
#endif

static cson_value * json_page_dir_list();
/*
** Mapping of /json/wiki/XXX commands/paths to callbacks.
*/

static const JsonPageDef JsonPageDefs_Dir[] = {
/* Last entry MUST have a NULL name. */
{NULL,NULL,0}
};


#if 0 /* TODO: Not used? */
static char const * json_dir_path_extra(){
  static char const * zP = NULL;
  if( !zP ){
    zP = g.zExtra;
    while(zP && *zP && ('/'==*zP)){







>




>







23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include "json_detail.h"
#endif

static cson_value * json_page_dir_list();
/*
** 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 * zP = NULL;
  if( !zP ){
    zP = g.zExtra;
    while(zP && *zP && ('/'==*zP)){

Changes to src/json_status.c.

112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
    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_wd_isfile_or_link(zFullName) ){
      if( file_access(zFullName, F_OK)==0 ){
        zStatus = "notAFile";
        ++nErr;
      }else{
        zStatus = "missing";
        ++nErr;
      }







|







112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
    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;
      }

Changes to 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
...
103
104
105
106
107
108
109
110
111
112
113
114
115
116

117
118
119
120
121
122
123
124
125
126


127
128
129
130
131
132
133
...
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
...
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
...
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
...
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
...
440
441
442
443
444
445
446












447
448
449
450
451
452
453
...
489
490
491
492
493
494
495
























496
497
498
499
500
501
502
...
519
520
521
522
523
524
525


526
527
528
529
530
531
532
...
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578



579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
...
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
...
806
807
808
809
810
811
812








813
814
815
816
817
818
819
...
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
...
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
....
1013
1014
1015
1016
1017
1018
1019








1020
1021
1022
1023
1024
1025
1026
....
1105
1106
1107
1108
1109
1110
1111

1112
1113
1114


1115

1116
1117
1118
1119
1120
1121
1122
/* linenoise.c -- VERSION 1.0
 *
 * 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-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:
................................................................................
 *    Sequence: ESC [ 2 J
 *    Effect: clear the whole screen
 *
 */

#include <termios.h>
#include <unistd.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>

#include <sys/types.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include "linenoise.h"
#include "sqlite3.h"

#define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100
#define LINENOISE_MAX_LINE 4096
static const char *unsupported_term[] = {"dumb","cons25","emacs",NULL};
static linenoiseCompletionCallback *completionCallback = 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 void linenoiseAtExit(void);
int linenoiseHistoryAdd(const char *line);
static void refreshLine(struct linenoiseState *l);

/* Debugging macro. */
#if 0
FILE *lndebug_fp = NULL;
#define lndebug(fmt, arg1) \
    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, ", " fmt, arg1); \
        fflush(lndebug_fp); \
    } while (0)
#else
#define lndebug(fmt, arg1)
#endif

/* =========================== C89 compatibility ============================ */

/* snprintf() is not C89, but sqlite3_vsnprintf() can be adapted. */
static int linenoiseSnprintf(char *str, size_t size, const char *format, ...) {
    va_list ap;
    int result;

    va_start(ap,format);
    result = (int)strlen(sqlite3_vsnprintf((int)size,str,format,ap));
    va_end(ap);

    return result;
}
#undef snprintf
#define snprintf linenoiseSnprintf

/* strdup() is technically not standard C89 despite being in POSIX. */
static char *linenoiseStrdup(const char *s) {
    size_t size = strlen(s)+1;
    char *result = malloc(size);

    if (result) memcpy(result,s,size);

    return result;
}
#undef strdup
#define strdup linenoiseStrdup

/* strcasecmp() is not standard C89.  SQLite offers a direct replacement. */
#undef strcasecmp
#define strcasecmp sqlite3_stricmp

/* ======================= Low level terminal handling ====================== */

/* Set if to use or not the multi line mode. */
void linenoiseSetMultiLine(int ml) {
    mlmode = ml;
}

................................................................................

    if (term == NULL) return 0;
    for (j = 0; unsupported_term[j]; j++)
        if (!strcasecmp(term,unsupported_term[j])) return 1;
    return 0;
}

/* Raw mode */
static int enableRawMode(int fd) {
    struct termios raw;

    if (!isatty(STDIN_FILENO)) goto fatal;
    if (!atexit_registered) {
        atexit(linenoiseAtExit);
        atexit_registered = 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) {
#if !defined(__sun__)
    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. */
................................................................................
        }
        return cols;
    } else {
        return ws.ws_col;
    }

failed:
#endif
    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. */
................................................................................
    return c; /* Return last read character */
}

/* Register a callback function to be called for tab-completion. */
void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn) {
    completionCallback = 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);
................................................................................
    ab->b = new;
    ab->len += len;
}

static void abFree(struct abuf *ab) {
    free(ab->b);
}

























/* 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];
................................................................................
    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);


    /* 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. */
................................................................................
    /* 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", 0);
        snprintf(seq,64,"\r\x1b[0K\x1b[1A");
        abAppend(&ab,seq,strlen(seq));
    }

    /* Clean the top line. */
    lndebug("clear", 0);
    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);




    /* 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>", 0);
        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", 0);
    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
................................................................................
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) /* || mlmode */) {
                /* Avoid a full update of the line in the
                 * trivial case. */
                if (write(l->ofd,&c,1) == -1) return -1;
            } else {
                refreshLine(l);
            }
        } else {
................................................................................
        }

        switch(c) {
        case ENTER:    /* enter */
            history_len--;
            free(history[history_len]);
            if (mlmode) linenoiseEditMoveEnd(&l);








            return (int)l.len;
        case CTRL_C:     /* ctrl-c */
            errno = EAGAIN;
            return -1;
        case BACKSPACE:   /* backspace */
        case 8:     /* ctrl-h */
            linenoiseEditBackspace(&l);
................................................................................
        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((int)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
................................................................................
static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) {
    int count;

    if (buflen == 0) {
        errno = EINVAL;
        return -1;
    }
    if (!isatty(STDIN_FILENO)) {
        /* Not a tty: read from file / pipe. */
        if (fgets(buf, buflen, stdin) == NULL) return -1;
        count = strlen(buf);
        if (count && buf[count-1] == '\n') {
            count--;
            buf[count] = '\0';
        }
    } else {
        /* Interactive editing. */
        if (enableRawMode(STDIN_FILENO) == -1) return -1;
        count = linenoiseEdit(STDIN_FILENO, STDOUT_FILENO, buf, buflen, prompt);
        disableRawMode(STDIN_FILENO);
        printf("\n");

    }


















    return count;
}

















/* 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 (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')) {
................................................................................
        return strdup(buf);
    } else {
        count = linenoiseRaw(buf,LINENOISE_MAX_LINE,prompt);
        if (count == -1) return NULL;
        return strdup(buf);
    }
}









/* ================================ 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) {
................................................................................
        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) {

    FILE *fp = fopen(filename,"w");
    int j;



    if (fp == NULL) return -1;

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










|







 







<






>




<



|

>
>







 







|








|



|


<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







 







|







 







<







 







<







 







>
>
>
>
>
>
>
>
>
>
>
>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>
>







 







|






|





|







>
>
>






|









|



|






|






|







 







|







 







>
>
>
>
>
>
>
>







 







|







 







<
<
<
<
<
<
<
|
<
<
|
|
|
|
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>










>
>
>
>
|







 







>
>
>
>
>
>
>
>







 







>
|


>
>

>









1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
...
101
102
103
104
105
106
107

108
109
110
111
112
113
114
115
116
117
118

119
120
121
122
123
124
125
126
127
128
129
130
131
132
...
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
































198
199
200
201
202
203
204
...
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
...
280
281
282
283
284
285
286

287
288
289
290
291
292
293
...
309
310
311
312
313
314
315

316
317
318
319
320
321
322
...
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
...
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
...
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
...
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
...
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
...
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
...
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
...
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
....
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
....
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178


/* 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:
................................................................................
 *    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 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;
}

................................................................................

    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 (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. */
................................................................................
        }
        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. */
................................................................................
    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);
................................................................................
    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];
................................................................................
    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. */
................................................................................
    /* 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
................................................................................
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 {
................................................................................
        }

        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);
................................................................................
        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
................................................................................
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')) {
................................................................................
        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) {
................................................................................
        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

Changes to src/linenoise.h.

45
46
47
48
49
50
51


52


53
54
55

56
57
58
59
60
61
62
63
64
65
66
67
68

typedef struct linenoiseCompletions {
  size_t len;
  char **cvec;
} linenoiseCompletions;

typedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *);


void linenoiseSetCompletionCallback(linenoiseCompletionCallback *);


void linenoiseAddCompletion(linenoiseCompletions *, const char *);

char *linenoise(const char *prompt);

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







>
>

>
>



>













45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73

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/login.c.

630
631
632
633
634
635
636






637

638
639
640
641
642
643
644
...
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
...
701
702
703
704
705
706
707
708
709

710
711
712
713
714
715
716
...
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
....
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
....
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
    }else if( anonFlag ){
      @ <p>Login as <b>anonymous</b> or any named user
      @ to access page <b>%h(zAbbrev)</b>.
    }else{
      @ <p>Login as a named user to access page <b>%h(zAbbrev)</b>.
    }
  }






  form_begin(0, "%R/login");

  if( zGoto ){
    @ <input type="hidden" name="g" value="%h(zGoto)" />
  }else if( zReferer && strncmp(g.zBaseURL, zReferer, strlen(g.zBaseURL))==0 ){
    @ <input type="hidden" name="g" value="%h(zReferer)" />
  }
  if( anonFlag ){
    @ <input type="hidden" name="anon" value="1" />
................................................................................
  if( g.zLogin==0 && (anonFlag || zGoto==0) ){
    zAnonPw = db_text(0, "SELECT pw FROM user"
                         " WHERE login='anonymous'"
                         "   AND cap!=''");
  }
  @ <tr>
  @   <td></td>
  @   <td><input type="submit" name="in" value="Login"
  @        onClick="chngAction(this.form)" /></td>
  @ </tr>
  @ </table>
  @ <script>
  @   gebi('u').focus()
  @   function chngAction(form){
  if( g.sslNotAvailable==0
   && strncmp(g.zBaseURL,"https:",6)!=0
   && db_get_boolean("https-login",0)
  ){
     char *zSSL = mprintf("https:%s", &g.zBaseURL[5]);
     @  if( form.u.value!="anonymous" ){
     @     form.action = "%h(zSSL)/login";
     @  }
  }
  @ }
  @ </script>
  @ <p>Pressing the Login button grants permission to store a cookie.</p>
  if( db_get_boolean("self-register", 0) ){
    @ <p>If you do not have an account, you can
    @ <a href="%R/register?g=%T(P("G"))">create one</a>.
  }
  if( zAnonPw ){
    unsigned int uSeed = captcha_seed();
................................................................................
    @ <p><input type="hidden" name="cs" value="%u(uSeed)" />
    @ Visitors may enter <b>anonymous</b> as the user-ID with
    @ the 8-character hexadecimal password shown below:</p>
    @ <div class="captcha"><table class="captcha"><tr><td><pre>
    @ %h(zCaptcha)
    @ </pre></td></tr></table>
    if( bAutoCaptcha ) {
        @ <input type="button" value="Fill out captcha"
        @  onclick="gebi('u').value='anonymous'; gebi('p').value='%s(zDecoded)';" />

    }
    @ </div>
    free(zCaptcha);
  }
  @ </form>
  if( g.zLogin && g.perm.Password ){
    @ <hr />
................................................................................
  ** local login is disabled and if we are using HTTP and not HTTPS,
  ** then there is no need to check user credentials.
  **
  ** This feature allows the "fossil ui" command to give the user
  ** full access rights without having to log in.
  */
  zRemoteAddr = ipPrefix(zIpAddr = PD("REMOTE_ADDR","nil"));
  if( ( fossil_strcmp(zIpAddr, "127.0.0.1")==0 ||
        (g.fSshClient & CGI_SSH_CLIENT)!=0 )
   && g.useLocalauth
   && db_get_int("localauth",0)==0
   && P("HTTPS")==0
  ){
    if( g.localOpen ) zLogin = db_lget("default-user",0);
    if( zLogin!=0 ){
      uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zLogin);
................................................................................
    " WHERE name GLOB 'peer-repo-*'"
    "   AND name <> 'peer-repo-%q'"
    " ORDER BY +value",
    zSelfCode
  );
  while( db_step(&q)==SQLITE_ROW ){
    const char *zRepoName = db_column_text(&q, 1);
    if( file_size(zRepoName)<0 ){
      /* Silently remove non-existent repositories from the login group. */
      const char *zLabel = db_column_text(&q, 0);
      db_multi_exec(
         "DELETE FROM config WHERE name GLOB 'peer-*-%q'",
         &zLabel[10]
      );
      continue;
................................................................................
  /* Make sure we are not trying to join ourselves */
  if( fossil_strcmp(zRepo, zSelfRepo)==0 ){
    *pzErrMsg = mprintf("The \"other\" repository is the same as this one.");
    return;
  }

  /* Make sure the other repository is a valid Fossil database */
  if( file_size(zRepo)<0 ){
    *pzErrMsg = mprintf("repository file \"%s\" does not exist", zRepo);
    return;
  }
  rc = sqlite3_open_v2(
       zRepo, &pOther,
       SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
       g.zVfsName







>
>
>
>
>
>
|
>







 







|
<


<
<
<
<
<
<
<
<
<
<
<
<
<
<







 







|
|
>







 







|
|







 







|







 







|







630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
...
672
673
674
675
676
677
678
679

680
681














682
683
684
685
686
687
688
...
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
...
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
....
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
....
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
    }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>.
    }
  }
  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)" />
  }else if( zReferer && strncmp(g.zBaseURL, zReferer, strlen(g.zBaseURL))==0 ){
    @ <input type="hidden" name="g" value="%h(zReferer)" />
  }
  if( anonFlag ){
    @ <input type="hidden" name="anon" value="1" />
................................................................................
  if( g.zLogin==0 && (anonFlag || zGoto==0) ){
    zAnonPw = db_text(0, "SELECT pw FROM user"
                         " WHERE login='anonymous'"
                         "   AND cap!=''");
  }
  @ <tr>
  @   <td></td>
  @   <td><input type="submit" name="in" value="Login">

  @ </tr>
  @ </table>














  @ <p>Pressing the Login button grants permission to store a cookie.</p>
  if( db_get_boolean("self-register", 0) ){
    @ <p>If you do not have an account, you can
    @ <a href="%R/register?g=%T(P("G"))">create one</a>.
  }
  if( zAnonPw ){
    unsigned int uSeed = captcha_seed();
................................................................................
    @ <p><input type="hidden" name="cs" value="%u(uSeed)" />
    @ Visitors may enter <b>anonymous</b> as the user-ID with
    @ the 8-character hexadecimal password shown below:</p>
    @ <div class="captcha"><table class="captcha"><tr><td><pre>
    @ %h(zCaptcha)
    @ </pre></td></tr></table>
    if( bAutoCaptcha ) {
       @ <input type="button" value="Fill out captcha" id='autofillButton' \
       @ data-af='%s(zDecoded)' />
       style_load_one_js_file("login.js");
    }
    @ </div>
    free(zCaptcha);
  }
  @ </form>
  if( g.zLogin && g.perm.Password ){
    @ <hr />
................................................................................
  ** local login is disabled and if we are using HTTP and not HTTPS,
  ** then there is no need to check user credentials.
  **
  ** This feature allows the "fossil ui" command to give the user
  ** full access rights without having to log in.
  */
  zRemoteAddr = ipPrefix(zIpAddr = PD("REMOTE_ADDR","nil"));
  if( ( cgi_is_loopback(zIpAddr)
       || (g.fSshClient & CGI_SSH_CLIENT)!=0 )
   && g.useLocalauth
   && db_get_int("localauth",0)==0
   && P("HTTPS")==0
  ){
    if( g.localOpen ) zLogin = db_lget("default-user",0);
    if( zLogin!=0 ){
      uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zLogin);
................................................................................
    " WHERE name GLOB 'peer-repo-*'"
    "   AND name <> 'peer-repo-%q'"
    " ORDER BY +value",
    zSelfCode
  );
  while( db_step(&q)==SQLITE_ROW ){
    const char *zRepoName = db_column_text(&q, 1);
    if( file_size(zRepoName, ExtFILE)<0 ){
      /* Silently remove non-existent repositories from the login group. */
      const char *zLabel = db_column_text(&q, 0);
      db_multi_exec(
         "DELETE FROM config WHERE name GLOB 'peer-*-%q'",
         &zLabel[10]
      );
      continue;
................................................................................
  /* Make sure we are not trying to join ourselves */
  if( fossil_strcmp(zRepo, zSelfRepo)==0 ){
    *pzErrMsg = mprintf("The \"other\" repository is the same as this one.");
    return;
  }

  /* Make sure the other repository is a valid Fossil database */
  if( file_size(zRepo, ExtFILE)<0 ){
    *pzErrMsg = mprintf("repository file \"%s\" does not exist", zRepo);
    return;
  }
  rc = sqlite3_open_v2(
       zRepo, &pOther,
       SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
       g.zVfsName

Added src/login.js.













>
>
>
>
>
>
1
2
3
4
5
6
/* Javascript code to handle button actions on the login page */
var autofillButton = document.getElementById('autofillButton');
autofillButton.onclick = function(){
  document.getElementById('u').value = 'anonymous';
  document.getElementById('p').value = autofillButton.getAttribute('data-af');
};

Changes to src/lookslike.c.

420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
  const char *zCount = find_option("limit","n",1);
  int nRepeat = 1;

  if( g.argc!=3 ) usage("FILENAME");
  if( zCount ){
    nRepeat = atoi(zCount);
  }
  blob_read_from_file(&blob, g.argv[2]);
  while( --nRepeat >= 0 ){
    fUtf8 = starts_with_utf8_bom(&blob, 0);
    fUtf16 = starts_with_utf16_bom(&blob, 0, &bRevUtf16);
    if( fForceUtf8 ){
      fUnicode = 0;
    }else{
      fUnicode = could_be_utf16(&blob, 0) || fForceUtf16;







|







420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
  const char *zCount = find_option("limit","n",1);
  int nRepeat = 1;

  if( g.argc!=3 ) usage("FILENAME");
  if( zCount ){
    nRepeat = atoi(zCount);
  }
  blob_read_from_file(&blob, g.argv[2], ExtFILE);
  while( --nRepeat >= 0 ){
    fUtf8 = starts_with_utf8_bom(&blob, 0);
    fUtf16 = starts_with_utf16_bom(&blob, 0, &bRevUtf16);
    if( fForceUtf8 ){
      fUnicode = 0;
    }else{
      fUnicode = could_be_utf16(&blob, 0) || fForceUtf16;

Changes to src/main.c.

229
230
231
232
233
234
235
236
237
238
239


240
241
242
243
244
245
246
...
516
517
518
519
520
521
522





523
524
525
526
527
528
529
...
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
...
992
993
994
995
996
997
998



999
1000
1001
1002
1003
1004
1005
....
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
....
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
....
1289
1290
1291
1292
1293
1294
1295



1296
1297
1298
1299
1300
1301
1302
....
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
....
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
....
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
....
1537
1538
1539
1540
1541
1542
1543
1544
1545





















1546
1547
1548
1549
1550
1551
1552
1553




1554
1555
1556
1557
1558
1559
1560
....
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
....
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
....
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
....
2288
2289
2290
2291
2292
2293
2294









2295
2296
2297
2298
2299
2300
2301
....
2340
2341
2342
2343
2344
2345
2346


2347
2348
2349
2350
2351
2352
2353
....
2363
2364
2365
2366
2367
2368
2369

2370
2371
2372
2373
2374
2375
2376
....
2390
2391
2392
2393
2394
2395
2396

2397
2398
2399
2400
2401
2402
2403
....
2446
2447
2448
2449
2450
2451
2452

2453
2454
2455



2456

2457

2458
2459
2460
2461
2462
2463
2464
....
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
....
2513
2514
2515
2516
2517
2518
2519
2520



2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
  /* Storage for the aux() and/or option() SQL function arguments */
  int nAux;                    /* Number of distinct aux() or option() values */
  const char *azAuxName[MX_AUX]; /* Name of each aux() or option() value */
  char *azAuxParam[MX_AUX];      /* Param of each aux() or option() value */
  const char *azAuxVal[MX_AUX];  /* Value of each aux() or option() value */
  const char **azAuxOpt[MX_AUX]; /* Options of each option() value */
  int anAuxCols[MX_AUX];         /* Number of columns for option() values */

  int allowSymlinks;             /* Cached "allow-symlinks" option */

  int mainTimerId;               /* Set to fossil_timer_start() */


#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
................................................................................
#ifdef __APPLE__
  /* Disable the file alias warning on apple products because Time Machine
  ** creates lots of aliases and the warning alarms people. */
  if( iCode==SQLITE_WARNING ) return;
#endif
  if( iCode==SQLITE_SCHEMA ) return;
  if( g.dbIgnoreErrors ) return;





  fossil_warning("%s: %s", fossil_sqlite_return_code_name(iCode), zErrmsg);
}

/*
** This function attempts to find command line options known to contain
** bitwise flags and initializes the associated global variables.  After
** this function executes, all global variables (i.e. in the "g" struct)
................................................................................
#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_OMIT_DELTA_CKSUM_TEST)
  blob_append(pOut, "FOSSIL_OMIT_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
................................................................................
  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(USE_SEE)
  blob_append(pOut, "USE_SEE\n", -1);
#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(),
................................................................................
    if( g.db!=0 ){
      db_close(1);
    }

    file_canonical_name(zRepo, &dir, 0);
    zDir = blob_str(&dir);
    if( !noJail ){
      if( file_isdir(zDir)==1 ){
        if( file_chdir(zDir, 1) ){
          fossil_fatal("unable to chroot into %s", zDir);
        }
        g.fJail = 1;
        zRepo = "/";
      }else{
        for(i=strlen(zDir)-1; i>0 && zDir[i]!='/'; i--){}
................................................................................
      fossil_fatal("cannot stat() repository: %s", zRepo);
    }
    i = setgid(sStat.st_gid);
    i = i || setuid(sStat.st_uid);
    if(i){
      fossil_fatal("setgid/uid() failed with errno %d", errno);
    }
    if( g.db==0 && file_isfile(zRepo) ){
      db_open_repository(zRepo);
    }
  }
#endif
  return zRepo;
}

................................................................................
      if( nName<7 ) continue;
      zUrl = sqlite3_mprintf("%.*s", nName-7, zName);
      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. */
        @ <li>%h(zName)</li>



      } else if( allRepo && sqlite3_strglob("[a-zA-Z]:/?*", zName)!=0 ){
        @ <li><a href="%R/%T(zUrl)/home" target="_blank">/%h(zName)</a></li>
      }else{
        @ <li><a href="%R/%T(zUrl)/home" target="_blank">%h(zName)</a></li>
      }
      sqlite3_free(zUrl);
    }
................................................................................
      ** does not exist, szFile will become -1.  If the file does exist,
      ** then szFile will become zero (for an empty file) or positive.
      ** Special case:  Assume any file with a basename of ".fossil" does
      ** not exist.
      */
      zCleanRepo = file_cleanup_fullpath(zRepo);
      if( szFile==0 && sqlite3_strglob("*/.fossil",zRepo)!=0 ){
        szFile = file_size(zCleanRepo);
        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( szFile<0 && i>0 ){
        const char *zMimetype;
        assert( fossil_strcmp(&zRepo[j], ".fossil")==0 );
        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)==1 ){
          fossil_free(zToFree);
          i++;
          continue;
        }

        /* If zRepo is the name of an ordinary file that matches the
        ** "--file GLOB" pattern, then the CGI reply is the text of
................................................................................
        ** to be returned this way, to prevent complete repositories from
        ** being delivered accidently.  This is not intended to be a
        ** general-purpose web server.  The "--file GLOB" mechanism is
        ** designed to allow the delivery of a few static images or HTML
        ** pages.
        */
        if( pFileGlob!=0
         && file_isfile(zCleanRepo)
         && glob_match(pFileGlob, file_cleanup_fullpath(zRepo))
         && sqlite3_strglob("*.fossil*",zRepo)!=0
         && (zMimetype = mimetype_from_name(zRepo))!=0
         && strcmp(zMimetype, "application/x-fossil-artifact")!=0
        ){
          Blob content;
          blob_read_from_file(&content, file_cleanup_fullpath(zRepo));
          cgi_set_content_type(zMimetype);
          cgi_set_content(&content);
          cgi_reply();
          return;
        }
        zRepo[j] = '.';
      }
................................................................................
        @ <!-- translated g.zBaseURL: "%h(g.zBaseURL)" -->
        fprintf(stderr, "# translated g.zBaseURL = [%s]\n", g.zBaseURL);
      }
    }
  }

  /* At this point, the appropriate repository database file will have
  ** been opened.  Use the first element of PATH_INFO as the page name
  ** and deliver the appropriate page back to the user.





















  */
  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";
  }




  set_base_url(0);
  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
................................................................................
    zFile = g.argv[1];
  }
  g.httpOut = stdout;
  g.httpIn = stdin;
  fossil_binary_mode(g.httpOut);
  fossil_binary_mode(g.httpIn);
  g.cgiOutput = 1;
  blob_read_from_file(&config, zFile);
  while( blob_line(&config, &line) ){
    if( !blob_token(&line, &key) ) continue;
    if( blob_buffer(&key)[0]=='#' ) continue;
    if( blob_eq(&key, "repository:") && blob_tail(&line, &value) ){
      /* repository: FILENAME
      **
      ** The name of the Fossil repository to be served via CGI.  Most
................................................................................

/*
** If g.argv[arg] exists then it is either the name of a repository
** that will be used by a server, or else it is a directory that
** contains multiple repositories that can be served.  If g.argv[arg]
** is a directory, the repositories it contains must be named
** "*.fossil".  If g.argv[arg] does not exist, then we must be within
** an open check-out and the repository serve is the repository of
** that check-out.
**
** Open the repository to be served if it is known.  If g.argv[arg] is
** a directory full of repositories, then set g.zRepositoryName to
** the name of that directory and the specific repository will be
** opened later by process_one_web_page() based on the content of
** the PATH_INFO variable.
................................................................................
** does not already exist. Always use "auto" hash-policy in this case.
*/
static void find_server_repository(int arg, int fCreate){
  if( g.argc<=arg ){
    db_must_be_within_tree();
  }else{
    const char *zRepo = g.argv[arg];
    int isDir = file_isdir(zRepo);
    if( isDir==1 ){
      g.zRepositoryName = mprintf("%s", zRepo);
      file_simplify_name(g.zRepositoryName, -1, 0);
    }else{
      if( isDir==0 && fCreate ){
        const char *zPassword;
        db_create_repository(zRepo);
................................................................................
    if( bExists==0 ) return 1;
    zPath += i;
  }
  return 0;
}
#endif
#endif










/*
** COMMAND: server*
** COMMAND: ui
**
** Usage: %fossil server ?OPTIONS? ?REPOSITORY?
**    or: %fossil ui ?OPTIONS? ?REPOSITORY?
................................................................................
**   --baseurl URL       Use URL as the base (useful for reverse proxies)
**   --create            Create a new REPOSITORY if it does not already exist
**   --page PAGE         Start "ui" on PAGE.  ex: --page "timeline?y=ci"
**   --files GLOBLIST    Comma-separated list of glob patterns for static files
**   --localauth         enable automatic login for requests from localhost
**   --localhost         listen on 127.0.0.1 only (always true for "ui")
**   --https             signal a request coming in via https


**   --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
**   --th-trace          trace TH1 execution (for debugging purposes)
**   --repolist          If REPOSITORY is dir, URL "/" lists repos.
**   --scgi              Accept SCGI rather than HTTP
................................................................................
  const char *zBrowser;     /* Name of web browser program */
  char *zBrowserCmd = 0;    /* Command to launch the web browser */
  int isUiCmd;              /* True if command is "ui", not "server' */
  const char *zNotFound;    /* The --notfound option or NULL */
  int flags = 0;            /* Server flags */
#if !defined(_WIN32)
  int noJail;               /* Do not enter the chroot jail */

#endif
  int allowRepoList;         /* List repositories on URL "/" */
  const char *zAltBase;      /* Argument to the --baseurl option */
  const char *zFileGlob;     /* Static content must match this */
  char *zIpAddr = 0;         /* Bind to this IP address */
  int fCreate = 0;           /* The --create flag */
  const char *zInitPage = 0; /* Start on this page.  --page option */
................................................................................
    zFileGlob = z;
  }else{
    zFileGlob = find_option("files",0,1);
  }
  skin_override();
#if !defined(_WIN32)
  noJail = find_option("nojail",0,0)!=0;

#endif
  g.useLocalauth = find_option("localauth", 0, 0)!=0;
  Th_InitTraceLog();
  zPort = find_option("port", "P", 1);
  isUiCmd = g.argv[1][0]=='u';
  if( isUiCmd ){
    zInitPage = find_option("page", 0, 1);
................................................................................
    if( isUiCmd && g.localOpen ){
      zInitPage = "timeline?c=current";
    }else{
      zInitPage = "";
    }
  }
  if( zPort ){

    int i;
    for(i=strlen(zPort)-1; i>=0 && zPort[i]!=':'; i--){}
    if( i>0 ){



      zIpAddr = mprintf("%.*s", i, zPort);

      zPort += i+1;

    }
    iPort = mxPort = atoi(zPort);
  }else{
    iPort = db_get_int("http-port", 8080);
    mxPort = iPort+100;
  }
#if !defined(_WIN32)
................................................................................
          break;
        }
      }
    }
#else
    zBrowser = db_get("web-browser", "open");
#endif
    if( zIpAddr ){



      zBrowserCmd = mprintf("%s http://%s:%%d/%s &",
                            zBrowser, zIpAddr, zInitPage);
    }else{
      zBrowserCmd = mprintf("%s http://localhost:%%d/%s &",
                            zBrowser, zInitPage);
    }
  }
  if( g.repositoryOpen ) flags |= HTTP_SERVER_HAD_REPOSITORY;
  if( g.localOpen ) flags |= HTTP_SERVER_HAD_CHECKOUT;
  db_close(1);
  if( cgi_http_server(iPort, mxPort, zBrowserCmd, zIpAddr, flags) ){
    fossil_fatal("unable to listen on TCP socket %d", iPort);
  }




  g.httpIn = stdin;
  g.httpOut = stdout;
  if( g.fHttpTrace || g.fSqlTrace ){
    fprintf(stderr, "====== SERVER pid %d =======\n", getpid());
  }
  g.cgiOutput = 1;
  find_server_repository(2, 0);
................................................................................
    cgi_handle_http_request(0);
  }
  process_one_web_page(zNotFound, glob_create(zFileGlob), allowRepoList);
#else
  /* Win32 implementation */
  if( isUiCmd ){
    zBrowser = db_get("web-browser", "start");
    if( zIpAddr ){



      zBrowserCmd = mprintf("%s http://%s:%%d/%s &",
                            zBrowser, zIpAddr, zInitPage);
    }else{
      zBrowserCmd = mprintf("%s http://localhost:%%d/%s &",
                            zBrowser, zInitPage);
    }
  }
  if( g.repositoryOpen ) flags |= HTTP_SERVER_HAD_REPOSITORY;
  if( g.localOpen ) flags |= HTTP_SERVER_HAD_CHECKOUT;
  db_close(1);
  if( allowRepoList ){
    flags |= HTTP_SERVER_REPOLIST;







<

<

>
>







 







>
>
>
>
>







 







|
|







 







>
>
>







 







|







 







|







 







>
>
>







 







|







 







|







 







|






|







 







|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>








>
>
>
>







 







|







 







|







 







|







 







>
>
>
>
>
>
>
>
>







 







>
>







 







>







 







>







 







>
|
|
|
>
>
>
|
>
|
>







 







|
>
>
>
|


|
|








>
>
>
>







 







|
>
>
>
|


|
|







229
230
231
232
233
234
235

236

237
238
239
240
241
242
243
244
245
246
...
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
...
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
...
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
....
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
....
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
....
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
....
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
....
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
....
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
....
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
....
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
....
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
....
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
....
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
....
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
....
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
....
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
....
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
....
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
....
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
  /* Storage for the aux() and/or option() SQL function arguments */
  int nAux;                    /* Number of distinct aux() or option() values */
  const char *azAuxName[MX_AUX]; /* Name of each aux() or option() value */
  char *azAuxParam[MX_AUX];      /* Param of each aux() or option() value */
  const char *azAuxVal[MX_AUX];  /* Value of each aux() or option() value */
  const char **azAuxOpt[MX_AUX]; /* Options of each option() value */
  int anAuxCols[MX_AUX];         /* Number of columns for option() values */

  int allowSymlinks;             /* Cached "allow-symlinks" option */

  int mainTimerId;               /* Set to fossil_timer_start() */
  int nPendingRequest;           /* # of HTTP requests in "fossil server" */
  int nRequest;                  /* Total # of HTTP request */
#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
................................................................................
#ifdef __APPLE__
  /* Disable the file alias warning on apple products because Time Machine
  ** creates lots of aliases and the warning alarms people. */
  if( iCode==SQLITE_WARNING ) return;
#endif
  if( iCode==SQLITE_SCHEMA ) return;
  if( g.dbIgnoreErrors ) return;
#ifdef SQLITE_READONLY_DIRECTORY
  if( iCode==SQLITE_READONLY_DIRECTORY ){
    zErrmsg = "database is in a read-only directory";
  }
#endif
  fossil_warning("%s: %s", fossil_sqlite_return_code_name(iCode), zErrmsg);
}

/*
** This function attempts to find command line options known to contain
** bitwise flags and initializes the associated global variables.  After
** this function executes, all global variables (i.e. in the "g" struct)
................................................................................
#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
................................................................................
  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_SEE)
  blob_append(pOut, "USE_SEE\n", -1);
#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(),
................................................................................
    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( file_chdir(zDir, 1) ){
          fossil_fatal("unable to chroot into %s", zDir);
        }
        g.fJail = 1;
        zRepo = "/";
      }else{
        for(i=strlen(zDir)-1; i>0 && zDir[i]!='/'; i--){}
................................................................................
      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;
}

................................................................................
      if( nName<7 ) continue;
      zUrl = sqlite3_mprintf("%.*s", nName-7, zName);
      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. */
        @ <li>%h(zName)</li>
      } else if( sqlite3_strglob("*/.*", zName)==0 ){
        /* Do not show hidden repos */
        @ <li>%h(zName) (hidden)</li>
      } else if( allRepo && sqlite3_strglob("[a-zA-Z]:/?*", zName)!=0 ){
        @ <li><a href="%R/%T(zUrl)/home" target="_blank">/%h(zName)</a></li>
      }else{
        @ <li><a href="%R/%T(zUrl)/home" target="_blank">%h(zName)</a></li>
      }
      sqlite3_free(zUrl);
    }
................................................................................
      ** does not exist, szFile will become -1.  If the file does exist,
      ** then szFile will become zero (for an empty file) or positive.
      ** Special case:  Assume any file with a basename of ".fossil" does
      ** not exist.
      */
      zCleanRepo = file_cleanup_fullpath(zRepo);
      if( szFile==0 && sqlite3_strglob("*/.fossil",zRepo)!=0 ){
        szFile = file_size(zCleanRepo, ExtFILE);
        if( g.fHttpTrace ){
          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( szFile<0 && i>0 ){
        const char *zMimetype;
        assert( fossil_strcmp(&zRepo[j], ".fossil")==0 );
        zRepo[j] = 0;  /* Remove the ".fossil" suffix */

        /* The PATH_INFO prefix seen so far is a valid directory.
        ** Continue the loop with the next element of the PATH_INFO */
        if( zPathInfo[i]=='/' && file_isdir(zCleanRepo, ExtFILE)==1 ){
          fossil_free(zToFree);
          i++;
          continue;
        }

        /* If zRepo is the name of an ordinary file that matches the
        ** "--file GLOB" pattern, then the CGI reply is the text of
................................................................................
        ** to be returned this way, to prevent complete repositories from
        ** being delivered accidently.  This is not intended to be a
        ** general-purpose web server.  The "--file GLOB" mechanism is
        ** designed to allow the delivery of a few static images or HTML
        ** pages.
        */
        if( pFileGlob!=0
         && file_isfile(zCleanRepo, ExtFILE)
         && glob_match(pFileGlob, file_cleanup_fullpath(zRepo))
         && sqlite3_strglob("*.fossil*",zRepo)!=0
         && (zMimetype = mimetype_from_name(zRepo))!=0
         && strcmp(zMimetype, "application/x-fossil-artifact")!=0
        ){
          Blob content;
          blob_read_from_file(&content, file_cleanup_fullpath(zRepo), ExtFILE);
          cgi_set_content_type(zMimetype);
          cgi_set_content(&content);
          cgi_reply();
          return;
        }
        zRepo[j] = '.';
      }
................................................................................
        @ <!-- translated g.zBaseURL: "%h(g.zBaseURL)" -->
        fprintf(stderr, "# translated g.zBaseURL = [%s]\n", g.zBaseURL);
      }
    }
  }

  /* At this point, the appropriate repository database file will have
  ** been opened.
  **
  ** Check to see if the the PATH_INFO begins with "draft[1-9]" and if
  ** so activate the special handling for draft skins
  */
  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;
    skin_use_draft(iSkin);
    zNewScript = mprintf("%s/draft%d", P("SCRIPT_NAME"), iSkin);
    if( g.zTop ) g.zTop = mprintf("%s/draft%d", g.zTop, iSkin);
    if( g.zBaseURL ) g.zBaseURL = mprintf("%s/draft%d", g.zBaseURL, iSkin);
    zPathInfo += 7;
    cgi_replace_parameter("PATH_INFO", zPathInfo);
    cgi_replace_parameter("SCRIPT_NAME", zNewScript);
  }

  /* 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( 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
................................................................................
    zFile = g.argv[1];
  }
  g.httpOut = stdout;
  g.httpIn = stdin;
  fossil_binary_mode(g.httpOut);
  fossil_binary_mode(g.httpIn);
  g.cgiOutput = 1;
  blob_read_from_file(&config, zFile, 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
................................................................................

/*
** If g.argv[arg] exists then it is either the name of a repository
** that will be used by a server, or else it is a directory that
** contains multiple repositories that can be served.  If g.argv[arg]
** is a directory, the repositories it contains must be named
** "*.fossil".  If g.argv[arg] does not exist, then we must be within
** an open check-out and the repository to serve is the repository of
** that check-out.
**
** Open the repository to be served if it is known.  If g.argv[arg] is
** a directory full of repositories, then set g.zRepositoryName to
** the name of that directory and the specific repository will be
** opened later by process_one_web_page() based on the content of
** the PATH_INFO variable.
................................................................................
** does not already exist. Always use "auto" hash-policy in this case.
*/
static void find_server_repository(int arg, int fCreate){
  if( g.argc<=arg ){
    db_must_be_within_tree();
  }else{
    const char *zRepo = g.argv[arg];
    int isDir = file_isdir(zRepo, ExtFILE);
    if( isDir==1 ){
      g.zRepositoryName = mprintf("%s", zRepo);
      file_simplify_name(g.zRepositoryName, -1, 0);
    }else{
      if( isDir==0 && fCreate ){
        const char *zPassword;
        db_create_repository(zRepo);
................................................................................
    if( bExists==0 ) return 1;
    zPath += i;
  }
  return 0;
}
#endif
#endif

/*
** Send a time-out reply
*/
void sigalrm_handler(int x){
  printf("TIMEOUT\n");
  fflush(stdout);
  exit(1);
}

/*
** COMMAND: server*
** COMMAND: ui
**
** Usage: %fossil server ?OPTIONS? ?REPOSITORY?
**    or: %fossil ui ?OPTIONS? ?REPOSITORY?
................................................................................
**   --baseurl URL       Use URL as the base (useful for reverse proxies)
**   --create            Create a new REPOSITORY if it does not already exist
**   --page PAGE         Start "ui" on PAGE.  ex: --page "timeline?y=ci"
**   --files GLOBLIST    Comma-separated list of glob patterns for static files
**   --localauth         enable automatic login for requests from localhost
**   --localhost         listen on 127.0.0.1 only (always true for "ui")
**   --https             signal a request coming in via https
**   --max-latency N     Do not let any single HTTP request run for more than N
**                       seconds (only works on unix)
**   --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
**   --th-trace          trace TH1 execution (for debugging purposes)
**   --repolist          If REPOSITORY is dir, URL "/" lists repos.
**   --scgi              Accept SCGI rather than HTTP
................................................................................
  const char *zBrowser;     /* Name of web browser program */
  char *zBrowserCmd = 0;    /* Command to launch the web browser */
  int isUiCmd;              /* True if command is "ui", not "server' */
  const char *zNotFound;    /* The --notfound option or NULL */
  int flags = 0;            /* Server flags */
#if !defined(_WIN32)
  int noJail;               /* Do not enter the chroot jail */
  const char *zMaxLatency;   /* Maximum 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 */
  const char *zInitPage = 0; /* Start on this page.  --page option */
................................................................................
    zFileGlob = z;
  }else{
    zFileGlob = find_option("files",0,1);
  }
  skin_override();
#if !defined(_WIN32)
  noJail = find_option("nojail",0,0)!=0;
  zMaxLatency = 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);
................................................................................
    if( isUiCmd && g.localOpen ){
      zInitPage = "timeline?c=current";
    }else{
      zInitPage = "";
    }
  }
  if( zPort ){
    if( strchr(zPort,':') ){
      int i;
      for(i=strlen(zPort)-1; i>=0 && zPort[i]!=':'; i--){}
      if( i>0 ){
        if( zPort[0]=='[' && zPort[i-1]==']' ){
          zIpAddr = mprintf("%.*s", i-2, zPort+1);
        }else{
          zIpAddr = mprintf("%.*s", i, zPort);
        }
        zPort += i+1;
      }
    }
    iPort = mxPort = atoi(zPort);
  }else{
    iPort = db_get_int("http-port", 8080);
    mxPort = iPort+100;
  }
#if !defined(_WIN32)
................................................................................
          break;
        }
      }
    }
#else
    zBrowser = db_get("web-browser", "open");
#endif
    if( zIpAddr==0 ){
      zBrowserCmd = mprintf("%s http://localhost:%%d/%s &",
                            zBrowser, zInitPage);
    }else if( strchr(zIpAddr,':') ){
      zBrowserCmd = mprintf("%s http://[%s]:%%d/%s &",
                            zBrowser, zIpAddr, zInitPage);
    }else{
      zBrowserCmd = mprintf("%s http://%s:%%d/%s &",
                            zBrowser, zIpAddr, zInitPage);
    }
  }
  if( g.repositoryOpen ) flags |= HTTP_SERVER_HAD_REPOSITORY;
  if( g.localOpen ) flags |= HTTP_SERVER_HAD_CHECKOUT;
  db_close(1);
  if( cgi_http_server(iPort, mxPort, zBrowserCmd, zIpAddr, flags) ){
    fossil_fatal("unable to listen on TCP socket %d", iPort);
  }
  if( zMaxLatency ){
    signal(SIGALRM, sigalrm_handler);
    alarm(atoi(zMaxLatency));
  }
  g.httpIn = stdin;
  g.httpOut = stdout;
  if( g.fHttpTrace || g.fSqlTrace ){
    fprintf(stderr, "====== SERVER pid %d =======\n", getpid());
  }
  g.cgiOutput = 1;
  find_server_repository(2, 0);
................................................................................
    cgi_handle_http_request(0);
  }
  process_one_web_page(zNotFound, glob_create(zFileGlob), allowRepoList);
#else
  /* Win32 implementation */
  if( isUiCmd ){
    zBrowser = db_get("web-browser", "start");
    if( zIpAddr==0 ){
      zBrowserCmd = mprintf("%s http://localhost:%%d/%s &",
                            zBrowser, zInitPage);
    }else if( strchr(zIpAddr,':') ){
      zBrowserCmd = mprintf("%s http://[%s]:%%d/%s &",
                            zBrowser, zIpAddr, zInitPage);
    }else{
      zBrowserCmd = mprintf("%s http://%s:%%d/%s &",
                            zBrowser, zIpAddr, zInitPage);
    }
  }
  if( g.repositoryOpen ) flags |= HTTP_SERVER_HAD_REPOSITORY;
  if( g.localOpen ) flags |= HTTP_SERVER_HAD_CHECKOUT;
  db_close(1);
  if( allowRepoList ){
    flags |= HTTP_SERVER_REPOLIST;

Changes to src/main.mk.

31
32
33
34
35
36
37

38
39
40
41
42
43
44
...
140
141
142
143
144
145
146




147
148
149
150
151
152
153
...
190
191
192
193
194
195
196

197



198








199
200
201
202
203
204
205
...
214
215
216
217
218
219
220

221
222
223
224
225
226
227
...
342
343
344
345
346
347
348

349
350
351
352
353
354
355
...
480
481
482
483
484
485
486



487
488
489
490
491
492
493
...
501
502
503
504
505
506
507



508
509
510
511
512
513
514
...
524
525
526
527
528
529
530
531



532
533
534
535
536
537
538
...
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
...
634
635
636
637
638
639
640

641
642
643
644
645
646
647
...
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
...
906
907
908
909
910
911
912








913
914
915
916
917
918
919
....
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
  $(SRCDIR)/checkin.c \
  $(SRCDIR)/checkout.c \
  $(SRCDIR)/clearsign.c \
  $(SRCDIR)/clone.c \
  $(SRCDIR)/comformat.c \
  $(SRCDIR)/configure.c \
  $(SRCDIR)/content.c \

  $(SRCDIR)/db.c \
  $(SRCDIR)/delta.c \
  $(SRCDIR)/deltacmd.c \
  $(SRCDIR)/descendants.c \
  $(SRCDIR)/diff.c \
  $(SRCDIR)/diffcmd.c \
  $(SRCDIR)/dispatch.c \
................................................................................
  $(SRCDIR)/wysiwyg.c \
  $(SRCDIR)/xfer.c \
  $(SRCDIR)/xfersetup.c \
  $(SRCDIR)/zip.c

EXTRA_FILES = \
  $(SRCDIR)/../skins/aht/details.txt \




  $(SRCDIR)/../skins/black_and_white/css.txt \
  $(SRCDIR)/../skins/black_and_white/details.txt \
  $(SRCDIR)/../skins/black_and_white/footer.txt \
  $(SRCDIR)/../skins/black_and_white/header.txt \
  $(SRCDIR)/../skins/blitz/css.txt \
  $(SRCDIR)/../skins/blitz/details.txt \
  $(SRCDIR)/../skins/blitz/footer.txt \
................................................................................
  $(SRCDIR)/../skins/rounded1/details.txt \
  $(SRCDIR)/../skins/rounded1/footer.txt \
  $(SRCDIR)/../skins/rounded1/header.txt \
  $(SRCDIR)/../skins/xekri/css.txt \
  $(SRCDIR)/../skins/xekri/details.txt \
  $(SRCDIR)/../skins/xekri/footer.txt \
  $(SRCDIR)/../skins/xekri/header.txt \

  $(SRCDIR)/diff.tcl \



  $(SRCDIR)/markdown.md









TRANS_SRC = \
  $(OBJDIR)/add_.c \
  $(OBJDIR)/allrepo_.c \
  $(OBJDIR)/attach_.c \
  $(OBJDIR)/bag_.c \
  $(OBJDIR)/bisect_.c \
................................................................................
  $(OBJDIR)/checkin_.c \
  $(OBJDIR)/checkout_.c \
  $(OBJDIR)/clearsign_.c \
  $(OBJDIR)/clone_.c \
  $(OBJDIR)/comformat_.c \
  $(OBJDIR)/configure_.c \
  $(OBJDIR)/content_.c \

  $(OBJDIR)/db_.c \
  $(OBJDIR)/delta_.c \
  $(OBJDIR)/deltacmd_.c \
  $(OBJDIR)/descendants_.c \
  $(OBJDIR)/diff_.c \
  $(OBJDIR)/diffcmd_.c \
  $(OBJDIR)/dispatch_.c \
................................................................................
 $(OBJDIR)/checkin.o \
 $(OBJDIR)/checkout.o \
 $(OBJDIR)/clearsign.o \
 $(OBJDIR)/clone.o \
 $(OBJDIR)/comformat.o \
 $(OBJDIR)/configure.o \
 $(OBJDIR)/content.o \

 $(OBJDIR)/db.o \
 $(OBJDIR)/delta.o \
 $(OBJDIR)/deltacmd.o \
 $(OBJDIR)/descendants.o \
 $(OBJDIR)/diff.o \
 $(OBJDIR)/diffcmd.o \
 $(OBJDIR)/dispatch.o \
................................................................................

$(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)/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
................................................................................
# the run to just those test cases.
#
test:	$(OBJDIR) $(APPNAME)
	$(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME) -quiet $(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




# Setup the options used to compile the included SQLite library.
SQLITE_OPTIONS = -DNDEBUG=1 \
                 -DSQLITE_THREADSAFE=0 \
                 -DSQLITE_DEFAULT_MEMSTATUS=0 \
                 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 \
                 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS \
................................................................................
                 -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




# Setup the options used to compile the included SQLite shell.
SHELL_OPTIONS = -Dmain=sqlite3_shell \
                -DSQLITE_SHELL_IS_UTF8=1 \
                -DSQLITE_OMIT_LOAD_EXTENSION=1 \
                -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) \
                -DSQLITE_SHELL_DBNAME_PROC=fossil_open
................................................................................

$(OBJDIR)/page_index.h: $(TRANS_SRC) $(OBJDIR)/mkindex
	$(OBJDIR)/mkindex $(TRANS_SRC) >$@

$(OBJDIR)/builtin_data.h: $(OBJDIR)/mkbuiltin $(EXTRA_FILES)
	$(OBJDIR)/mkbuiltin --prefix $(SRCDIR)/ $(EXTRA_FILES) >$@

$(OBJDIR)/headers:	$(OBJDIR)/page_index.h $(OBJDIR)/builtin_data.h $(OBJDIR)/makeheaders $(OBJDIR)/VERSION.h
	$(OBJDIR)/makeheaders $(OBJDIR)/add_.c:$(OBJDIR)/add.h \
	$(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h \
	$(OBJDIR)/attach_.c:$(OBJDIR)/attach.h \
	$(OBJDIR)/bag_.c:$(OBJDIR)/bag.h \
	$(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h \
	$(OBJDIR)/blob_.c:$(OBJDIR)/blob.h \
	$(OBJDIR)/branch_.c:$(OBJDIR)/branch.h \
................................................................................
	$(OBJDIR)/checkin_.c:$(OBJDIR)/checkin.h \
	$(OBJDIR)/checkout_.c:$(OBJDIR)/checkout.h \
	$(OBJDIR)/clearsign_.c:$(OBJDIR)/clearsign.h \
	$(OBJDIR)/clone_.c:$(OBJDIR)/clone.h \
	$(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h \
	$(OBJDIR)/configure_.c:$(OBJDIR)/configure.h \
	$(OBJDIR)/content_.c:$(OBJDIR)/content.h \

	$(OBJDIR)/db_.c:$(OBJDIR)/db.h \
	$(OBJDIR)/delta_.c:$(OBJDIR)/delta.h \
	$(OBJDIR)/deltacmd_.c:$(OBJDIR)/deltacmd.h \
	$(OBJDIR)/descendants_.c:$(OBJDIR)/descendants.h \
	$(OBJDIR)/diff_.c:$(OBJDIR)/diff.h \
	$(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h \
	$(OBJDIR)/dispatch_.c:$(OBJDIR)/dispatch.h \
................................................................................
	$(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h \
	$(OBJDIR)/wysiwyg_.c:$(OBJDIR)/wysiwyg.h \
	$(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h \
	$(OBJDIR)/xfersetup_.c:$(OBJDIR)/xfersetup.h \
	$(OBJDIR)/zip_.c:$(OBJDIR)/zip.h \
	$(SRCDIR)/sqlite3.h \
	$(SRCDIR)/th.h \
	$(OBJDIR)/VERSION.h
	touch $(OBJDIR)/headers
$(OBJDIR)/headers: Makefile
$(OBJDIR)/json.o $(OBJDIR)/json_artifact.o $(OBJDIR)/json_branch.o $(OBJDIR)/json_config.o $(OBJDIR)/json_diff.o $(OBJDIR)/json_dir.o $(OBJDIR)/json_finfo.o $(OBJDIR)/json_login.o $(OBJDIR)/json_query.o $(OBJDIR)/json_report.o $(OBJDIR)/json_status.o $(OBJDIR)/json_tag.o $(OBJDIR)/json_timeline.o $(OBJDIR)/json_user.o $(OBJDIR)/json_wiki.o : $(SRCDIR)/json_detail.h
Makefile:
$(OBJDIR)/add_.c:	$(SRCDIR)/add.c $(OBJDIR)/translate
	$(OBJDIR)/translate $(SRCDIR)/add.c >$@

.......