Fossil

Check-in Differences
Login

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

Difference From:

[a0001dcf] Version 2.4 (user: drh tags: trunk, release, version-2.4, date: 2017-11-03 09:29:29)

To:

[4f90d591] Now that pledge() is automatically detected, update the comment with the new name of the configuration parameter. (user: andybradford tags: trunk, date: 2018-01-18 03:52:41)

Changes to Makefile.in.

    34     34   TCC = @CC@
    35     35   
    36     36   #### Tcl shell for use in running the fossil testsuite.  If you do not
    37     37   #    care about testing the end result, this can be blank.
    38     38   #
    39     39   TCLSH = tclsh
    40     40   
           41  +CFLAGS = @CFLAGS@
    41     42   LIB =	@LDFLAGS@ @EXTRA_LDFLAGS@ @LIBS@
    42     43   BCCFLAGS =	@CPPFLAGS@ @CFLAGS@
    43     44   TCCFLAGS =	@EXTRA_CFLAGS@ @CPPFLAGS@ @CFLAGS@ -DHAVE_AUTOCONFIG_H -D_HAVE_SQLITE_CONFIG_H
    44     45   INSTALLDIR = $(DESTDIR)@prefix@/bin
    45     46   USE_SYSTEM_SQLITE = @USE_SYSTEM_SQLITE@
    46     47   USE_LINENOISE = @USE_LINENOISE@
    47     48   USE_SEE = @USE_SEE@

Changes to VERSION.

     1         -2.4
            1  +2.5

Changes to ajax/js/whajaj.js.

   145    145       "requester" object per connection attempt, for connections to the
   146    146       same back-end, using an instance configured for that back-end
   147    147       can simplify usage. This class is designed so that the actual
   148    148       connection-related details (i.e. _how_ it connects to the
   149    149       back-end) may be re-implemented to use a client's preferred
   150    150       connection mechanism (e.g. jQuery).
   151    151   
   152         -    The optional opt paramater may be an object with any (or all) of
          152  +    The optional opt parameter may be an object with any (or all) of
   153    153       the properties documented for WhAjaj.Connector.options.ajax.
   154    154       Properties set here (or later via modification of the "options"
   155    155       property of this object) will be used in calls to
   156    156       WhAjaj.Connector.sendRequest(), and these override (normally) any
   157    157       options set in WhAjaj.Connector.options.ajax. Note that
   158    158       WhAjaj.Connector.sendRequest() _also_ takes an options object,
   159    159       and ones passed there will override, for purposes of that one

Changes to auto.def.

     9      9       with-zlib:path|auto|tree
    10     10                            => {Look for zlib in the given path, automatically, or in the source tree}
    11     11       with-exec-rel-paths=0
    12     12                            => {Enable relative paths for external diff/gdiff}
    13     13       with-legacy-mv-rm=0  => {Enable legacy behavior for mv/rm (skip checkout files)}
    14     14       with-th1-docs=0      => {Enable TH1 for embedded documentation pages}
    15     15       with-th1-hooks=0     => {Enable TH1 hooks for commands and web pages}
    16         -    with-tcl=path        => {Enable Tcl integration, with Tcl in the specified path}
           16  +    with-tcl:path        => {Enable Tcl integration, with Tcl in the specified path}
    17     17       with-tcl-stubs=0     => {Enable Tcl integration via stubs library mechanism}
    18     18       with-tcl-private-stubs=0
    19     19                            => {Enable Tcl integration via private stubs mechanism}
    20     20       with-see=0           => {Enable the SQLite Encryption Extension (SEE)}
    21     21       internal-sqlite=1    => {Don't use the internal SQLite, use the system one}
    22     22       static=0             => {Link a static executable}
    23     23       fusefs=1             => {Disable the Fuse Filesystem}
    24     24       fossil-debug=0       => {Build with fossil debugging enabled}
           25  +    no-opt=0             => {Build without optimization}
    25     26       json=0               => {Build with fossil JSON API enabled}
    26     27   }
    27     28   
    28     29   # sqlite wants these types if possible
    29     30   cc-with {-includes {stdint.h inttypes.h}} {
    30     31       cc-check-types uint32_t uint16_t int16_t uint8_t
    31     32   }
................................................................................
    32     33   
    33     34   # Use pread/pwrite system calls in place of seek + read/write if possible
    34     35   define USE_PREAD [cc-check-functions pread]
    35     36   
    36     37   # Find tclsh for the test suite. Can't yet use jimsh for this.
    37     38   cc-check-progs tclsh
    38     39   
    39         -define EXTRA_CFLAGS ""
           40  +define EXTRA_CFLAGS "-Wall"
    40     41   define EXTRA_LDFLAGS ""
    41     42   define USE_SYSTEM_SQLITE 0
    42     43   define USE_LINENOISE 0
    43     44   define FOSSIL_ENABLE_MINIZ 0
    44     45   define USE_SEE 0
    45     46   
    46     47   # This procedure is a customized version of "cc-check-function-in-lib",
................................................................................
    86     87       # search for the system SQLite once with -ldl, and once without. If
    87     88       # the library can only be found with $extralibs set to -ldl, then
    88     89       # the code below will append -ldl to LIBS.
    89     90       #
    90     91       foreach extralibs {{} {-ldl}} {
    91     92   
    92     93         # Locate the system SQLite by searching for sqlite3_open(). Then check
    93         -      # if sqlite3_prepare_v3() can be found as well. If we can find open() but
    94         -      # not prepare_v3(), then the system SQLite is too old to link against
           94  +      # if sqlite3_vtab_collation() can be found as well. If we can find open() but
           95  +      # not vtab_collation(), then the system SQLite is too old to link against
    95     96         # fossil.
    96     97         #
    97     98         if {[check-function-in-lib sqlite3_open sqlite3 $extralibs]} {
    98         -        if {![check-function-in-lib sqlite3_prepare_v3 sqlite3 $extralibs]} {
    99         -          user-error "system sqlite3 too old (require >= 3.20.0)"
           99  +        if {![check-function-in-lib sqlite3_vtab_collation sqlite3 $extralibs]} {
          100  +          user-error "system sqlite3 too old (require >= 3.22.0)"
   100    101           }
   101    102   
   102    103           # Success. Update symbols and return.
   103    104           #
   104    105           define USE_SYSTEM_SQLITE 1
   105    106           define-append LIBS -lsqlite3
   106    107           define-append LIBS $extralibs
................................................................................
   130    131   
   131    132   if {[string match *-solaris* [get-define host]]} {
   132    133       define-append EXTRA_CFLAGS {-D_XOPEN_SOURCE=500 -D__EXTENSIONS__}
   133    134   }
   134    135   
   135    136   if {[opt-bool fossil-debug]} {
   136    137       define-append EXTRA_CFLAGS -DFOSSIL_DEBUG
   137         -    define CFLAGS {-g -O0}
          138  +    define CFLAGS {-g -O0 -Wall}
   138    139       msg-result "Debugging support enabled"
   139    140   }
          141  +
          142  +if {[opt-bool no-opt]} {
          143  +    define CFLAGS {-g -O0 -Wall}
          144  +    msg-result "Builting without compiler optimization"
          145  +}
   140    146   
   141    147   if {[opt-bool with-see]} {
   142    148       define-append EXTRA_CFLAGS -DUSE_SEE
   143    149       define USE_SEE 1
   144    150       msg-result "Enabling encryption support"
   145    151   }
   146    152   
................................................................................
   357    363                   /usr /usr/local /usr/share /opt/local]
   358    364               set msg "on your system"
   359    365           }
   360    366       } else {
   361    367           array set tclconfig [parse-tclconfig-sh $tclpath]
   362    368           set msg "at $tclpath"
   363    369       }
          370  +    if {[opt-bool static]} {
          371  +        set tclconfig(TCL_LD_FLAGS) { }
          372  +    }
   364    373       if {![info exists tclconfig(TCL_INCLUDE_SPEC)]} {
   365    374           user-error "Cannot find Tcl $msg"
   366    375       }
   367    376       set tclstubs [opt-bool with-tcl-stubs]
   368    377       if {$tclprivatestubs} {
   369    378           define FOSSIL_ENABLE_TCL_PRIVATE_STUBS
   370    379           define USE_TCL_STUBS
................................................................................
   462    471       if {[is_mingw]} {
   463    472           define-append LIBS -lwsock32
   464    473       }
   465    474   }
   466    475   cc-check-functions utime
   467    476   cc-check-functions usleep
   468    477   cc-check-functions strchrnul
          478  +cc-check-functions pledge
   469    479   
   470    480   # Check for getloadavg(), and if it doesn't exist, define FOSSIL_OMIT_LOAD_AVERAGE
   471    481   if {![cc-check-functions getloadavg]} {
   472    482     define FOSSIL_OMIT_LOAD_AVERAGE 1
   473    483     msg-result "Load average support unavailable"
   474    484   }
   475    485   

Added skins/ardoise/README.md.

            1  +## Ardoise theme
            2  +
            3  +A black and grey skin ("Ardoise" is the french word for slate).
            4  +
            5  +The skin includes custom icons for the file browser and the WYSIWYG editor, which are embedded directly in the css as base64 blobs. For convenience, they are also provided as standalone files in the images subdirectory.
            6  +
            7  +This skin was contributed by Antoine Chavasse.
            8  +
            9  +This theme is loosely based upon, and still contains some elements from the Blitz theme by James Moger.
           10  +
           11  +This theme embeds & uses a modified copy of [Normalize 3.0.2](https://necolas.github.io/normalize.css/) which is distributed under an [MIT license](https://github.com/necolas/normalize.css/blob/master/LICENSE.md).
           12  +
           13  +This theme embeds & uses a modified copy of [Skeleton](http://getskeleton.com) which is distributed under an [MIT license](https://github.com/dhg/Skeleton/blob/master/LICENSE.md).
           14  +
           15  +The sass version of Skeleton used in this project was made by [Seth Coelen](https://github.com/whatsnewsaes).

Added skins/ardoise/css.txt.

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

Added skins/ardoise/details.txt.

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

Added skins/ardoise/footer.txt.

            1  +  <th1>
            2  +    if {[string first artifact $current_page] == 0 || [string first hexdump $current_page] == 0} {
            3  +      html "</div>"
            4  +    }
            5  +  </th1>
            6  +  </div> <!-- end div container -->
            7  +</div> <!-- end div middle max-full-width -->
            8  +<div class="footer">
            9  +  <div class="container">
           10  +    <div class="pull-right">
           11  +      <a href="https://www.fossil-scm.org/">Fossil $release_version $manifest_version $manifest_date</a>
           12  +    </div>
           13  +    This page was generated in about <th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s
           14  +  </div>
           15  +</div>

Added skins/ardoise/header.txt.

            1  +<div class="header">
            2  +  <div class="container">
            3  +    <div class="login pull-right">
            4  +      <th1>
            5  +        if {[info exists login]} {
            6  +          html "<b>$login</b> — <a class='button' href='$home/login'>Logout</a>\n"
            7  +        } else {
            8  +          html "<a class='button' href='$home/login'>Login</a>\n"
            9  +        }
           10  +      </th1>
           11  +    </div>
           12  +    <div class='title'>
           13  +      <h1>$<project_name>
           14  +      <th1>
           15  +      if {[anycap jor]} {
           16  +        html "<a class='rss' href='$home/timeline.rss'></a>"
           17  +      }
           18  +      </th1>
           19  +      <small> &nbsp;$<title></small></h1>
           20  +    </div>
           21  +
           22  +    <!-- Main Menu -->
           23  +    <div class="mainmenu">
           24  +      <ul>
           25  +        <th1>
           26  +proc menulink {url name} {
           27  +  upvar current_page current
           28  +  upvar home home
           29  +  if {[string range $url 0 [string length $current]] eq "/$current"} {
           30  +    html "<li class='active'>"
           31  +  } else {
           32  +    html "<li>"
           33  +  }
           34  +  html "<a href='$home$url'>$name</a></li>\n"
           35  +}
           36  +menulink $index_page Home
           37  +if {[anycap jor]} {
           38  +  menulink /timeline Timeline
           39  +}
           40  +if {[hascap oh]} {
           41  +  menulink /dir?ci=tip Files
           42  +}
           43  +if {[hascap o]} {
           44  +  menulink  /brlist Branches
           45  +  menulink  /taglist Tags
           46  +}
           47  +if {[hascap r]} {
           48  +  menulink /ticket Tickets
           49  +}
           50  +if {[hascap j]} {
           51  +  menulink /wiki Wiki
           52  +}
           53  +if {[hascap o]} {
           54  +  menulink /help Help
           55  +  }
           56  +if {[hascap s]} {
           57  +  menulink /setup Admin
           58  +} elseif {[hascap a]} {
           59  +  menulink /setup_ulist Users
           60  +}
           61  +            </th1>
           62  +          </ul>
           63  +        </div> <!-- end div mainmenu -->
           64  +      </div> <!-- end div container -->
           65  +    </div> <!-- end div header -->
           66  +    <div class="middle max-full-width">
           67  +      <div class="container">
           68  +        <th1>
           69  +          if {[string first artifact $current_page] == 0 || [string first hexdump $current_page] == 0} {
           70  +            html "<div class=\"artifact_content\">"
           71  +          }
           72  +        </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      1   <div class="footer">
     2      2   Fossil $release_version $manifest_version $manifest_date
     3      3   </div>
     4         -</body></html>

Changes to skins/black_and_white/header.txt.

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

Changes to skins/blitz/css.txt.

  1068   1068     border-bottom: 1px solid #ddd;
  1069   1069     border-right: 1px solid #ddd;
  1070   1070   }
  1071   1071   
  1072   1072   tr.timelineCurrent td.timelineTableCell {
  1073   1073   }
  1074   1074   
  1075         -tr.timelineSpacer {
  1076         -}
  1077         -
  1078   1075   tr.timelineBottom td {
  1079   1076     border-bottom: 0;
  1080   1077   }
  1081   1078   
  1082   1079   div.timelineDate {
  1083   1080     font-weight: bold;
  1084   1081     white-space: nowrap;

Changes to skins/blitz/footer.txt.

     1         -      </div> <!-- end div container -->
     2         -    </div> <!-- end div middle max-full-width -->
     3         -    <div class="footer">
     4         -      <div class="container">
     5         -        <div class="pull-right">
     6         -          <a href="https://www.fossil-scm.org/">Fossil $release_version $manifest_version $manifest_date</a>
     7         -        </div>
     8         -        This page was generated in about <th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s
     9         -      </div>
            1  +  </div> <!-- end div container -->
            2  +</div> <!-- end div middle max-full-width -->
            3  +<div class="footer">
            4  +  <div class="container">
            5  +    <div class="pull-right">
            6  +      <a href="https://www.fossil-scm.org/">Fossil $release_version $manifest_version $manifest_date</a>
    10      7       </div>
    11         -  </body>
    12         -</html>
            8  +    This page was generated in about <th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s
            9  +  </div>
           10  +</div>

Changes to skins/blitz/header.txt.

     1         -<html>
     2         -  <head>
     3         -    <base href="$baseurl/$current_page" />
     4         -    <title>$<project_name>: $<title></title>
     5         -      <link rel="alternate" type="application/rss+xml" title="RSS Feed" href="$home/timeline.rss" />
     6         -      <link rel="stylesheet" href="$stylesheet_url" type="text/css" media="screen" />
     7         -  </head>
            1  +<div class="header">
            2  +  <div class="container">
     8      3   
     9         -  <body>
    10         -    <div class="header">
    11         -      <div class="container">
            4  +    <!-- Header -->
            5  +    <div class="login pull-right">
            6  +      <th1>
            7  +        if {[info exists login]} {
            8  +          html "<b>$login</b> — <a class='button' href='$home/login'>Logout</a>\n"
            9  +        } else {
           10  +          html "<a class='button' href='$home/login'>Login</a>\n"
           11  +        }
           12  +      </th1>
           13  +      <div>
           14  +        <h2><small>$title</small></h2>
           15  +      </div>
           16  +    </div>
           17  +    <div class='logo'>
           18  +      <img src='$logo_image_url' />
           19  +      <th1>
           20  +      if {[anycap jor]} {
           21  +        html "<a class='rss' href='$home/timeline.rss'></a>"
           22  +      }
           23  +      </th1>
           24  +    </div>
    12     25   
    13         -        <!-- Header -->
    14         -        <div class="login pull-right">
    15         -          <th1>
    16         -            if {[info exists login]} {
    17         -              html "<b>$login</b> — <a class='button' href='$home/login'>Logout</a>\n"
    18         -            } else {
    19         -              html "<a class='button' href='$home/login'>Login</a>\n"
    20         -            }
    21         -          </th1>
    22         -          <div>
    23         -            <h2><small>$title</small></h2>
    24         -          </div>
    25         -        </div>
    26         -        <div class='logo'>
    27         -          <img src='$logo_image_url' />
    28         -          <th1>
    29         -          if {[anycap jor]} {
    30         -            html "<a class='rss' href='$home/timeline.rss'></a>"
    31         -          }
    32         -          </th1>
    33         -        </div>
    34         -
    35         -        <!-- Main Menu -->
    36         -        <div class="mainmenu">
    37         -          <ul>
    38         -            <th1>
           26  +    <!-- Main Menu -->
           27  +    <div class="mainmenu">
           28  +      <ul>
           29  +        <th1>
    39     30   proc menulink {url name} {
    40     31     upvar current_page current
    41     32     upvar home home
    42     33     if {[string range $url 0 [string length $current]] eq "/$current"} {
    43     34       html "<li class='active'>"
    44     35     } else {
    45     36       html "<li>"

Changes to skins/blitz_no_logo/css.txt.

  1068   1068     border-bottom: 1px solid #ddd;
  1069   1069     border-right: 1px solid #ddd;
  1070   1070   }
  1071   1071   
  1072   1072   tr.timelineCurrent td.timelineTableCell {
  1073   1073   }
  1074   1074   
  1075         -tr.timelineSpacer {
  1076         -}
  1077         -
  1078   1075   tr.timelineBottom td {
  1079   1076     border-bottom: 0;
  1080   1077   }
  1081   1078   
  1082   1079   div.timelineDate {
  1083   1080     font-weight: bold;
  1084   1081     white-space: nowrap;

Changes to skins/blitz_no_logo/footer.txt.

     1         -      </div> <!-- end div container -->
     2         -    </div> <!-- end div middle max-full-width -->
     3         -    <div class="footer">
     4         -      <div class="container">
     5         -        <div class="pull-right">
     6         -          <a href="https://www.fossil-scm.org/">Fossil $release_version $manifest_version $manifest_date</a>
     7         -        </div>
     8         -        This page was generated in about <th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s
     9         -      </div>
            1  +  </div> <!-- end div container -->
            2  +</div> <!-- end div middle max-full-width -->
            3  +<div class="footer">
            4  +  <div class="container">
            5  +    <div class="pull-right">
            6  +      <a href="https://www.fossil-scm.org/">Fossil $release_version $manifest_version $manifest_date</a>
    10      7       </div>
    11         -  </body>
    12         -</html>
            8  +    This page was generated in about <th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s
            9  +  </div>
           10  +</div>

Changes to skins/blitz_no_logo/header.txt.

     1         -<html>
     2         -  <head>
     3         -    <base href="$baseurl/$current_page" />
     4         -    <title>$<project_name>: $<title></title>
     5         -      <link rel="alternate" type="application/rss+xml" title="RSS Feed" href="$home/timeline.rss" />
     6         -      <link rel="stylesheet" href="$stylesheet_url" type="text/css" media="screen" />
     7         -  </head>
            1  +<div class="header">
            2  +  <div class="container">
     8      3   
     9         -  <body>
    10         -    <div class="header">
    11         -      <div class="container">
            4  +    <div class="login pull-right">
            5  +      <th1>
            6  +        if {[info exists login]} {
            7  +          html "<b>$login</b> — <a class='button' href='$home/login'>Logout</a>\n"
            8  +        } else {
            9  +          html "<a class='button' href='$home/login'>Login</a>\n"
           10  +        }
           11  +      </th1>
           12  +    </div>
           13  +    <div class='title'>
           14  +      <h1>$<project_name>
           15  +      <th1>
           16  +      if {[anycap jor]} {
           17  +        html "<a class='rss' href='$home/timeline.rss'></a>"
           18  +      }
           19  +      </th1>
           20  +      <small> &nbsp;$<title></small></h1>
           21  +    </div>
    12     22   
    13         -        <div class="login pull-right">
    14         -          <th1>
    15         -            if {[info exists login]} {
    16         -              html "<b>$login</b> — <a class='button' href='$home/login'>Logout</a>\n"
    17         -            } else {
    18         -              html "<a class='button' href='$home/login'>Login</a>\n"
    19         -            }
    20         -          </th1>
    21         -        </div>
    22         -        <div class='title'>
    23         -          <h1>$<project_name>
    24         -          <th1>
    25         -          if {[anycap jor]} {
    26         -            html "<a class='rss' href='$home/timeline.rss'></a>"
    27         -          }
    28         -          </th1>
    29         -          <small> &nbsp;$<title></small></h1>
    30         -        </div>
    31         -
    32         -        <!-- Main Menu -->
    33         -        <div class="mainmenu">
    34         -          <ul>
    35         -            <th1>
           23  +    <!-- Main Menu -->
           24  +    <div class="mainmenu">
           25  +      <ul>
           26  +        <th1>
    36     27   proc menulink {url name} {
    37     28     upvar current_page current
    38     29     upvar home home
    39     30     if {[string range $url 0 [string length $current]] eq "/$current"} {
    40     31       html "<li class='active'>"
    41     32     } else {
    42     33       html "<li>"

Changes to skins/bootstrap/footer.txt.

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

Changes to skins/default/css.txt.

    40     40   
    41     41   .content h1 {
    42     42       font-size: 1.25em;
    43     43   }
    44     44   .content h2 {
    45     45       font-size: 1.15em;
    46     46   }
    47         -.content h2 {
           47  +.content h3 {
    48     48       font-size: 1.05em;
    49     49       font-weight: bold;
    50     50   }
    51     51   
    52     52   .section {
    53     53       font-size: 1em;
    54     54       font-weight: bold;
................................................................................
   179    179       vertical-align: top;
   180    180       background-color: #f8f8f8;
   181    181       border: 1px solid #ccc;
   182    182   }
   183    183   td.tktDspValue pre {
   184    184       white-space: pre-wrap;
   185    185   }
          186  +
          187  +span.timelineDetail {
          188  +    font-size: 90%;
          189  +}
   186    190   
   187    191   .footer {
   188    192       border-top: 1px solid #ccc;
   189    193       padding: 10px;
   190    194       font-size:.7em;
   191    195       margin-top: 10px;
   192    196       color: #ccc;

Changes to skins/default/footer.txt.

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

Changes to skins/default/header.txt.

     1         -<html>
     2         -  <head>
     3         -    <base href="$baseurl/$current_page" />
     4         -    <title>$<project_name>: $<title></title>
     5         -      <link rel="alternate" type="application/rss+xml" title="RSS Feed"
     6         -            href="$home/timeline.rss" />
     7         -      <link rel="stylesheet" href="$stylesheet_url" type="text/css"
     8         -            media="screen" />
     9         -  </head>
    10         -
    11         -  <body>
    12         -    <div class="header">
    13         -      <div class="title"><h1>$<project_name></h1>$<title></div>
    14         -        <div class="status"><th1>
    15         -     if {[info exists login]} {
    16         -       html "$login — <a href='$home/login'>Logout</a>\n"
    17         -     } else {
    18         -       html "<a href='$home/login'>Login</a>\n"
    19         -     }
    20         -        </th1></div>
    21         -    </div>
    22         -
    23         -    <div class="mainmenu">
            1  +<div class="header">
            2  +  <div class="title"><h1>$<project_name></h1>$<title></div>
            3  +    <div class="status"><th1>
            4  + if {[info exists login]} {
            5  +   html "$login — <a href='$home/login'>Logout</a>\n"
            6  + } else {
            7  +   html "<a href='$home/login'>Login</a>\n"
            8  + }
            9  +    </th1></div>
           10  +</div>
           11  +<div class="mainmenu">
    24     12   <th1>
    25     13   proc menulink {url name} {
    26     14     upvar current_page current
    27     15     upvar home home
    28     16     if {[string range $url 0 [string length $current]] eq "/$current"} {
    29     17       html "<a href='$home$url' class='active'>$name</a>\n"
    30     18     } else {

Changes to skins/eagle/css.txt.

   162    162   
   163    163   /* the format for the timeline data table */
   164    164   table.timelineTable {
   165    165     cellspacing: 0;
   166    166     border: 0;
   167    167     cellpadding: 0;
   168    168     font-family: "courier new";
   169         -  border-collapse: collapse;
          169  +  border-spacing: 0px 2px;
          170  +  // border-collapse: collapse;
   170    171   }
   171    172   
   172    173   tr.timelineSelected {
   173    174     background-color: #7EA2D9;
   174    175   }
   175    176   
   176    177   /* commit node */
................................................................................
   331    332   div.selectedText {
   332    333     background-color: #7EA2D9;
   333    334   }
   334    335   
   335    336   .statistics-report-graph-line {
   336    337     background-color: #7EA2D9;
   337    338   }
          339  +
          340  +.timelineModernCell[id], .timelineColumnarCell[id], .timelineDetailCell[id] {
          341  +  background-color: #455978;
          342  +}

Changes to skins/eagle/footer.txt.

    18     18     This page was generated in about
    19     19     <th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s by
    20     20     <a href="$fossilUrl/">Fossil</a>
    21     21     version $release_version $tclVersion
    22     22     <a href="$fossilUrl/index.html/info/$version">$manifest_version</a>
    23     23     <a href="$fossilUrl/index.html/timeline?c=$fossilDate&amp;y=ci">$manifest_date</a>
    24     24   </div>
    25         -</body></html>

Changes to skins/eagle/header.txt.

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

Changes to skins/enhanced1/footer.txt.

    18     18     This page was generated in about
    19     19     <th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s by
    20     20     <a href="$fossilUrl/">Fossil</a>
    21     21     version $release_version $tclVersion
    22     22     <a href="$fossilUrl/index.html/info/$version">$manifest_version</a>
    23     23     <a href="$fossilUrl/index.html/timeline?c=$fossilDate&amp;y=ci">$manifest_date</a>
    24     24   </div>
    25         -</body></html>

Changes to skins/enhanced1/header.txt.

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

Changes to skins/khaki/footer.txt.

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

Changes to skins/khaki/header.txt.

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

Changes to skins/original/footer.txt.

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

Changes to skins/original/header.txt.

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

Changes to skins/plain_gray/footer.txt.

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

Changes to skins/plain_gray/header.txt.

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

Changes to skins/rounded1/details.txt.

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

Changes to skins/rounded1/footer.txt.

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

Changes to skins/rounded1/header.txt.

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

Changes to skins/xekri/css.txt.

   705    705   }
   706    706   
   707    707   
   708    708   /**************************************
   709    709    * Timeline
   710    710    */
   711    711   
   712         -div.divider {
   713         -  color: #ee0;
   714         -  font-size: 1.2rem;
   715         -  font-weight: bold;
   716         -  margin-top: 1rem;
   717         -  white-space: nowrap;
   718         -}
   719         -
   720    712   /* The suppressed duplicates lines in timeline, .. */
   721         -span.timelineDisabled {
   722         -  font-size: 0.5rem;
          713  +.timelineDisabled {
          714  +  font-size:  0.5rem;
   723    715     font-style: italic;
   724    716   }
   725    717   
   726         -/* the format for the timeline data table */
   727         -table.timelineTable {
   728         -  border: 0;
   729         -}
   730         -
   731         -/* The row in the timeline table that contains the entry of interest */
   732         -tr.timelineSelected {
   733         -  border: 1px solid #eee;
   734         -  border-radius: 1rem;
   735         -}
   736         -
   737         -tr.timelineSelected td.timelineTime
   738         -, tr.timelineSelected td.timelineTableCell {
   739         -  background-color: #333;
   740         -  box-shadow: 2px 2px 1px #000;
   741         -  padding: 0.5rem;
   742         -}
   743         -
   744         -tr.timelineSelected td.timelineTime {
   745         -  border-radius: 1rem 0 0 1rem;
   746         -}
   747         -
   748         -tr.timelineSelected td.timelineTableCell {
   749         -  border-radius: 0 1rem 1rem 0;
   750         -}
   751         -
   752         -/* the format for the timeline data cells */
   753         -td.timelineTableCell {
   754         -  padding: 0.3rem;
   755         -  text-align: left;
          718  +/* the format for the timeline version display(no history permission!) */
          719  +.timelineHistDsp {
          720  +  font-weight: bold;
          721  +}
          722  +
          723  +.content .timelineTable {
          724  +  border:         0;
          725  +  border-spacing: 0 0.5rem;
          726  +}
          727  +
          728  +.content .timelineTable tr {
          729  +  background: #222;
          730  +  border:     0;
          731  +  padding:    0;
          732  +  box-shadow: none;
          733  +}
          734  +
          735  +.timelineTable .timelineDate {
          736  +  color:       #ee0;
          737  +  font-size:   1.2rem;
          738  +  font-weight: bold;
          739  +  margin-top:  1rem;
          740  +  white-space: nowrap;
          741  +}
          742  +
          743  +.timelineTable .timelineTime {
          744  +  border-radius: 0;
          745  +  border-width:  0;
          746  +  padding:       0.25rem 0.5rem 0.5rem 0.5rem;
          747  +  white-space:   nowrap;
          748  +}
          749  +
          750  +.timelineGraph {
          751  +  text-align:     left;
   756    752     vertical-align: top;
   757         -}
   758         -
   759         -td.timelineTableCell[style] {
   760         -  color: #000;
   761         -}
   762         -
   763         -/* the format for the timeline data cell of the current checkout */
   764         -tr.timelineCurrent td.timelineTableCell {
   765         -  border: 0;
   766         -  border-radius: 1em 0em;
   767         -}
   768         -
   769         -/* the format for the timeline leaf marks */
   770         -span.timelineLeaf {
   771         -  font-weight: bold;
          753  +  width:          20px;
          754  +}
          755  +
          756  +.timelineTable .timelineModernCell  ,
          757  +.timelineTable .timelineCompactCell ,
          758  +.timelineTable .timelineVerboseCell ,
          759  +.timelineTable .timelineDetailCell  {
          760  +/*
          761  +  background:    linear-gradient(to bottom, #222 0%, #333 16%, #222 100%);
          762  +*/
          763  +  border-radius: 0;
          764  +  border-width:  0;
          765  +  padding:       0.25rem 0.5rem 0.5rem 0.5rem;
          766  +}
          767  +
          768  +.timelineTable .timelineColumnarCell {
          769  +/*
          770  +  background:    linear-gradient(to bottom, #222 0%, #333 16%, #222 100%);
          771  +*/
          772  +  border-radius: 0;
          773  +  border-width:  0;
          774  +  padding:       0.25rem 0.5rem 0.5rem 0.5rem;
          775  +}
          776  +
          777  +.timelineTable .timelineModernCell[id]   ,
          778  +.timelineTable .timelineCompactCell[id]  ,
          779  +.timelineTable .timelineVerboseCell[id]  ,
          780  +.timelineTable .timelineColumnarCell[id] ,
          781  +.timelineTable .timelineDetailCell[id]   {
          782  +  background: #272727;
          783  +}
          784  +
          785  +.timelineTable .timelineCurrent .timelineTime {
          786  +  background:    #333;
          787  +  border-radius: 1rem 0 0 1rem;
          788  +  border-width:  0;
          789  +}
          790  +
          791  +.timelineTable .timelineCurrent .timelineColumnarCell {
          792  +  background:    #333;
          793  +}
          794  +
          795  +.timelineTable .timelineCurrent .timelineModernCell  ,
          796  +.timelineTable .timelineCurrent .timelineCompactCell ,
          797  +.timelineTable .timelineCurrent .timelineVerboseCell ,
          798  +.timelineTable .timelineCurrent .timelineDetailCell  {
          799  +  background:    #333;
          800  +  border-radius: 0 1rem 1rem 0;
          801  +}
          802  +
          803  +.timelineTable .timelineSelected {
          804  +  background: #222;
          805  +  border:     0;
          806  +  box-shadow: none;
          807  +}
          808  +
          809  +.timelineTable .timelineSelected .timelineTime {
          810  +  background:    #333;
          811  +  border-radius: 1rem 0 0 1rem;
          812  +  box-shadow:    2px 2px 1px #000;
          813  +}
          814  +
          815  +.timelineTable .timelineSelected .timelineColumnarCell {
          816  +  background: #333;
          817  +  box-shadow: 2px 2px 1px #000;
          818  +}
          819  +
          820  +.timelineTable .timelineSelected .timelineModernCell  ,
          821  +.timelineTable .timelineSelected .timelineCompactCell ,
          822  +.timelineTable .timelineSelected .timelineVerboseCell ,
          823  +.timelineTable .timelineSelected .timelineDetailCell  {
          824  +  background:    #333;
          825  +  border-radius: 0 1rem 1rem 0;
          826  +  box-shadow:    2px 2px 1px #000;
          827  +}
          828  +
          829  +.timelineTable .timelineModernCell  .timelineModernComment  ,
          830  +.timelineTable .timelineModernCell  .timelineModernDetail   ,
          831  +.timelineTable .timelineCompactCell .timelineCompactComment ,
          832  +.timelineTable .timelineCompactCell .timelineCompactDetail  ,
          833  +.timelineTable .timelineVerboseCell .timelineVerboseComment ,
          834  +.timelineTable .timelineVerboseCell .timelineVerboseDetail  {
          835  +}
          836  +
          837  +.timelineTable .timelineModernCell     .timelineLeaf ,
          838  +.timelineTable .timelineCompactCell    .timelineLeaf ,
          839  +.timelineTable .timelineVerboseCell    .timelineLeaf ,
          840  +.timelineTable .timelineVerboseComment .timelineLeaf {
          841  +  font-weight: bold;
          842  +}
          843  +
          844  +.timelineTable .timelineModernCell .timelineModernDetail ,
          845  +.timelineTable .timelineDetailCell {
          846  +  font-size: 85%;
          847  +}
          848  +
          849  +.timelineTable .timelineDetailCell .timelineColumnarDetail {
          850  +  white-space: pre-line;
          851  +}
          852  +
          853  +.timelineTable .timelineDetailCell ul.filelist::before {
          854  +  content: "files:";
          855  +}
          856  +
          857  +.timelineTable .timelineDetailCell ul.filelist {
          858  +  margin-left:  0;
          859  +  padding-left: 0;
          860  +}
          861  +
          862  +.timelineTable .timelineDetailCell ul.filelist li {
          863  +  margin-left:  1.5rem;
          864  +  padding-left: 0;
          865  +  white-space:  nowrap;
   772    866   }
   773    867   
   774    868   /* the format for the timeline version links */
   775    869   a.timelineHistLink {
   776    870   }
   777    871   
   778         -/* the format for the timeline version display(no history permission!) */
   779         -span.timelineHistDsp {
   780         -  font-weight: bold;
   781         -}
   782         -
   783         -/* the format for the timeline time display */
   784         -td.timelineTime {
   785         -  text-align: right;
   786         -  vertical-align: top;
   787         -  white-space: nowrap;
   788         -}
   789         -
   790         -/* the format for the grap placeholder cells in timelines */
   791         -td.timelineGraph {
   792         -  text-align: left;
   793         -  vertical-align: top;
   794         -  width: 20px;
   795         -}
   796         -
   797    872   
   798    873   /**************************************
   799    874    * User Edit
   800    875    */
   801    876   
   802    877   /* layout definition for the capabilities box on the user edit detail page */
   803    878   div.ueditCapBox {
................................................................................
   820    895   /* color for capabilities, inherited by developer */
   821    896   span.ueditInheritDeveloper {
   822    897     color: #f00;
   823    898   }
   824    899   
   825    900   /* color for capabilities, inherited by reader */
   826    901   span.ueditInheritReader {
   827         -  color: black;
          902  +  color: #ee0;
   828    903   }
   829    904   
   830    905   /* color for capabilities, inherited by anonymous */
   831    906   span.ueditInheritAnonymous {
   832    907     color: #00f;
   833    908   }
   834    909   
................................................................................
   936   1011     /* no special definitions, class defined, to enable color pickers,
   937   1012     * f.e.:
   938   1013     * ** add the color picker found at http:jscolor.com as java script
   939   1014     * include
   940   1015     * ** to the header and configure the java script file with
   941   1016     * ** 1. use as bindClass :checkinUserColor
   942   1017     * ** 2. change the default hash adding behaviour to ON
   943         -  * ** or change the class defition of element identified by
         1018  +  * ** or change the class definition of element identified by
   944   1019     * id="clrcust"
   945   1020     * ** to a standard jscolor definition with java script in the footer.
   946   1021     * */
   947   1022   }
   948   1023   
   949   1024   /* format for end of content area, to be used to clear page flow. */
   950   1025   div.endContent {
................................................................................
   985   1060     color: #f00;
   986   1061     font-weight: bold;
   987   1062   }
   988   1063   /* format for artifact lines, no longer shunned */
   989   1064   p.noMoreShun {
   990   1065     color: #00f;
   991   1066   }
   992         -/* format for artifact lines beeing shunned */
         1067  +/* format for artifact lines being shunned */
   993   1068   p.shunned {
   994   1069     color: #00f;
   995   1070   }
   996   1071   /* a broken hyperlink */
   997   1072   span.brokenlink {
   998   1073     color: #f00;
   999   1074   }

Changes to skins/xekri/details.txt.

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

Changes to skins/xekri/footer.txt.

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

Changes to skins/xekri/header.txt.

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

Changes to src/add.c.

    21     21   #include "config.h"
    22     22   #include "add.h"
    23     23   #include <assert.h>
    24     24   #include <dirent.h>
    25     25   #include "cygsup.h"
    26     26   
    27     27   /*
    28         -** WARNING: For Fossil version 1.x this value was always zero.  For Fossil
    29         -**          2.x, it will probably always be one.  When this value is zero,
           28  +** WARNING: For Fossil version x.x this value was always zero.  For Fossil-NG
           29  +**          it will probably always be one.  When this value is zero,
    30     30   **          files in the checkout will not be moved by the "mv" command and
    31     31   **          files in the checkout will not be removed by the "rm" command.
    32     32   **
    33     33   **          If the FOSSIL_ENABLE_LEGACY_MV_RM compile-time option is used,
    34     34   **          the "mv-rm-files" setting will be consulted instead of using
    35     35   **          this value.
    36     36   **
    37         -**          To retain the Fossil version 1.x behavior when using Fossil 2.x,
           37  +**          To retain the Fossil version 2.x behavior when using Fossil-NG
    38     38   **          the FOSSIL_ENABLE_LEGACY_MV_RM compile-time option must be used
    39     39   **          -AND- the "mv-rm-files" setting must be set to zero.
    40     40   */
    41     41   #ifndef FOSSIL_MV_RM_FILE
    42     42   #define FOSSIL_MV_RM_FILE                        (0)
    43     43   #endif
    44     44   
................................................................................
   183    183     if( db_exists("SELECT 1 FROM vfile"
   184    184                   " WHERE pathname=%Q %s", zPath, filename_collation()) ){
   185    185       db_multi_exec("UPDATE vfile SET deleted=0"
   186    186                     " WHERE pathname=%Q %s AND deleted",
   187    187                     zPath, filename_collation());
   188    188     }else{
   189    189       char *zFullname = mprintf("%s%s", g.zLocalRoot, zPath);
   190         -    int isExe = file_wd_isexe(zFullname);
          190  +    int isExe = file_isexe(zFullname, RepoFILE);
   191    191       db_multi_exec(
   192    192         "INSERT INTO vfile(vid,deleted,rid,mrid,pathname,isexe,islink)"
   193    193         "VALUES(%d,0,0,0,%Q,%d,%d)",
   194         -      vid, zPath, isExe, file_wd_islink(0));
          194  +      vid, zPath, isExe, file_islink(0));
   195    195       fossil_free(zFullname);
   196    196     }
   197    197     if( db_changes() ){
   198    198       fossil_print("ADDED  %s\n", zPath);
   199    199       return 1;
   200    200     }else{
   201    201       fossil_print("SKIP   %s\n", zPath);
................................................................................
   324    324       /* file_tree_name() throws a fatal error if g.argv[i] is outside of the
   325    325       ** checkout. */
   326    326       file_tree_name(g.argv[i], &fullName, 0, 1);
   327    327       blob_reset(&fullName);
   328    328   
   329    329       file_canonical_name(g.argv[i], &fullName, 0);
   330    330       zName = blob_str(&fullName);
   331         -    isDir = file_wd_isdir(zName);
          331  +    isDir = file_isdir(zName, RepoFILE);
   332    332       if( isDir==1 ){
   333    333         vfile_scan(&fullName, nRoot-1, scanFlags, pClean, pIgnore);
   334    334       }else if( isDir==0 ){
   335    335         fossil_warning("not found: %s", zName);
   336    336       }else{
   337    337         char *zTreeName = &zName[nRoot];
   338    338         if( !forceFlag && glob_match(pIgnore, zTreeName) ){
................................................................................
   693    693     );
   694    694     while( db_step(&q)==SQLITE_ROW ){
   695    695       const char *zFile;
   696    696       const char *zPath;
   697    697   
   698    698       zFile = db_column_text(&q, 0);
   699    699       zPath = db_column_text(&q, 1);
   700         -    if( !file_wd_isfile_or_link(zPath) ){
          700  +    if( !file_isfile_or_link(zPath) ){
   701    701         if( !dryRunFlag ){
   702    702           db_multi_exec("UPDATE vfile SET deleted=1 WHERE pathname=%Q", zFile);
   703    703         }
   704    704         fossil_print("DELETED  %s\n", zFile);
   705    705         nDelete++;
   706    706       }
   707    707     }
................................................................................
   795    795     Stmt move;
   796    796     if( db_table_exists("temp", "fmove") ){
   797    797       db_prepare(&move, "SELECT x, y FROM fmove ORDER BY x;");
   798    798       while( db_step(&move)==SQLITE_ROW ){
   799    799         const char *zOldName = db_column_text(&move, 0);
   800    800         const char *zNewName = db_column_text(&move, 1);
   801    801         if( !dryRunFlag ){
   802         -        int isOldDir = file_isdir(zOldName);
          802  +        int isOldDir = file_isdir(zOldName, RepoFILE);
   803    803           if( isOldDir==1 ){
   804         -          int isNewDir = file_isdir(zNewName);
          804  +          int isNewDir = file_isdir(zNewName, RepoFILE);
   805    805             if( isNewDir==0 ){
   806    806               file_rename(zOldName, zNewName, isOldDir, isNewDir);
   807    807             }
   808    808           }else{
   809         -          if( file_wd_islink(zOldName) ){
          809  +          if( file_islink(zOldName) ){
   810    810               symlink_copy(zOldName, zNewName);
   811    811             }else{
   812    812               file_copy(zOldName, zNewName);
   813    813             }
   814    814             file_delete(zOldName);
   815    815           }
   816    816         }
................................................................................
   898    898     file_tree_name(zDest, &dest, 0, 1);
   899    899     db_multi_exec(
   900    900       "UPDATE vfile SET origname=pathname WHERE origname IS NULL;"
   901    901     );
   902    902     db_multi_exec(
   903    903       "CREATE TEMP TABLE mv(f TEXT UNIQUE ON CONFLICT IGNORE, t TEXT);"
   904    904     );
   905         -  if( file_wd_isdir(zDest)!=1 ){
          905  +  if( file_isdir(zDest, RepoFILE)!=1 ){
   906    906       Blob orig;
   907    907       if( g.argc!=4 ){
   908    908         usage("OLDNAME NEWNAME");
   909    909       }
   910    910       file_tree_name(g.argv[2], &orig, 0, 1);
   911    911       db_multi_exec(
   912    912         "INSERT INTO mv VALUES(%B,%B)", &orig, &dest

Changes to src/allrepo.c.

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

Changes to src/attach.c.

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

Changes to src/blob.c.

   781    781     }
   782    782     return blob_size(pBlob);
   783    783   }
   784    784   
   785    785   /*
   786    786   ** Initialize a blob to be the content of a file.  If the filename
   787    787   ** is blank or "-" then read from standard input.
          788  +**
          789  +** If zFilename is a symbolic link, behavior depends on the eFType
          790  +** parameter:
          791  +**
          792  +**    *  If eFType is ExtFILE or allow-symlinks is OFF, then the
          793  +**       pBlob is initialized to the *content* of the object to which
          794  +**       the zFilename symlink points.
          795  +**
          796  +**    *  If eFType is RepoFILE and allow-symlinks is ON, then the
          797  +**       pBlob is initialized to the *name* of the object to which
          798  +**       the zFilename symlink points.
   788    799   **
   789    800   ** Any prior content of the blob is discarded, not freed.
   790    801   **
   791    802   ** Return the number of bytes read. Calls fossil_fatal() on error (i.e.
   792    803   ** it exit()s and does not return).
   793    804   */
   794         -sqlite3_int64 blob_read_from_file(Blob *pBlob, const char *zFilename){
          805  +sqlite3_int64 blob_read_from_file(
          806  +  Blob *pBlob,               /* The blob to be initialized */
          807  +  const char *zFilename,     /* Extract content from this file */
          808  +  int eFType                 /* ExtFILE or RepoFILE - see above */
          809  +){
   795    810     sqlite3_int64 size, got;
   796    811     FILE *in;
   797    812     if( zFilename==0 || zFilename[0]==0
   798    813           || (zFilename[0]=='-' && zFilename[1]==0) ){
   799    814       return blob_read_from_channel(pBlob, stdin, -1);
   800    815     }
   801         -  size = file_wd_size(zFilename);
          816  +  if( file_islink(zFilename) ){
          817  +    return blob_read_link(pBlob, zFilename);
          818  +  }
          819  +  size = file_size(zFilename, eFType);
   802    820     blob_zero(pBlob);
   803    821     if( size<0 ){
   804    822       fossil_fatal("no such file: %s", zFilename);
   805    823     }
   806    824     if( size==0 ){
   807    825       return 0;
   808    826     }
................................................................................
   840    858     return len;
   841    859   #else
   842    860     blob_zero(pBlob);
   843    861     return 0;
   844    862   #endif
   845    863   }
   846    864   
   847         -
   848    865   /*
   849    866   ** Write the content of a blob into a file.
   850    867   **
   851    868   ** If the filename is blank or "-" then write to standard output.
          869  +**
          870  +** This routine always assumes ExtFILE.  If zFilename is a symbolic link
          871  +** then the content is written into the object that symbolic link points
          872  +** to, not into the symbolic link itself.  This is true regardless of
          873  +** the allow-symlinks setting.
   852    874   **
   853    875   ** Return the number of bytes written.
   854    876   */
   855    877   int blob_write_to_file(Blob *pBlob, const char *zFilename){
   856    878     FILE *out;
   857    879     int nWrote;
   858    880   
................................................................................
   866    888   #endif
   867    889       nWrote = fwrite(blob_buffer(pBlob), 1, blob_size(pBlob), stdout);
   868    890   #if defined(_WIN32)
   869    891       fflush(stdout);
   870    892       _setmode(_fileno(stdout), _O_TEXT);
   871    893   #endif
   872    894     }else{
   873         -    file_mkfolder(zFilename, 1, 0);
          895  +    file_mkfolder(zFilename, ExtFILE, 1, 0);
   874    896       out = fossil_fopen(zFilename, "wb");
   875    897       if( out==0 ){
   876    898   #if _WIN32
   877    899         const char *zReserved = file_is_win_reserved(zFilename);
   878    900         if( zReserved ){
   879    901           fossil_fatal("cannot open \"%s\" because \"%s\" is "
   880    902                "a reserved name on Windows", zFilename, zReserved);
................................................................................
   931    953   ** Run compression on INPUTFILE and write the result into OUTPUTFILE.
   932    954   **
   933    955   ** This is used to test and debug the blob_compress() routine.
   934    956   */
   935    957   void compress_cmd(void){
   936    958     Blob f;
   937    959     if( g.argc!=4 ) usage("INPUTFILE OUTPUTFILE");
   938         -  blob_read_from_file(&f, g.argv[2]);
          960  +  blob_read_from_file(&f, g.argv[2], ExtFILE);
   939    961     blob_compress(&f, &f);
   940    962     blob_write_to_file(&f, g.argv[3]);
   941    963   }
   942    964   
   943    965   /*
   944    966   ** Compress the concatenation of a blobs pIn1 and pIn2.  Store the result
   945    967   ** in pOut.
................................................................................
   990   1012   ** content, then write results into OUT.
   991   1013   **
   992   1014   ** This is used to test and debug the blob_compress2() routine.
   993   1015   */
   994   1016   void compress2_cmd(void){
   995   1017     Blob f1, f2;
   996   1018     if( g.argc!=5 ) usage("INPUTFILE1 INPUTFILE2 OUTPUTFILE");
   997         -  blob_read_from_file(&f1, g.argv[2]);
   998         -  blob_read_from_file(&f2, g.argv[3]);
         1019  +  blob_read_from_file(&f1, g.argv[2], ExtFILE);
         1020  +  blob_read_from_file(&f2, g.argv[3], ExtFILE);
   999   1021     blob_compress2(&f1, &f2, &f1);
  1000   1022     blob_write_to_file(&f1, g.argv[4]);
  1001   1023   }
  1002   1024   
  1003   1025   /*
  1004   1026   ** Uncompress blob pIn and store the result in pOut.  It is ok for pIn and
  1005   1027   ** pOut to be the same blob.
................................................................................
  1042   1064   ** Read the content of file IN, uncompress that content, and write the
  1043   1065   ** result into OUT.  This command is intended for testing of the
  1044   1066   ** blob_compress() function.
  1045   1067   */
  1046   1068   void uncompress_cmd(void){
  1047   1069     Blob f;
  1048   1070     if( g.argc!=4 ) usage("INPUTFILE OUTPUTFILE");
  1049         -  blob_read_from_file(&f, g.argv[2]);
         1071  +  blob_read_from_file(&f, g.argv[2], ExtFILE);
  1050   1072     blob_uncompress(&f, &f);
  1051   1073     blob_write_to_file(&f, g.argv[3]);
  1052   1074   }
  1053   1075   
  1054   1076   /*
  1055   1077   ** COMMAND: test-cycle-compress
  1056   1078   **
................................................................................
  1057   1079   ** Compress and uncompress each file named on the command line.
  1058   1080   ** Verify that the original content is recovered.
  1059   1081   */
  1060   1082   void test_cycle_compress(void){
  1061   1083     int i;
  1062   1084     Blob b1, b2, b3;
  1063   1085     for(i=2; i<g.argc; i++){
  1064         -    blob_read_from_file(&b1, g.argv[i]);
         1086  +    blob_read_from_file(&b1, g.argv[i], ExtFILE);
  1065   1087       blob_compress(&b1, &b2);
  1066   1088       blob_uncompress(&b2, &b3);
  1067   1089       if( blob_compare(&b1, &b3) ){
  1068   1090         fossil_fatal("compress/uncompress cycle failed for %s", g.argv[i]);
  1069   1091       }
  1070   1092       blob_reset(&b1);
  1071   1093       blob_reset(&b2);

Changes to src/branch.c.

   391    391     style_header("Branches");
   392    392     style_adunit_config(ADUNIT_RIGHT_OK);
   393    393     style_submenu_checkbox("colors", "Use Branch Colors", 0, 0);
   394    394     login_anonymous_available();
   395    395   
   396    396     db_prepare(&q, brlistQuery/*works-like:""*/);
   397    397     rNow = db_double(0.0, "SELECT julianday('now')");
   398         -  @ <div class="brlist"><table id="branchlisttable">
          398  +  @ <div class="brlist">
          399  +  @ <table class='sortable' data-column-types='tkNtt' data-init-sort='2'>
   399    400     @ <thead><tr>
   400    401     @ <th>Branch Name</th>
   401    402     @ <th>Age</th>
   402    403     @ <th>Check-ins</th>
   403    404     @ <th>Status</th>
   404    405     @ <th>Resolution</th>
   405    406     @ </tr></thead><tbody>
................................................................................
   437    438       }else{
   438    439         @ <td></td>
   439    440       }
   440    441       @ </tr>
   441    442     }
   442    443     @ </tbody></table></div>
   443    444     db_finalize(&q);
   444         -  output_table_sorting_javascript("branchlisttable","tkNtt",2);
          445  +  style_table_sorter();
   445    446     style_footer();
   446    447   }
   447    448   
   448    449   /*
   449    450   ** WEBPAGE: brlist
   450    451   ** Show a list of branches.  With no query parameters, a sortable table
   451    452   ** is used to show all branches.  If query parameters are present a

Changes to src/browse.c.

   195    195       @ </h2>
   196    196       zSubdirLink = mprintf("%R/dir?ci=%!S&name=%T", zUuid, zPrefix);
   197    197       if( nD==0 ){
   198    198         style_submenu_element("File Ages", "%R/fileage?name=%!S", zUuid);
   199    199       }
   200    200     }else{
   201    201       @ <h2>The union of all files from all check-ins
   202         -    @ %s(blob_str(&dirname))</h2>
          202  +    @ %s(blob_str(&dirname))
          203  +    if( zD ){
          204  +      @ &nbsp;&nbsp;%z(href("%R/timeline?chng=%T/*", zD))[history]</a>
          205  +    }
          206  +    @ </h2>
   203    207       zSubdirLink = mprintf("%R/dir?name=%T", zPrefix);
   204    208     }
   205    209     style_submenu_element("All", "%s", url_render(&sURI, "ci", 0, 0, 0));
   206    210     style_submenu_element("Tree-View", "%s",
   207    211                           url_render(&sURI, "type", "tree", 0, 0));
   208    212   
   209    213     /* Compute the temporary table "localfiles" containing the names
................................................................................
   841    845         while( nClose-- > 0 ){
   842    846           @ </ul>
   843    847         }
   844    848       }
   845    849     }
   846    850     @ </ul>
   847    851     @ </ul></div>
   848         -  @ <script>(function(){
   849         -  @ function isExpanded(ul){
   850         -  @   return ul.className=='';
   851         -  @ }
   852         -  @
   853         -  @ function toggleDir(ul, useInitValue){
   854         -  @   if( !useInitValue ){
   855         -  @     expandMap[ul.id] = !isExpanded(ul);
   856         -  @     history.replaceState(expandMap, '');
   857         -  @   }
   858         -  @   ul.className = expandMap[ul.id] ? '' : 'collapsed';
   859         -  @ }
   860         -  @
   861         -  @ function toggleAll(tree, useInitValue){
   862         -  @   var lists = tree.querySelectorAll('.subdir > ul > li ul');
   863         -  @   if( !useInitValue ){
   864         -  @     var expand = true;  /* Default action: make all sublists visible */
   865         -  @     for( var i=0; lists[i]; i++ ){
   866         -  @       if( isExpanded(lists[i]) ){
   867         -  @         expand = false; /* Any already visible - make them all hidden */
   868         -  @         break;
   869         -  @       }
   870         -  @     }
   871         -  @     expandMap = {'*': expand};
   872         -  @     history.replaceState(expandMap, '');
   873         -  @   }
   874         -  @   var className = expandMap['*'] ? '' : 'collapsed';
   875         -  @   for( var i=0; lists[i]; i++ ){
   876         -  @     lists[i].className = className;
   877         -  @   }
   878         -  @ }
   879         -  @
   880         -  @ function checkState(){
   881         -  @   expandMap = history.state || {};
   882         -  @   if( '*' in expandMap ) toggleAll(outer_ul, true);
   883         -  @   for( var id in expandMap ){
   884         -  @     if( id!=='*' ) toggleDir(gebi(id), true);
   885         -  @   }
   886         -  @ }
   887         -  @
   888         -  @ function belowSubdir(node){
   889         -  @   do{
   890         -  @     node = node.parentNode;
   891         -  @     if( node==subdir ) return true;
   892         -  @   } while( node && node!=outer_ul );
   893         -  @   return false;
   894         -  @ }
   895         -  @
   896         -  @ var history = window.history || {};
   897         -  @ if( !history.replaceState ) history.replaceState = function(){};
   898         -  @ var outer_ul = document.querySelector('.filetree > ul');
   899         -  @ var subdir = outer_ul.querySelector('.subdir');
   900         -  @ var expandMap = {};
   901         -  @ checkState();
   902         -  @ outer_ul.onclick = function(e){
   903         -  @   e = e || window.event;
   904         -  @   var a = e.target || e.srcElement;
   905         -  @   if( a.nodeName!='A' ) return true;
   906         -  @   if( a.parentNode.parentNode==subdir ){
   907         -  @     toggleAll(outer_ul);
   908         -  @     return false;
   909         -  @   }
   910         -  @   if( !belowSubdir(a) ) return true;
   911         -  @   var ul = a.parentNode.nextSibling;
   912         -  @   while( ul && ul.nodeName!='UL' ) ul = ul.nextSibling;
   913         -  @   if( !ul ) return true; /* This is a file link, not a directory */
   914         -  @   toggleDir(ul);
   915         -  @   return false;
   916         -  @ }
   917         -  @ }())</script>
          852  +  style_load_one_js_file("tree.js");
   918    853     style_footer();
   919    854   
   920    855     /* We could free memory used by sTree here if we needed to.  But
   921    856     ** the process is about to exit, so doing so would not really accomplish
   922    857     ** anything useful. */
   923    858   }
   924    859   
................................................................................
  1144   1079         }else{
  1145   1080           @ %z(href("%R/artifact/%!S",zFUuid))%h(zFile)</a><br />
  1146   1081         }
  1147   1082       }
  1148   1083       db_reset(&q2);
  1149   1084       @ </td>
  1150   1085       @ <td>
  1151         -    @ %z(href("%R/info/%!S",zUuid))[%S(zUuid)]</a>
         1086  +    @ %W(zComment)
         1087  +    @ (check-in:&nbsp;%z(href("%R/ci/%!S",zUuid))%S(zUuid)</a>,
  1152   1088       if( showId ){
  1153         -      @ (%d(mid))
         1089  +      @ id: %d(mid)
  1154   1090       }
  1155         -    @ %W(zComment) (user:
  1156         -    @ %z(href("%R/timeline?u=%t&c=%!S&nd&n=200",zUser,zUuid))%h(zUser)</a>,
  1157         -    @ branch:
  1158         -    @ %z(href("%R/timeline?r=%t&c=%!S&nd&n=200",zBranch,zUuid))%h(zBranch)</a>)
         1091  +    @ user:&nbsp;%z(href("%R/timeline?u=%t&c=%!S&nd",zUser,zUuid))%h(zUser)</a>,
         1092  +    @ branch:&nbsp;\
         1093  +    @ %z(href("%R/timeline?r=%t&c=%!S&nd",zBranch,zUuid))%h(zBranch)</a>)
  1159   1094       @ </td></tr>
  1160   1095       @
  1161   1096       fossil_free(zAge);
  1162   1097     }
  1163   1098     @ </table></div>
  1164   1099     db_finalize(&q1);
  1165   1100     db_finalize(&q2);
  1166   1101     style_footer();
  1167   1102   }

Changes to src/builtin.c.

    62     62   */
    63     63   void test_builtin_list(void){
    64     64     int i;
    65     65     for(i=0; i<count(aBuiltinFiles); i++){
    66     66       fossil_print("%-30s %6d\n", aBuiltinFiles[i].zName,aBuiltinFiles[i].nByte);
    67     67     }
    68     68   }
           69  +
           70  +/*
           71  +** WEBPAGE: test-builtin-files
           72  +**
           73  +** Show all built-in text files.
           74  +*/
           75  +void test_builtin_list_page(void){
           76  +  int i;
           77  +  style_header("Built-in Text Files");
           78  +  @ <ul>
           79  +  for(i=0; i<count(aBuiltinFiles); i++){
           80  +    const char *z = aBuiltinFiles[i].zName;
           81  +    @ <li>%z(href("%R/builtin?name=%T&id=%S",z,MANIFEST_UUID))%h(z)</a>
           82  +  }
           83  +  @ </ul>
           84  +  style_footer();
           85  +}
    69     86   
    70     87   /*
    71     88   ** COMMAND: test-builtin-get
    72     89   **
    73     90   ** Usage: %fossil test-builtin-get NAME ?OUTPUT-FILE?
    74     91   */
    75     92   void test_builtin_get(void){

Changes to src/bundle.c.

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

Changes to src/cache.c.

    47     47     sqlite3 *db = 0;
    48     48     int rc;
    49     49     i64 sz;
    50     50   
    51     51     zDbName = cacheName();
    52     52     if( zDbName==0 ) return 0;
    53     53     if( bForce==0 ){
    54         -    sz = file_size(zDbName);
           54  +    sz = file_size(zDbName, ExtFILE);
    55     55       if( sz<=0 ){
    56     56         fossil_free(zDbName);
    57     57         return 0;
    58     58       }
    59     59     }
    60     60     rc = sqlite3_open(zDbName, &db);
    61     61     fossil_free(zDbName);
................................................................................
   315    315                sqlite3_column_text(pStmt, 0));
   316    316             nEntry++;
   317    317           }
   318    318           sqlite3_finalize(pStmt);
   319    319         }
   320    320         sqlite3_close(db);
   321    321         fossil_print("Entries: %d  Cache-file Size: %lld\n",
   322         -                   nEntry, file_size(zDbName));
          322  +                   nEntry, file_size(zDbName, ExtFILE));
   323    323         fossil_free(zDbName);
   324    324       }
   325    325     }else if( strncmp(zCmd, "status", nCmd)==0 ){
   326    326       fossil_print("TBD...\n");
   327    327     }else{
   328    328       fossil_fatal("Unknown subcommand \"%s\"."
   329    329                    " Should be one of: clear init list status", zCmd);
................................................................................
   363    363           @ hit-count: %d(sqlite3_column_int(pStmt,2))
   364    364           @ last-access: %s(sqlite3_column_text(pStmt,3))</p></li>
   365    365         }
   366    366         sqlite3_finalize(pStmt);
   367    367         @ </ol>
   368    368       }
   369    369       zDbName = cacheName();
   370         -    bigSizeName(sizeof(zBuf), zBuf, file_size(zDbName));
          370  +    bigSizeName(sizeof(zBuf), zBuf, file_size(zDbName, ExtFILE));
   371    371       @ <p>cache-file name: %h(zDbName)</p>
   372    372       @ <p>cache-file size: %s(zBuf)</p>
   373    373       fossil_free(zDbName);
   374    374       sqlite3_close(db);
   375    375     }
   376    376     style_footer();
   377    377   }

Changes to src/cgi.c.

   207    207   void cgi_set_cookie(
   208    208     const char *zName,    /* Name of the cookie */
   209    209     const char *zValue,   /* Value of the cookie.  Automatically escaped */
   210    210     const char *zPath,    /* Path cookie applies to.  NULL means "/" */
   211    211     int lifetime          /* Expiration of the cookie in seconds from now */
   212    212   ){
   213    213     char *zSecure = "";
   214         -  if( zPath==0 ) zPath = g.zTop;
          214  +  if( zPath==0 ){
          215  +    zPath = g.zTop;
          216  +    if( zPath[0]==0 ) zPath = "/";
          217  +  }
   215    218     if( g.zBaseURL!=0 && strncmp(g.zBaseURL, "https:", 6)==0 ){
   216    219       zSecure = " secure;";
   217    220     }
   218    221     if( lifetime>0 ){
   219    222       lifetime += (int)time(0);
   220    223       blob_appendf(&extraHeader,
   221    224          "Set-Cookie: %s=%t; Path=%s; expires=%z; HttpOnly;%s Version=1\r\n",
................................................................................
  1338   1341   /* z[] is the value of an X-FORWARDED-FOR: line in an HTTP header.
  1339   1342   ** Return a pointer to a string containing the real IP address, or a
  1340   1343   ** NULL pointer to stick with the IP address previously computed and
  1341   1344   ** loaded into g.zIpAddr.
  1342   1345   */
  1343   1346   static const char *cgi_accept_forwarded_for(const char *z){
  1344   1347     int i;
  1345         -  if( fossil_strcmp(g.zIpAddr, "127.0.0.1")!=0 ) return 0;
  1346         -
         1348  +  if( !cgi_is_loopback(g.zIpAddr) ){
         1349  +    /* Only accept X-FORWARDED-FOR if input coming from the local machine */
         1350  +    return 0;
         1351  +  }
  1347   1352     i = strlen(z)-1;
  1348   1353     while( i>=0 && z[i]!=',' && !fossil_isspace(z[i]) ) i--;
  1349   1354     return &z[++i];
  1350   1355   }
  1351   1356   
  1352   1357   /*
  1353   1358   ** Remove the first space-delimited token from a string and return
................................................................................
  1749   1754   #define HTTP_SERVER_HAD_CHECKOUT   0x0008     /* Was a checkout open? */
  1750   1755   #define HTTP_SERVER_REPOLIST       0x0010     /* Allow repo listing */
  1751   1756   
  1752   1757   #endif /* INTERFACE */
  1753   1758   
  1754   1759   /*
  1755   1760   ** Maximum number of child processes that we can have running
  1756         -** at one time before we start slowing things down.
         1761  +** at one time.  Set this to 0 for "no limit".
  1757   1762   */
  1758         -#define MAX_PARALLEL 2
         1763  +#ifndef FOSSIL_MAX_CONNECTIONS
         1764  +# define FOSSIL_MAX_CONNECTIONS 1000
         1765  +#endif
  1759   1766   
  1760   1767   /*
  1761   1768   ** Implement an HTTP server daemon listening on port iPort.
  1762   1769   **
  1763   1770   ** As new connections arrive, fork a child and let child return
  1764   1771   ** out of this procedure call.  The child will handle the request.
  1765   1772   ** The parent never returns from this procedure.
................................................................................
  1775   1782   ){
  1776   1783   #if defined(_WIN32)
  1777   1784     /* Use win32_http_server() instead */
  1778   1785     fossil_exit(1);
  1779   1786   #else
  1780   1787     int listener = -1;           /* The server socket */
  1781   1788     int connection;              /* A socket for each individual connection */
         1789  +  int nRequest = 0;            /* Number of requests handled so far */
  1782   1790     fd_set readfds;              /* Set of file descriptors for select() */
  1783   1791     socklen_t lenaddr;           /* Length of the inaddr structure */
  1784   1792     int child;                   /* PID of the child process */
  1785   1793     int nchildren = 0;           /* Number of child processes */
  1786   1794     struct timeval delay;        /* How long to wait inside select() */
  1787   1795     struct sockaddr_in inaddr;   /* The socket address */
  1788   1796     int opt = 1;                 /* setsockopt flag */
................................................................................
  1845   1853       }else
  1846   1854   #endif
  1847   1855       if( system(zBrowser)<0 ){
  1848   1856         fossil_warning("cannot start browser: %s\n", zBrowser);
  1849   1857       }
  1850   1858     }
  1851   1859     while( 1 ){
  1852         -    if( nchildren>MAX_PARALLEL ){
  1853         -      /* Slow down if connections are arriving too fast */
  1854         -      sleep( nchildren-MAX_PARALLEL );
         1860  +#if FOSSIL_MAX_CONNECTIONS>0
         1861  +    while( nchildren>=FOSSIL_MAX_CONNECTIONS ){
         1862  +      if( wait(0)>=0 ) nchildren--;
  1855   1863       }
  1856         -    delay.tv_sec = 60;
  1857         -    delay.tv_usec = 0;
         1864  +#endif
         1865  +    delay.tv_sec = 0;
         1866  +    delay.tv_usec = 100000;
  1858   1867       FD_ZERO(&readfds);
  1859   1868       assert( listener>=0 );
  1860   1869       FD_SET( listener, &readfds);
  1861   1870       select( listener+1, &readfds, 0, 0, &delay);
  1862   1871       if( FD_ISSET(listener, &readfds) ){
  1863   1872         lenaddr = sizeof(inaddr);
  1864   1873         connection = accept(listener, (struct sockaddr*)&inaddr, &lenaddr);
  1865   1874         if( connection>=0 ){
  1866   1875           child = fork();
  1867   1876           if( child!=0 ){
  1868         -          if( child>0 ) nchildren++;
         1877  +          if( child>0 ){
         1878  +            nchildren++;
         1879  +            nRequest++;
         1880  +          }
  1869   1881             close(connection);
  1870   1882           }else{
  1871   1883             int nErr = 0, fd;
  1872   1884             close(0);
  1873   1885             fd = dup(connection);
  1874   1886             if( fd!=0 ) nErr++;
  1875   1887             close(1);
................................................................................
  1877   1889             if( fd!=1 ) nErr++;
  1878   1890             if( !g.fAnyTrace ){
  1879   1891               close(2);
  1880   1892               fd = dup(connection);
  1881   1893               if( fd!=2 ) nErr++;
  1882   1894             }
  1883   1895             close(connection);
         1896  +          g.nPendingRequest = nchildren+1;
         1897  +          g.nRequest = nRequest+1;
  1884   1898             return nErr;
  1885   1899           }
  1886   1900         }
  1887   1901       }
  1888   1902       /* Bury dead children */
  1889         -    while( waitpid(0, 0, WNOHANG)>0 ){
  1890         -      nchildren--;
  1891         -    }
         1903  +    if( nchildren ){
         1904  +      while(1){
         1905  +        int iStatus = 0;
         1906  +        pid_t x = waitpid(-1, &iStatus, WNOHANG);
         1907  +        if( x<=0 ) break;
         1908  +        nchildren--;
         1909  +      }
         1910  +    }  
  1892   1911     }
  1893   1912     /* NOT REACHED */
  1894   1913     fossil_exit(1);
  1895   1914   #endif
  1896   1915     /* NOT REACHED */
  1897   1916     return 0;
  1898   1917   }
................................................................................
  2012   2031       if( (zIndex = strchr(zSshClient,' '))!=0 ){
  2013   2032         zSshClient[zIndex-zSshClient] = '\0';
  2014   2033         return zSshClient;
  2015   2034       }
  2016   2035     }
  2017   2036     return zDefault;
  2018   2037   }
         2038  +
         2039  +/*
         2040  +** Return true if information is coming from the loopback network.
         2041  +*/
         2042  +int cgi_is_loopback(const char *zIpAddr){
         2043  +  return fossil_strcmp(zIpAddr, "127.0.0.1")==0 ||
         2044  +         fossil_strcmp(zIpAddr, "::ffff:127.0.0.1")==0 ||
         2045  +         fossil_strcmp(zIpAddr, "::1")==0;
         2046  +}

Changes to src/checkin.c.

    84     84       blob_init(&name, g.zLocalRoot, nRoot - 1);
    85     85       vfile_scan(&name, blob_size(&name), scanFlags, pIgnore, 0);
    86     86       blob_reset(&name);
    87     87     }else{
    88     88       for(i=0; i<argc; i++){
    89     89         file_canonical_name(argv[i], &name, 0);
    90     90         zName = blob_str(&name);
    91         -      isDir = file_wd_isdir(zName);
           91  +      isDir = file_isdir(zName, RepoFILE);
    92     92         if( isDir==1 ){
    93     93           vfile_scan(&name, nRoot-1, scanFlags, pIgnore, 0);
    94     94         }else if( isDir==0 ){
    95     95           fossil_warning("not found: %s", &zName[nRoot]);
    96     96         }else if( file_access(zName, R_OK) ){
    97     97           fossil_fatal("cannot open %s", &zName[nRoot]);
    98     98         }else{
................................................................................
   207    207       const char *zMtime = db_column_text(&q, 1);
   208    208       int size = db_column_int(&q, 2);
   209    209       int isDeleted = db_column_int(&q, 3);
   210    210       int isChnged = db_column_int(&q, 4);
   211    211       int isNew = isManaged && !db_column_int(&q, 5);
   212    212       int isRenamed = db_column_int(&q, 6);
   213    213       char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname);
   214         -    int isMissing = !file_wd_isfile_or_link(zFullName);
          214  +    int isMissing = !file_isfile_or_link(zFullName);
   215    215   
   216    216       /* Determine the file change classification, if any. */
   217    217       if( isDeleted ){
   218    218         if( flags & C_DELETED ){
   219    219           zClass = "DELETED";
   220    220         }
   221    221       }else if( isMissing ){
................................................................................
   252    252         zClass = "EXECUTABLE";
   253    253       }else if( (flags & C_META) && isChnged==7 ){
   254    254         zClass = "SYMLINK";
   255    255       }else if( (flags & C_META) && isChnged==8 ){
   256    256         zClass = "UNEXEC";
   257    257       }else if( (flags & C_META) && isChnged==9 ){
   258    258         zClass = "UNLINK";
   259         -    }else if( (flags & C_CONFLICT) && isChnged && !file_wd_islink(zFullName)
          259  +    }else if( (flags & C_CONFLICT) && isChnged && !file_islink(zFullName)
   260    260              && file_contains_merge_marker(zFullName) ){
   261    261         zClass = "CONFLICT";
   262    262       }else if( (flags & (C_EDITED | C_CHANGED)) && isChnged
   263    263              && (isChnged<2 || isChnged>9) ){
   264    264         zClass = "EDITED";
   265    265       }else if( (flags & C_RENAMED) && isRenamed ){
   266    266         zClass = "RENAMED";
................................................................................
   469    469     int useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0;
   470    470     int showHdr = command==CHANGES && find_option("header", 0, 0);
   471    471     int verboseFlag = command==CHANGES && find_option("verbose", "v", 0);
   472    472     const char *zIgnoreFlag = find_option("ignore", 0, 1);
   473    473     unsigned scanFlags = 0;
   474    474     unsigned flags = 0;
   475    475     int vid, i;
          476  +
          477  +  fossil_pledge("stdio rpath wpath cpath id flock tty");
   476    478   
   477    479     /* Load affirmative flag options. */
   478    480     for( i=0; i<count(flagDefs); ++i ){
   479    481       if( (command==CHANGES || !(flagDefs[i].mask & C_CLASSIFY))
   480    482        && find_option(flagDefs[i].option, 0, 0) ){
   481    483         flags |= flagDefs[i].mask;
   482    484       }
................................................................................
   762    764       char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname);
   763    765       const char *type = "";
   764    766       if( verboseFlag ){
   765    767         if( isNew ){
   766    768           type = "ADDED      ";
   767    769         }else if( isDeleted ){
   768    770           type = "DELETED    ";
   769         -      }else if( !file_wd_isfile_or_link(zFullName) ){
          771  +      }else if( !file_isfile_or_link(zFullName) ){
   770    772           if( file_access(zFullName, F_OK)==0 ){
   771    773             type = "NOT_A_FILE ";
   772    774           }else{
   773    775             type = "MISSING    ";
   774    776           }
   775    777         }else if( chnged ){
   776    778           if( chnged==2 ){
................................................................................
  1219   1221     if( zEditor ){
  1220   1222       zCmd = mprintf("%s \"%s\"", zEditor, zFile);
  1221   1223       fossil_print("%s\n", zCmd);
  1222   1224       if( fossil_system(zCmd) ){
  1223   1225         fossil_fatal("editor aborted: \"%s\"", zCmd);
  1224   1226       }
  1225   1227   
  1226         -    blob_read_from_file(&reply, zFile);
         1228  +    blob_read_from_file(&reply, zFile, ExtFILE);
  1227   1229     }else{
  1228   1230       char zIn[300];
  1229   1231       blob_zero(&reply);
  1230   1232       while( fgets(zIn, sizeof(zIn), stdin)!=0 ){
  1231   1233         if( zIn[0]=='.' && (zIn[1]==0 || zIn[1]=='\r' || zIn[1]=='\n') ){
  1232   1234           break;
  1233   1235         }
................................................................................
  1560   1562       ** directly from the filesystem.  On windows, permissions are
  1561   1563       ** unchanged from the original.  However, only do this if the file
  1562   1564       ** itself is actually selected to be part of this check-in.
  1563   1565       */
  1564   1566       if( isSelected ){
  1565   1567         int mPerm;
  1566   1568   
  1567         -      mPerm = file_wd_perm(blob_str(&filename));
         1569  +      mPerm = file_perm(blob_str(&filename), RepoFILE);
  1568   1570         isExe = ( mPerm==PERM_EXE );
  1569   1571         isLink = ( mPerm==PERM_LNK );
  1570   1572       }
  1571   1573   #endif
  1572   1574       if( isExe ){
  1573   1575         zPerm = " x";
  1574   1576       }else if( isLink ){
................................................................................
  1906   1908   
  1907   1909       zFullname = db_column_text(&q, 0);
  1908   1910       zName = db_column_text(&q, 1);
  1909   1911       crlfOk = db_column_int(&q, 2);
  1910   1912       binOk = db_column_int(&q, 3);
  1911   1913       encodingOk = db_column_int(&q, 4);
  1912   1914       blob_zero(&content);
  1913         -    if( file_wd_islink(zFullname) ){
  1914         -      blob_read_link(&content, zFullname);
  1915         -    }else{
  1916         -      blob_read_from_file(&content, zFullname);
  1917         -    }
         1915  +    blob_read_from_file(&content, zFullname, RepoFILE);
  1918   1916       blob_zero(&reason);
  1919   1917       fileRc = commit_warning(&content, crlfOk, binOk, encodingOk, 2,
  1920   1918                               zFullname, &reason);
  1921   1919       if( fileRc || verboseFlag ){
  1922   1920         fossil_print("%d\t%s\t%s\n", fileRc, zName, blob_str(&reason));
  1923   1921       }
  1924   1922       blob_reset(&reason);
................................................................................
  2301   2299     }
  2302   2300   
  2303   2301     if( zComment ){
  2304   2302       blob_zero(&comment);
  2305   2303       blob_append(&comment, zComment, -1);
  2306   2304     }else if( zComFile ){
  2307   2305       blob_zero(&comment);
  2308         -    blob_read_from_file(&comment, zComFile);
         2306  +    blob_read_from_file(&comment, zComFile, ExtFILE);
  2309   2307       blob_to_utf8_no_bom(&comment, 1);
  2310   2308     }else if( dryRunFlag ){
  2311   2309       blob_zero(&comment);
  2312   2310     }else if( !noPrompt ){
  2313   2311       char *zInit = db_text(0, "SELECT value FROM vvar WHERE name='ci-comment'");
  2314   2312       prepare_commit_comment(&comment, zInit, &sCiInfo, vid);
  2315   2313       if( zInit && zInit[0] && fossil_strcmp(zInit, blob_str(&comment))==0 ){
................................................................................
  2372   2370       zFullname = db_column_text(&q, 1);
  2373   2371       rid = db_column_int(&q, 2);
  2374   2372       crlfOk = db_column_int(&q, 3);
  2375   2373       binOk = db_column_int(&q, 4);
  2376   2374       encodingOk = db_column_int(&q, 5);
  2377   2375   
  2378   2376       blob_zero(&content);
  2379         -    if( file_wd_islink(zFullname) ){
  2380         -      /* Instead of file content, put link destination path */
  2381         -      blob_read_link(&content, zFullname);
  2382         -    }else{
  2383         -      blob_read_from_file(&content, zFullname);
  2384         -    }
         2377  +    blob_read_from_file(&content, zFullname, RepoFILE);
  2385   2378       /* Do not emit any warnings when they are disabled. */
  2386   2379       if( !noWarningFlag ){
  2387   2380         abortCommit |= commit_warning(&content, crlfOk, binOk,
  2388   2381                                       encodingOk, noPrompt,
  2389   2382                                       zFullname, 0);
  2390   2383       }
  2391   2384       if( contains_merge_marker(&content) ){
................................................................................
  2489   2482     if( dryRunFlag ){
  2490   2483       blob_write_to_file(&manifest, "");
  2491   2484     }
  2492   2485     if( outputManifest & MFESTFLG_RAW ){
  2493   2486       zManifestFile = mprintf("%smanifest", g.zLocalRoot);
  2494   2487       blob_write_to_file(&manifest, zManifestFile);
  2495   2488       blob_reset(&manifest);
  2496         -    blob_read_from_file(&manifest, zManifestFile);
         2489  +    blob_read_from_file(&manifest, zManifestFile, ExtFILE);
  2497   2490       free(zManifestFile);
  2498   2491     }
  2499   2492   
  2500   2493     nvid = content_put(&manifest);
  2501   2494     if( nvid==0 ){
  2502   2495       fossil_fatal("trouble committing manifest: %s", g.zErrMsg);
  2503   2496     }

Changes to src/checkout.c.

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

Added src/ci_edit.js.

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

Changes to src/clearsign.c.

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

Changes to src/clone.c.

   142    142     /* We should be done with options.. */
   143    143     verify_all_options();
   144    144   
   145    145     if( g.argc < 4 ){
   146    146       usage("?OPTIONS? FILE-OR-URL NEW-REPOSITORY");
   147    147     }
   148    148     db_open_config(0, 0);
   149         -  if( -1 != file_size(g.argv[3]) ){
          149  +  if( -1 != file_size(g.argv[3], ExtFILE) ){
   150    150       fossil_fatal("file already exists: %s", g.argv[3]);
   151    151     }
   152    152   
   153    153     url_parse(g.argv[2], urlFlags);
   154    154     if( zDefaultUser==0 && g.url.user!=0 ) zDefaultUser = g.url.user;
   155    155     if( g.url.isFile ){
   156    156       file_copy(g.url.name, g.argv[3]);
................................................................................
   164    164       if( zDefaultUser ){
   165    165         g.zLogin = zDefaultUser;
   166    166       }else{
   167    167         g.zLogin = db_text(0, "SELECT login FROM user WHERE cap LIKE '%%s%%'");
   168    168       }
   169    169       fossil_print("Repository cloned into %s\n", g.argv[3]);
   170    170     }else{
          171  +    db_close_config();
   171    172       db_create_repository(g.argv[3]);
   172    173       db_open_repository(g.argv[3]);
          174  +    db_open_config(0,0);
   173    175       db_begin_transaction();
   174    176       db_record_repository_filename(g.argv[3]);
   175    177       db_initial_setup(0, 0, zDefaultUser);
   176    178       user_select();
   177    179       db_set("content-schema", CONTENT_SCHEMA, 0);
   178    180       db_set("aux-schema", AUX_SCHEMA_MAX, 0);
   179    181       db_set("rebuilt", get_version(), 0);

Changes to src/codecheck1.c.

   139    139       for(i=2; z[i] && z[i]!='\n'; i++){}
   140    140       if( z[i] ){
   141    141         (*pLN)++;
   142    142         i++;
   143    143       }
   144    144       *pType = TK_SPACE;
   145    145       return i;
          146  +  }
          147  +  if( z[0]=='\\' && (z[1]=='\n' || (z[1]=='\r' && z[2]=='\n')) ){
          148  +    *pType = TK_SPACE;
          149  +    return 1;
   146    150     }
   147    151     *pType = TK_OTHER;
   148    152     return 1;
   149    153   }
   150    154   
   151    155   /*
   152    156   ** Return the next non-whitespace token
................................................................................
   199    203   
   200    204   /*
   201    205   ** Return true if the input is a string literal.
   202    206   */
   203    207   static int is_string_lit(const char *z){
   204    208     int nu1, nu2;
   205    209     z = next_non_whitespace(z, &nu1, &nu2);
          210  +  if( strcmp(z, "NULL")==0 ) return 1;
   206    211     return z[0]=='"';
   207    212   }
   208    213   
   209    214   /*
   210    215   ** Return true if the input is an expression of string literals:
   211    216   **
   212    217   **      EXPR ? "..." : "..."
................................................................................
   308    313     const char *zFName;    /* Name of the function */
   309    314     int iFmtArg;           /* Index of format argument.  Leftmost is 1. */
   310    315     unsigned fmtFlags;     /* Processing flags */
   311    316   } aFmtFunc[] = {
   312    317     { "admin_log",               1, 0 },
   313    318     { "blob_append_sql",         2, FMT_NO_S },
   314    319     { "blob_appendf",            2, 0 },
          320  +  { "cgi_debug",               1, 0 },
   315    321     { "cgi_panic",               1, 0 },
          322  +  { "cgi_printf",              1, 0 },
   316    323     { "cgi_redirectf",           1, 0 },
          324  +  { "chref",                   2, 0 },
   317    325     { "db_blob",                 2, FMT_NO_S },
          326  +  { "db_debug",                1, FMT_NO_S },
   318    327     { "db_double",               2, FMT_NO_S },
   319    328     { "db_err",                  1, 0 },
   320    329     { "db_exists",               1, FMT_NO_S },
          330  +  { "db_get_mprintf",          2, 0 },
   321    331     { "db_int",                  2, FMT_NO_S },
   322    332     { "db_int64",                2, FMT_NO_S },
   323    333     { "db_multi_exec",           1, FMT_NO_S },
   324    334     { "db_optional_sql",         2, FMT_NO_S },
   325    335     { "db_prepare",              2, FMT_NO_S },
   326    336     { "db_prepare_ignore_error", 2, FMT_NO_S },
          337  +  { "db_set_mprintf",          3, 0 },
   327    338     { "db_static_prepare",       2, FMT_NO_S },
   328    339     { "db_text",                 2, FMT_NO_S },
          340  +  { "db_unset_mprintf",        2, 0 },
   329    341     { "form_begin",              2, 0 },
   330    342     { "fossil_error",            2, 0 },
   331    343     { "fossil_errorlog",         1, 0 },
   332    344     { "fossil_fatal",            1, 0 },
   333    345     { "fossil_fatal_recursive",  1, 0 },
   334    346     { "fossil_panic",            1, 0 },
   335    347     { "fossil_print",            1, 0 },
   336    348     { "fossil_trace",            1, 0 },
   337    349     { "fossil_warning",          1, 0 },
   338    350     { "href",                    1, 0 },
   339    351     { "json_new_string_f",       1, 0 },
          352  +  { "json_set_err",            2, 0 },
          353  +  { "json_warn",               2, 0 },
   340    354     { "mprintf",                 1, 0 },
   341    355     { "socket_set_errmsg",       1, 0 },
   342    356     { "ssl_set_errmsg",          1, 0 },
   343    357     { "style_header",            1, 0 },
   344    358     { "style_set_current_page",  1, 0 },
          359  +  { "style_submenu_element",   2, 0 },
          360  +  { "style_submenu_sql",       3, 0 },
   345    361     { "webpage_error",           1, 0 },
   346    362     { "xhref",                   2, 0 },
   347    363   };
   348    364   
   349    365   /*
   350    366   ** Determine if the indentifier zIdent of length nIndent is a Fossil
   351    367   ** internal interface that uses a printf-style argument.  Return zero if not.
................................................................................
   464    480              zFilename, lnFCall, szFName, zFCall);
   465    481       nErr++;
   466    482     }else{
   467    483       const char *zFmt = azArg[fmtArg-1];
   468    484       const char *zOverride = strstr(zFmt, "/*works-like:");
   469    485       if( zOverride ) zFmt = zOverride + sizeof("/*works-like:")-1;
   470    486       if( !is_string_lit(zFmt) ){
   471         -      printf("%s:%d: %.*s() has non-constant format string\n",
   472         -             zFilename, lnFCall, szFName, zFCall);
          487  +      printf("%s:%d: %.*s() has non-constant format on arg[%d]\n",
          488  +             zFilename, lnFCall, szFName, zFCall, fmtArg-1);
   473    489         nErr++;
   474    490       }else if( (k = formatArgCount(zFmt, nArg, acType))>=0
   475    491                && nArg!=fmtArg+k ){
   476    492         printf("%s:%d: too %s arguments to %.*s() "
   477    493                "- got %d and expected %d\n",
   478    494                zFilename, lnFCall, (nArg<fmtArg+k ? "few" : "many"),
   479    495                szFName, zFCall, nArg, fmtArg+k);

Changes to src/comformat.c.

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

Changes to src/config.h.

   251    251   # define NORETURN
   252    252   #endif
   253    253   
   254    254   /*
   255    255   ** Number of elements in an array
   256    256   */
   257    257   #define count(X) (sizeof(X)/sizeof(X[0]))
          258  +
          259  +/*
          260  +** The pledge() interface is currently only available on OpenBSD 5.9
          261  +** and later.  Make calls to fossil_pledge() no-ops on all platforms
          262  +** that omit the HAVE_PLEDGE configuration parameter.
          263  +*/
          264  +#if !defined(HAVE_PLEDGE)
          265  +# define fossil_pledge(A)
          266  +#endif
          267  +
   258    268   
   259    269   #endif /* _RC_COMPILE_ */

Changes to src/configure.c.

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

Changes to src/content.c.

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

Added src/cookies.c.

            1  +/*
            2  +** Copyright (c) 2017 D. Richard Hipp
            3  +**
            4  +** This program is free software; you can redistribute it and/or
            5  +** modify it under the terms of the Simplified BSD License (also
            6  +** known as the "2-Clause License" or "FreeBSD License".)
            7  +**
            8  +** This program is distributed in the hope that it will be useful,
            9  +** but without any warranty; without even the implied warranty of
           10  +** merchantability or fitness for a particular purpose.
           11  +**
           12  +** Author contact information:
           13  +**   drh@hwaci.com
           14  +**   http://www.hwaci.com/drh/
           15  +**
           16  +*******************************************************************************
           17  +**
           18  +** This file contains code used to manage a cookie that stores user-specific
           19  +** display preferences for the web interface.
           20  +**
           21  +** cookie_parse(void);
           22  +**
           23  +**    Read and parse the display preferences cookie.
           24  +**
           25  +** cookie_read_parameter(zQP, zPName);
           26  +**
           27  +**    If query parameter zQP does not exist but zPName does exist in
           28  +**    the parsed cookie, then initialize zQP to hold the same value
           29  +**    as the zPName element in the parsed cookie.
           30  +**
           31  +** cookie_write_parameter(zQP, zPName, zDefault);
           32  +**
           33  +**    If query parameter zQP exists and if it has a different value from
           34  +**    the zPName parameter in the parsed cookie, then replace the value of
           35  +**    zPName with the value of zQP.  If zQP exists but zPName does not
           36  +**    exist, then zPName is created.  If zQP does not exist or if it has
           37  +**    the same value as zPName, then this routine is a no-op.
           38  +**
           39  +** cookie_link_parameter(zQP, zPName, zDefault);
           40  +**
           41  +**    This does both cookie_read_parameter() and cookie_write_parameter()
           42  +**    all at once.
           43  +**
           44  +** cookie_render();
           45  +**
           46  +**    If any prior calls to cookie_write_parameter() have changed the
           47  +**    value of the user preferences cookie, this routine will cause the
           48  +**    new cookie value to be included in the HTTP header for the current
           49  +**    web page.  This routine is a destructor for this module and should
           50  +**    be called once.
           51  +**
           52  +** char *cookie_value(zPName, zDefault);
           53  +**
           54  +**    Look up the value of a cookie parameter zPName.  Return zDefault if
           55  +**    there is no display preferences cookie or if zPName does not exist.
           56  +*/
           57  +#include "cookies.h"
           58  +#include <assert.h>
           59  +#include <string.h>
           60  +
           61  +#if INTERFACE
           62  +/* the standard name of the display settings cookie for fossil */
           63  +# define DISPLAY_SETTINGS_COOKIE    "fossil_display_settings"
           64  +#endif
           65  +
           66  +
           67  +/*
           68  +** State information private to this module
           69  +*/
           70  +#define COOKIE_NPARAM  10
           71  +static struct {
           72  +  char *zCookieValue;         /* Value of the user preferences cookie */
           73  +  int bChanged;               /* True if any value has changed */
           74  +  int bIsInit;                /* True after initialization */
           75  +  int nParam;                 /* Number of parameters in the cookie */
           76  +  struct {
           77  +    const char *zPName;         /* Name of a parameter */
           78  +    char *zPValue;              /* Value of that parameter */
           79  +  } aParam[COOKIE_NPARAM];
           80  +} cookies;
           81  +
           82  +/* Initialize this module by parsing the content of the cookie named
           83  +** by DISPLAY_SETTINGS_COOKIE
           84  +*/
           85  +void cookie_parse(void){
           86  +  char *z;
           87  +  if( cookies.bIsInit ) return;
           88  +  z = (char*)P(DISPLAY_SETTINGS_COOKIE);
           89  +  if( z==0 ) z = "";
           90  +  cookies.zCookieValue = z = mprintf("%s", z);
           91  +  cookies.bIsInit = 1;
           92  +  while( cookies.nParam<COOKIE_NPARAM ){
           93  +    while( fossil_isspace(z[0]) ) z++;
           94  +    if( z[0]==0 ) break;
           95  +    cookies.aParam[cookies.nParam].zPName = z;
           96  +    while( *z && *z!='=' && *z!=',' ){ z++; }
           97  +    if( *z=='=' ){
           98  +      *z = 0;
           99  +      z++;
          100  +      cookies.aParam[cookies.nParam].zPValue = z;
          101  +      while( *z && *z!=',' ){ z++; }
          102  +      if( *z ){
          103  +        *z = 0;
          104  +        z++;
          105  +      }
          106  +      dehttpize(cookies.aParam[cookies.nParam].zPValue);
          107  +    }else{
          108  +      if( *z ){ *z++ = 0; }
          109  +      cookies.aParam[cookies.nParam].zPValue = "";
          110  +    }
          111  +    cookies.nParam++;
          112  +  }
          113  +}
          114  +
          115  +#define COOKIE_READ  1
          116  +#define COOKIE_WRITE 2
          117  +static void cookie_readwrite(
          118  +  const char *zQP,        /* Name of the query parameter */
          119  +  const char *zPName,     /* Name of the cooking setting */
          120  +  const char *zDflt,      /* Default value for the query parameter */
          121  +  int flags               /* READ or WRITE or both */
          122  +){
          123  +  const char *zQVal = P(zQP);
          124  +  int i;
          125  +  cookie_parse();
          126  +  for(i=0; i<cookies.nParam && strcmp(zPName,cookies.aParam[i].zPName); i++){}
          127  +  if( zQVal==0 && (flags & COOKIE_READ)!=0 && i<cookies.nParam ){
          128  +    cgi_set_parameter_nocopy(zQP, cookies.aParam[i].zPValue, 1);
          129  +    return;
          130  +  }
          131  +  if( zQVal==0 ) zQVal = zDflt;
          132  +  if( (flags & COOKIE_WRITE)!=0
          133  +   && i<COOKIE_NPARAM
          134  +   && (i==cookies.nParam || strcmp(zQVal, cookies.aParam[i].zPValue))
          135  +  ){
          136  +    if( i==cookies.nParam ){
          137  +      cookies.aParam[i].zPName = zPName;
          138  +      cookies.nParam++;
          139  +    }
          140  +    cookies.aParam[i].zPValue = (char*)zQVal;
          141  +    cookies.bChanged = 1;
          142  +  }
          143  +}
          144  +
          145  +/* If query parameter zQP is missing, initialize it using the zPName
          146  +** value from the user preferences cookie
          147  +*/
          148  +void cookie_read_parameter(const char *zQP, const char *zPName){
          149  +  cookie_readwrite(zQP, zPName, 0, COOKIE_READ);
          150  +}
          151  +
          152  +/* Update the zPName value of the user preference cookie to match
          153  +** the value of query parameter zQP.
          154  +*/
          155  +void cookie_write_parameter(
          156  +  const char *zQP,
          157  +  const char *zPName,
          158  +  const char *zDflt
          159  +){
          160  +  cookie_readwrite(zQP, zPName, zDflt, COOKIE_WRITE);
          161  +}
          162  +
          163  +/* Use the zPName user preference value as a default for zQP and record
          164  +** any changes to the zQP value back into the cookie.
          165  +*/
          166  +void cookie_link_parameter(
          167  +  const char *zQP,       /* The query parameter */
          168  +  const char *zPName,    /* The name of the cookie value */
          169  +  const char *zDflt      /* Default value for the parameter */
          170  +){
          171  +  cookie_readwrite(zQP, zPName, zDflt, COOKIE_READ|COOKIE_WRITE);
          172  +}
          173  +
          174  +/* Update the user preferences cookie, if necessary, and shut down this
          175  +** module
          176  +*/
          177  +void cookie_render(void){
          178  +  if( cookies.bChanged ){
          179  +    Blob new;
          180  +    int i;
          181  +    blob_init(&new, 0, 0);
          182  +    for(i=0;i<cookies.nParam;i++){
          183  +      if( i>0 ) blob_append(&new, ",", 1);
          184  +      blob_appendf(&new, "%s=%T",
          185  +          cookies.aParam[i].zPName, cookies.aParam[i].zPValue);
          186  +    }
          187  +    cgi_set_cookie(DISPLAY_SETTINGS_COOKIE, blob_str(&new), 0, 31536000);
          188  +  }
          189  +  cookies.bIsInit = 0;
          190  +}
          191  +
          192  +/* Return the value of a preference cookie.
          193  +*/
          194  +const char *cookie_value(const char *zPName, const char *zDefault){
          195  +  int i;
          196  +  assert( zPName!=0 );
          197  +  cookie_parse();
          198  +  for(i=0; i<cookies.nParam && strcmp(zPName,cookies.aParam[i].zPName); i++){}
          199  +  return i<cookies.nParam ? cookies.aParam[i].zPValue : zDefault;
          200  +}
          201  +
          202  +/*
          203  +** WEBPAGE:  cookies
          204  +**
          205  +** Show the current display settings contained in the
          206  +** "fossil_display_settings" cookie.
          207  +*/
          208  +void cookie_page(void){
          209  +  int i;
          210  +  if( PB("clear") ){
          211  +    cgi_set_cookie(DISPLAY_SETTINGS_COOKIE, "", 0, 1);
          212  +    cgi_replace_parameter(DISPLAY_SETTINGS_COOKIE, "");
          213  +  }
          214  +  cookie_parse();
          215  +  style_header("User Preference Cookie Values");
          216  +  if( cookies.nParam ){
          217  +    style_submenu_element("Clear", "%R/cookies?clear");
          218  +  }
          219  +  @ <p>The following are user preference settings held in the
          220  +  @ "fossil_display_settings" cookie.
          221  +  @ <ul>
          222  +  @ <li>Raw cookie value: "%h(PD("fossil_display_settings",""))"
          223  +  for(i=0; i<cookies.nParam; i++){
          224  +    @ <li>%h(cookies.aParam[i].zPName): "%h(cookies.aParam[i].zPValue)"
          225  +  }
          226  +  @ </ul>
          227  +  style_footer();
          228  +}

Changes to src/cson_amalgamation.c.

    17     17   #   ifdef _MSC_VER
    18     18   #	    ifdef JSON_PARSER_DLL_EXPORTS
    19     19   #		    define JSON_PARSER_DLL_API __declspec(dllexport)
    20     20   #	    else
    21     21   #		    define JSON_PARSER_DLL_API __declspec(dllimport)
    22     22   #       endif
    23     23   #   else
    24         -#	    define JSON_PARSER_DLL_API
           24  +#	    define JSON_PARSER_DLL_API 
    25     25   #   endif
    26     26   #else
    27         -#	define JSON_PARSER_DLL_API
           27  +#	define JSON_PARSER_DLL_API 
    28     28   #endif
    29     29   
    30     30   /* Determine the integer type use to parse non-floating point numbers */
    31     31   #ifdef _WIN32
    32     32   typedef __int64 JSON_int_t;
    33     33   #define JSON_PARSER_INTEGER_SSCANF_TOKEN "%I64d"
    34     34   #define JSON_PARSER_INTEGER_SPRINTF_TOKEN "%I64d"
    35     35   #elif (__STDC_VERSION__ >= 199901L) || (HAVE_LONG_LONG == 1)
    36     36   typedef long long JSON_int_t;
    37     37   #define JSON_PARSER_INTEGER_SSCANF_TOKEN "%lld"
    38     38   #define JSON_PARSER_INTEGER_SPRINTF_TOKEN "%lld"
    39         -#else
           39  +#else 
    40     40   typedef long JSON_int_t;
    41     41   #define JSON_PARSER_INTEGER_SSCANF_TOKEN "%ld"
    42     42   #define JSON_PARSER_INTEGER_SPRINTF_TOKEN "%ld"
    43     43   #endif
    44     44   
    45     45   
    46     46   #ifdef __cplusplus
    47     47   extern "C" {
    48         -#endif
           48  +#endif 
    49     49   
    50         -typedef enum
           50  +typedef enum 
    51     51   {
    52     52       JSON_E_NONE = 0,
    53     53       JSON_E_INVALID_CHAR,
    54     54       JSON_E_INVALID_KEYWORD,
    55     55       JSON_E_INVALID_ESCAPE_SEQUENCE,
    56     56       JSON_E_INVALID_UNICODE_SEQUENCE,
    57     57       JSON_E_INVALID_NUMBER,
................................................................................
    58     58       JSON_E_NESTING_DEPTH_REACHED,
    59     59       JSON_E_UNBALANCED_COLLECTION,
    60     60       JSON_E_EXPECTED_KEY,
    61     61       JSON_E_EXPECTED_COLON,
    62     62       JSON_E_OUT_OF_MEMORY
    63     63   } JSON_error;
    64     64   
    65         -typedef enum
           65  +typedef enum 
    66     66   {
    67     67       JSON_T_NONE = 0,
    68     68       JSON_T_ARRAY_BEGIN,
    69     69       JSON_T_ARRAY_END,
    70     70       JSON_T_OBJECT_BEGIN,
    71     71       JSON_T_OBJECT_END,
    72     72       JSON_T_INTEGER,
................................................................................
    78     78       JSON_T_KEY,
    79     79       JSON_T_MAX
    80     80   } JSON_type;
    81     81   
    82     82   typedef struct JSON_value_struct {
    83     83       union {
    84     84           JSON_int_t integer_value;
    85         -
           85  +        
    86     86           double float_value;
    87         -
           87  +        
    88     88           struct {
    89     89               const char* value;
    90     90               size_t length;
    91     91           } str;
    92     92       } vu;
    93     93   } JSON_value;
    94     94   
    95     95   typedef struct JSON_parser_struct* JSON_parser;
    96     96   
    97         -/*! \brief JSON parser callback
           97  +/*! \brief JSON parser callback 
    98     98   
    99     99       \param ctx The pointer passed to new_JSON_parser.
   100         -    \param type An element of JSON_type but not JSON_T_NONE.
          100  +    \param type An element of JSON_type but not JSON_T_NONE.    
   101    101       \param value A representation of the parsed value. This parameter is NULL for
   102    102           JSON_T_ARRAY_BEGIN, JSON_T_ARRAY_END, JSON_T_OBJECT_BEGIN, JSON_T_OBJECT_END,
   103    103           JSON_T_NULL, JSON_T_TRUE, and JSON_T_FALSE. String values are always returned
   104    104           as zero-terminated C strings.
   105    105   
   106    106       \return Non-zero if parsing should continue, else zero.
   107         -*/
          107  +*/    
   108    108   typedef int (*JSON_parser_callback)(void* ctx, int type, const JSON_value* value);
   109    109   
   110    110   
   111    111   /**
   112    112      A typedef for allocator functions semantically compatible with malloc().
   113    113   */
   114    114   typedef void* (*JSON_malloc_t)(size_t n);
   115    115   /**
   116    116      A typedef for deallocator functions semantically compatible with free().
   117    117   */
   118    118   typedef void (*JSON_free_t)(void* mem);
   119    119   
   120         -/*! \brief The structure used to configure a JSON parser object
          120  +/*! \brief The structure used to configure a JSON parser object 
   121    121   */
   122    122   typedef struct {
   123    123       /** Pointer to a callback, called when the parser has something to tell
   124    124           the user. This parameter may be NULL. In this case the input is
   125    125           merely checked for validity.
   126    126       */
   127    127       JSON_parser_callback    callback;
................................................................................
   174    174       - no comments
   175    175       - Uses realloc() for memory de/allocation.
   176    176   
   177    177       \param config. Used to configure the parser.
   178    178   */
   179    179   JSON_PARSER_DLL_API void init_JSON_config(JSON_config * config);
   180    180   
   181         -/*! \brief Create a JSON parser object
          181  +/*! \brief Create a JSON parser object 
   182    182   
   183    183       \param config. Used to configure the parser. Set to NULL to use
   184    184           the default configuration. See init_JSON_config.  Its contents are
   185    185           copied by this function, so it need not outlive the returned
   186    186           object.
   187         -
          187  +    
   188    188       \return The parser object, which is owned by the caller and must eventually
   189    189       be freed by calling delete_JSON_parser().
   190    190   */
   191    191   JSON_PARSER_DLL_API JSON_parser new_JSON_parser(JSON_config const* config);
   192    192   
   193    193   /*! \brief Destroy a previously created JSON parser object. */
   194    194   JSON_PARSER_DLL_API void delete_JSON_parser(JSON_parser jc);
................................................................................
   198    198       \return Non-zero, if all characters passed to this function are part of are valid JSON.
   199    199   */
   200    200   JSON_PARSER_DLL_API int JSON_parser_char(JSON_parser jc, int next_char);
   201    201   
   202    202   /*! \brief Finalize parsing.
   203    203   
   204    204       Call this method once after all input characters have been consumed.
   205         -
          205  +    
   206    206       \return Non-zero, if all parsed characters are valid JSON, zero otherwise.
   207    207   */
   208    208   JSON_PARSER_DLL_API int JSON_parser_done(JSON_parser jc);
   209    209   
   210         -/*! \brief Determine if a given string is valid JSON white space
          210  +/*! \brief Determine if a given string is valid JSON white space 
   211    211   
   212    212       \return Non-zero if the string is valid, zero otherwise.
   213    213   */
   214    214   JSON_PARSER_DLL_API int JSON_parser_is_legal_white_space_string(const char* s);
   215    215   
   216    216   /*! \brief Gets the last error that occurred during the use of JSON_parser.
   217    217   
................................................................................
   224    224       \return True (non-zero) on success, 0 on error (e.g. !jc).
   225    225   */
   226    226   JSON_PARSER_DLL_API int JSON_parser_reset(JSON_parser jc);
   227    227   
   228    228   
   229    229   #ifdef __cplusplus
   230    230   }
   231         -#endif
   232         -
          231  +#endif 
          232  +    
   233    233   
   234    234   #endif /* JSON_PARSER_H */
   235    235   /* end file parser/JSON_parser.h */
   236    236   /* begin file parser/JSON_parser.c */
   237    237   /*
   238    238   Copyright (c) 2007-2013 Jean Gressmann (jean@0x42.de)
   239    239   
................................................................................
  1429   1429   #endif
  1430   1430   
  1431   1431   #if defined(__cplusplus)
  1432   1432   extern "C" {
  1433   1433   #endif
  1434   1434   
  1435   1435   
  1436         -
         1436  +    
  1437   1437   /**
  1438   1438      This type holds the "vtbl" for type-specific operations when
  1439   1439      working with cson_value objects.
  1440   1440   
  1441   1441      All cson_values of a given logical type share a pointer to a single
  1442   1442      library-internal instance of this class.
  1443   1443   */
................................................................................
  1581   1581      Assumes V is a (cson_value*) ans V->value is a (T*). Returns
  1582   1582      V->value cast to a (T*).
  1583   1583   */
  1584   1584   #define CSON_CAST(T,V) ((T*)((V)->value))
  1585   1585   /**
  1586   1586      Assumes V is a pointer to memory which is allocated as part of a
  1587   1587      cson_value instance (the bytes immediately after that part).
  1588         -   Returns a pointer a cson_value by subtracting sizeof(cson_value)
         1588  +   Returns a pointer a a cson_value by subtracting sizeof(cson_value)
  1589   1589      from that address and casting it to a (cson_value*)
  1590   1590   */
  1591   1591   #define CSON_VCAST(V) ((cson_value *)(((unsigned char *)(V))-sizeof(cson_value)))
  1592   1592   
  1593   1593   /**
  1594   1594      CSON_INT(V) assumes that V is a (cson_value*) of type
  1595   1595      CSON_TYPE_INTEGER. This macro returns a (cson_int_t*) representing
................................................................................
  1605   1605   #define CSON_DBL(V) CSON_CAST(cson_double_t,(V))
  1606   1606   #define CSON_STR(V) CSON_CAST(cson_string,(V))
  1607   1607   #define CSON_OBJ(V) CSON_CAST(cson_object,(V))
  1608   1608   #define CSON_ARRAY(V) CSON_CAST(cson_array,(V))
  1609   1609   
  1610   1610   /**
  1611   1611    Holds special shared "constant" (though they are non-const)
  1612         - values.
         1612  + values. 
  1613   1613   */
  1614   1614   static struct CSON_EMPTY_HOLDER_
  1615   1615   {
  1616   1616       char trueValue;
  1617   1617       cson_string stringValue;
  1618   1618   } CSON_EMPTY_HOLDER = {
  1619   1619       1/*trueValue*/,
  1620   1620       cson_string_empty_m
  1621   1621   };
  1622   1622   
  1623   1623   /**
  1624   1624       Indexes into the CSON_SPECIAL_VALUES array.
  1625         -
         1625  +    
  1626   1626       If this enum changes in any way,
  1627   1627       makes damned sure that CSON_SPECIAL_VALUES is updated
  1628   1628       to match!!!
  1629   1629   */
  1630   1630   enum CSON_INTERNAL_VALUES {
  1631         -
         1631  +    
  1632   1632       CSON_VAL_UNDEF = 0,
  1633   1633       CSON_VAL_NULL = 1,
  1634   1634       CSON_VAL_TRUE = 2,
  1635   1635       CSON_VAL_FALSE = 3,
  1636   1636       CSON_VAL_INT_0 = 4,
  1637   1637       CSON_VAL_DBL_0 = 5,
  1638   1638       CSON_VAL_STR_EMPTY = 6,
................................................................................
  1640   1640   };
  1641   1641   
  1642   1642   /**
  1643   1643     Some "special" shared cson_value instances.
  1644   1644   
  1645   1645     These values MUST be initialized in the order specified
  1646   1646     by the CSON_INTERNAL_VALUES enum.
  1647         -
         1647  +   
  1648   1648     Note that they are not const because they are used as
  1649   1649     shared-allocation objects in non-const contexts. However, the
  1650   1650     public API provides no way to modifying them, and clients who
  1651   1651     modify values directly are subject to The Wrath of Undefined
  1652   1652     Behaviour.
  1653   1653   */
  1654   1654   static cson_value CSON_SPECIAL_VALUES[] = {
................................................................................
  1663   1663   };
  1664   1664   
  1665   1665   
  1666   1666   /**
  1667   1667       Returns non-0 (true) if m is one of our special
  1668   1668       "built-in" values, e.g. from CSON_SPECIAL_VALUES and some
  1669   1669       "empty" values.
  1670         -
         1670  +     
  1671   1671       If this returns true, m MUST NOT be free()d!
  1672   1672    */
  1673   1673   static char cson_value_is_builtin( void const * m )
  1674   1674   {
  1675   1675       if((m >= (void const *)&CSON_EMPTY_HOLDER)
  1676   1676           && ( m < (void const *)(&CSON_EMPTY_HOLDER+1)))
  1677   1677           return 1;
................................................................................
  2181   2181                                   int (*visitor)(cson_kvp * obj, void * visitorState ),
  2182   2182                                   void * visitorState );
  2183   2183   static int cson_value_list_visit( cson_value_list * self,
  2184   2184                                     int (*visitor)(cson_value * obj, void * visitorState ),
  2185   2185                                     void * visitorState );
  2186   2186   #endif
  2187   2187   #endif
  2188         -
         2188  +    
  2189   2189   #if 0
  2190   2190   #  define LIST_T cson_value_list
  2191   2191   #  define VALUE_T cson_value *
  2192   2192   #  define VALUE_T_IS_PTR 1
  2193   2193   #  define LIST_T cson_kvp_list
  2194   2194   #  define VALUE_T cson_kvp *
  2195   2195   #  define VALUE_T_IS_PTR 1
................................................................................
  2360   2360   cson_value * cson_value_new_object()
  2361   2361   {
  2362   2362       return cson_value_object_alloc();
  2363   2363   }
  2364   2364   
  2365   2365   cson_object * cson_new_object()
  2366   2366   {
  2367         -
         2367  +    
  2368   2368       return cson_value_get_object( cson_value_new_object() );
  2369   2369   }
  2370   2370   
  2371   2371   cson_value * cson_value_new_array()
  2372   2372   {
  2373   2373       return cson_value_array_alloc();
  2374   2374   }
................................................................................
  2606   2606       if( ! val || !val->api ) return cson_rc.ArgError;
  2607   2607       else
  2608   2608       {
  2609   2609           cson_int_t i = 0;
  2610   2610           int rc = 0;
  2611   2611           switch(val->api->typeID)
  2612   2612           {
  2613         -            case CSON_TYPE_UNDEF:
         2613  +            case CSON_TYPE_UNDEF: 
  2614   2614               case CSON_TYPE_NULL:
  2615   2615                 i = 0;
  2616   2616                 break;
  2617   2617               case CSON_TYPE_BOOL: {
  2618   2618                 char b = 0;
  2619   2619                 cson_value_fetch_bool( val, &b );
  2620   2620                 i = b;
................................................................................
  2659   2659       if( ! val || !val->api ) return cson_rc.ArgError;
  2660   2660       else
  2661   2661       {
  2662   2662           cson_double_t d = 0.0;
  2663   2663           int rc = 0;
  2664   2664           switch(val->api->typeID)
  2665   2665           {
  2666         -          case CSON_TYPE_UNDEF:
         2666  +          case CSON_TYPE_UNDEF: 
  2667   2667             case CSON_TYPE_NULL:
  2668   2668                 d = 0;
  2669   2669                 break;
  2670   2670             case CSON_TYPE_BOOL: {
  2671   2671                 char b = 0;
  2672   2672                 cson_value_fetch_bool( val, &b );
  2673   2673                 d = b ? 1.0 : 0.0;
................................................................................
  2789   2789   }
  2790   2790   
  2791   2791   #if 0
  2792   2792   /**
  2793   2793      Removes and returns the last value from the given array,
  2794   2794      shrinking its size by 1. Returns NULL if ar is NULL,
  2795   2795      ar->list.count is 0, or the element at that index is NULL.
  2796         -
         2796  +   
  2797   2797   
  2798   2798      If removeRef is true then cson_value_free() is called to remove
  2799   2799      ar's reference count for the value. In that case NULL is returned,
  2800   2800      even if the object still has live references. If removeRef is false
  2801   2801      then the caller takes over ownership of that reference count point.
  2802   2802   
  2803   2803      If removeRef is false then the caller takes over ownership
................................................................................
  2856   2856       {
  2857   2857           cson_value * c = cson_value_new(CSON_TYPE_INTEGER,0);
  2858   2858   #if !defined(NDEBUG) && CSON_VOID_PTR_IS_BIG
  2859   2859           assert( sizeof(cson_int_t) <= sizeof(void *) );
  2860   2860   #endif
  2861   2861           if( c )
  2862   2862           {
  2863         -            memcpy(CSON_INT(c), &v, sizeof(v));
         2863  +            memcpy( CSON_INT(c), &v, sizeof(v) );
  2864   2864           }
  2865   2865           return c;
  2866   2866       }
  2867   2867   }
  2868   2868   
  2869   2869   cson_value * cson_new_double( cson_double_t v )
  2870   2870   {
................................................................................
  2875   2875   {
  2876   2876       if( 0.0 == v ) return &CSON_SPECIAL_VALUES[CSON_VAL_DBL_0];
  2877   2877       else
  2878   2878       {
  2879   2879           cson_value * c = cson_value_new(CSON_TYPE_DOUBLE,0);
  2880   2880           if( c )
  2881   2881           {
  2882         -            memcpy(CSON_DBL(c), &v, sizeof(v));
         2882  +            memcpy( CSON_DBL(c), &v, sizeof(v) );
  2883   2883           }
  2884   2884           return c;
  2885   2885       }
  2886   2886   }
  2887   2887   
  2888   2888   cson_string * cson_new_string(char const * str, unsigned int len)
  2889   2889   {
................................................................................
  3066   3066       if( obj->kvp.count )
  3067   3067       {
  3068   3068           qsort( obj->kvp.list, obj->kvp.count, sizeof(cson_kvp*),
  3069   3069                  cson_kvp_cmp );
  3070   3070       }
  3071   3071   
  3072   3072   }
  3073         -#endif
         3073  +#endif    
  3074   3074   
  3075   3075   int cson_object_unset( cson_object * obj, char const * key )
  3076   3076   {
  3077   3077       if( ! obj || !key || !*key ) return cson_rc.ArgError;
  3078   3078       else
  3079   3079       {
  3080   3080           unsigned int ndx = 0;
................................................................................
  3236   3236   
  3237   3237      If p->node is-a Object then value is inserted into the object
  3238   3238      using p->key. In any other case cson_rc.InternalError is returned.
  3239   3239   
  3240   3240      Returns cson_rc.AllocError if an allocation fails.
  3241   3241   
  3242   3242      Returns 0 on success. On error, parsing must be ceased immediately.
  3243         -
         3243  +   
  3244   3244      Ownership of val is ALWAYS TRANSFERED to this function. If this
  3245   3245      function fails, val will be cleaned up and destroyed. (This
  3246   3246      simplifies error handling in the core parser.)
  3247   3247   */
  3248   3248   static int cson_parser_set_key( cson_parser * p, cson_value * val )
  3249   3249   {
  3250   3250       assert( p && val );
................................................................................
  3483   3483                 break;
  3484   3484             }
  3485   3485             ++p->totalKeyCount;
  3486   3486             break;
  3487   3487         }
  3488   3488         case JSON_T_STRING: {
  3489   3489             cson_value * v = cson_value_new_string( value->vu.str.value, value->vu.str.length );
  3490         -          rc = ( NULL == v )
         3490  +          rc = ( NULL == v ) 
  3491   3491               ? cson_rc.AllocError
  3492   3492               : cson_parser_push_value( p, v );
  3493   3493             break;
  3494   3494         }
  3495   3495         default:
  3496   3496             assert(0);
  3497   3497             rc = cson_rc.InternalError;
................................................................................
  3530   3530      Cleans up all contents of p but does not free p.
  3531   3531   
  3532   3532      To properly take over ownership of the parser's root node on a
  3533   3533      successful parse:
  3534   3534   
  3535   3535      - Copy p->root's pointer and set p->root to NULL.
  3536   3536      - Eventually free up p->root with cson_value_free().
  3537         -
         3537  +   
  3538   3538      If you do not set p->root to NULL, p->root will be freed along with
  3539   3539      any other items inserted into it (or under it) during the parsing
  3540   3540      process.
  3541   3541   */
  3542   3542   static int cson_parser_clean( cson_parser * p )
  3543   3543   {
  3544   3544       if( ! p ) return cson_rc.ArgError;
................................................................................
  3569   3569       unsigned char ch[2] = {0,0};
  3570   3570       cson_parse_opt const opt = opt_ ? *opt_ : cson_parse_opt_empty;
  3571   3571       int rc = 0;
  3572   3572       unsigned int len = 1;
  3573   3573       cson_parse_info info = info_ ? *info_ : cson_parse_info_empty;
  3574   3574       cson_parser p = cson_parser_empty;
  3575   3575       if( ! tgt || ! src ) return cson_rc.ArgError;
  3576         -
         3576  +    
  3577   3577       {
  3578   3578           JSON_config jopt = {0};
  3579   3579           init_JSON_config( &jopt );
  3580   3580           jopt.allow_comments = opt.allowComments;
  3581   3581           jopt.depth = opt.maxDepth;
  3582   3582           jopt.callback_ctx = &p;
  3583   3583           jopt.handle_floats_manually = 0;
................................................................................
  3778   3778       {
  3779   3779           unsigned char const * pos = (unsigned char const *)str;
  3780   3780           unsigned char const * end = (unsigned char const *)(str ? (str + len) : NULL);
  3781   3781           unsigned char const * next = NULL;
  3782   3782           int ch;
  3783   3783           unsigned char clen = 0;
  3784   3784           char escChar[3] = {'\\',0,0};
  3785         -        enum { UBLen = 13 };
         3785  +        enum { UBLen = 20 };
  3786   3786           char ubuf[UBLen];
  3787   3787           int rc = 0;
  3788   3788           rc = f(state, "\"", 1 );
  3789   3789           for( ; (pos < end) && (0 == rc); pos += clen )
  3790   3790           {
  3791   3791               ch = cson_utf8Read(pos, end, &next);
  3792   3792               if( 0 == ch ) break;
................................................................................
  4638   4638   #else
  4639   4639       rc = cson_value_clone(v);
  4640   4640   #endif
  4641   4641   #undef TRY_SHARING
  4642   4642       cson_value_add_reference(rc);
  4643   4643       return rc;
  4644   4644   }
  4645         -
         4645  +    
  4646   4646   static cson_value * cson_value_clone_array( cson_value const * orig )
  4647   4647   {
  4648   4648       unsigned int i = 0;
  4649   4649       cson_array const * asrc = cson_value_get_array( orig );
  4650   4650       unsigned int alen = cson_array_length_get( asrc );
  4651   4651       cson_value * destV = NULL;
  4652   4652       cson_array * destA = NULL;
................................................................................
  4678   4678                   return NULL;
  4679   4679               }
  4680   4680               cson_value_free(cl)/*remove our artificial reference */;
  4681   4681           }
  4682   4682       }
  4683   4683       return destV;
  4684   4684   }
  4685         -
         4685  +    
  4686   4686   static cson_value * cson_value_clone_object( cson_value const * orig )
  4687   4687   {
  4688   4688       cson_object const * src = cson_value_get_object( orig );
  4689   4689       cson_value * destV = NULL;
  4690   4690       cson_object * dest = NULL;
  4691   4691       cson_kvp const * kvp = NULL;
  4692   4692       cson_object_iterator iter = cson_object_iterator_empty;
................................................................................
  4832   4832                 v = cson_strdup( "null", 4 );
  4833   4833                 break;
  4834   4834             }
  4835   4835             case CSON_TYPE_STRING: {
  4836   4836                 cson_string const * jstr = cson_value_get_string(orig);
  4837   4837                 unsigned const int slen = cson_string_length_bytes( jstr );
  4838   4838                 assert( NULL != jstr );
  4839         -              v = cson_strdup( cson_string_cstr( jstr ), slen );
         4839  +              v = cson_strdup( cson_string_cstr( jstr ), slen ); 
  4840   4840                 break;
  4841   4841             }
  4842   4842             case CSON_TYPE_INTEGER: {
  4843   4843                 char buf[BufSize] = {0};
  4844   4844                 if( 0 < sprintf( v, "%"CSON_INT_T_PFMT, cson_value_get_integer(orig)) )
  4845   4845                 {
  4846   4846                     v = cson_strdup( buf, strlen(buf) );
................................................................................
  4885   4885                 v = cson_strdup( "null", 4 );
  4886   4886                 break;
  4887   4887             }
  4888   4888             case CSON_TYPE_STRING: {
  4889   4889                 cson_string const * jstr = cson_value_get_string(orig);
  4890   4890                 unsigned const int slen = cson_string_length_bytes( jstr );
  4891   4891                 assert( NULL != jstr );
  4892         -              v = cson_strdup( cson_string_cstr( jstr ), slen );
         4892  +              v = cson_strdup( cson_string_cstr( jstr ), slen ); 
  4893   4893                 break;
  4894   4894             }
  4895   4895             case CSON_TYPE_INTEGER: {
  4896   4896                 char buf[BufSize] = {0};
  4897   4897                 if( 0 < sprintf( v, "%"CSON_INT_T_PFMT, cson_value_get_integer(orig)) )
  4898   4898                 {
  4899   4899                     v = cson_strdup( buf, strlen(buf) );
................................................................................
  5349   5349       char const * colName = NULL;
  5350   5350       int i = 0;
  5351   5351       int rc = 0;
  5352   5352       int colCount = 0;
  5353   5353       assert(st);
  5354   5354       colCount = sqlite3_column_count(st);
  5355   5355       if( colCount <= 0 ) return NULL;
  5356         -
         5356  +    
  5357   5357       aryV = cson_value_new_array();
  5358   5358       if( ! aryV ) return NULL;
  5359   5359       ary = cson_value_get_array(aryV);
  5360   5360       assert(ary);
  5361   5361       for( i = 0; (0 ==rc) && (i < colCount); ++i )
  5362   5362       {
  5363   5363           colName = sqlite3_column_name( st, i );
................................................................................
  5489   5489       error:
  5490   5490       cson_value_free(aryV);
  5491   5491       aryV = NULL;
  5492   5492       end:
  5493   5493       return aryV;
  5494   5494   }
  5495   5495   
  5496         -
         5496  +    
  5497   5497   /**
  5498   5498       Internal impl of cson_sqlite3_stmt_to_json() when the 'fat'
  5499   5499       parameter is non-0.
  5500   5500   */
  5501   5501   static int cson_sqlite3_stmt_to_json_fat( sqlite3_stmt * st, cson_value ** tgt )
  5502   5502   {
  5503   5503   #define RETURN(RC) { if(rootV) cson_value_free(rootV); return RC; }
................................................................................
  5634   5634       {
  5635   5635           sqlite3_stmt * st = NULL;
  5636   5636           int rc = sqlite3_prepare_v2( db, sql, -1, &st, NULL );
  5637   5637           if( 0 != rc ) return cson_rc.IOError /* FIXME: Better error code? */;
  5638   5638           rc = cson_sqlite3_stmt_to_json( st, tgt, fat );
  5639   5639           sqlite3_finalize( st );
  5640   5640           return rc;
  5641         -    }
         5641  +    }        
  5642   5642   }
  5643   5643   
  5644   5644   int cson_sqlite3_bind_value( sqlite3_stmt * st, int ndx, cson_value const * v )
  5645   5645   {
  5646   5646       int rc = 0;
  5647   5647       char convertErr = 0;
  5648   5648       if(!st) return cson_rc.ArgError;

Changes to src/cson_amalgamation.h.

    57     57   typedef __int64 cson_int_t;
    58     58   #define CSON_INT_T_SFMT "I64d"
    59     59   #define CSON_INT_T_PFMT "I64d"
    60     60   #elif (__STDC_VERSION__ >= 199901L) || (HAVE_LONG_LONG == 1)
    61     61   typedef long long cson_int_t;
    62     62   #define CSON_INT_T_SFMT "lld"
    63     63   #define CSON_INT_T_PFMT "lld"
    64         -#else
           64  +#else 
    65     65   typedef long cson_int_t;
    66     66   #define CSON_INT_T_SFMT "ld"
    67     67   #define CSON_INT_T_PFMT "ld"
    68     68   #endif
    69     69   
    70     70   /** @typedef double_or_long_double cson_double_t
    71     71   
................................................................................
   213    213   
   214    214   /**
   215    215      Convenience typedef.
   216    216   */
   217    217   typedef struct cson_value cson_value;
   218    218   
   219    219   /** @struct cson_value
   220         -
          220  +   
   221    221      The core value type of this API. It is opaque to clients, and
   222    222      only the cson public API should be used for setting or
   223    223      inspecting their values.
   224    224   
   225    225      This class is opaque because stack-based usage can easily cause
   226    226      leaks if one does not intimately understand the underlying
   227    227      internal memory management (which sometimes changes).
................................................................................
   229    229      It is (as of 20110323) legal to insert a given value instance into
   230    230      multiple containers (they will share ownership using reference
   231    231      counting) as long as those insertions do not cause cycles. However,
   232    232      be very aware that such value re-use uses a reference to the
   233    233      original copy, meaning that if its value is changed once, it is
   234    234      changed everywhere. Also beware that multi-threaded write
   235    235      operations on such references leads to undefined behaviour.
   236         -
          236  +   
   237    237      PLEASE read the ACHTUNGEN below...
   238    238   
   239    239      ACHTUNG #1:
   240    240   
   241    241      cson_values MUST NOT form cycles (e.g. via object or array
   242    242      entries).
   243    243   
   244    244      Not abiding th Holy Law Of No Cycles will lead to double-frees and
   245    245      the like (i.e. undefined behaviour, likely crashes due to infinite
   246    246      recursion or stepping on invalid (freed) pointers).
   247    247   
   248    248      ACHTUNG #2:
   249         -
          249  +   
   250    250      ALL cson_values returned as non-const cson_value pointers from any
   251    251      public functions in the cson API are to be treated as if they are
   252    252      heap-allocated, and MUST be freed by client by doing ONE of:
   253         -
          253  +   
   254    254      - Passing it to cson_value_free().
   255         -
          255  +   
   256    256      - Adding it to an Object or Array, in which case the object/array
   257    257      takes over ownership. As of 20110323, a value may be inserted into
   258    258      a single container multiple times, or into multiple containers,
   259    259      in which case they all share ownership (via reference counting)
   260    260      of the original value (meaning any changes to it are visible in
   261    261      all references to it).
   262         -
          262  +   
   263    263      Each call to cson_value_new_xxx() MUST eventually be followed up
   264    264      by one of those options.
   265         -
          265  +   
   266    266      Some cson_value_new_XXX() implementations do not actually allocate
   267    267      memory, but this is an internal implementation detail. Client code
   268    268      MUST NOT rely on this behaviour and MUST treat each object
   269    269      returned by such a function as if it was a freshly-allocated copy
   270    270      (even if their pointer addresses are the same).
   271         -
          271  +   
   272    272      ACHTUNG #3:
   273    273   
   274    274      Note that ACHTUNG #2 tells us that we must always free (or transfer
   275    275      ownership of) all pointers returned bycson_value_new_xxx(), but
   276    276      that two calls to (e.g.) cson_value_new_bool(1) will (or might)
   277    277      return the same address. The client must not rely on the
   278    278      "non-allocation" policy of such special cases, and must pass each
................................................................................
   313    313      @code
   314    314      int rc = cson_some_func(...);
   315    315      if( 0 == rc ) {...success...}
   316    316      else if( cson_rc.ArgError == rc ) { ... some argument was wrong ... }
   317    317      else if( cson_rc.AllocError == rc ) { ... allocation error ... }
   318    318      ...
   319    319      @endcode
   320         -
          320  +   
   321    321      The entries named Parse_XXX are generally only returned by
   322    322      cson_parse() and friends.
   323    323   */
   324    324   
   325    325   /** @struct cson_rc_
   326    326      See \ref cson_rc for details.
   327    327   */
................................................................................
   470    470        */
   471    471       unsigned int col;
   472    472   
   473    473       /**
   474    474          Length, in bytes.
   475    475       */
   476    476       unsigned int length;
   477         -
          477  +    
   478    478       /**
   479    479          Error code of the parse run (0 for no error).
   480    480       */
   481    481       int errorCode;
   482    482   
   483    483       /**
   484    484          The total number of object keys successfully processed by the
................................................................................
   521    521   struct cson_output_opt
   522    522   {
   523    523       /**
   524    524          Specifies how to indent (or not) output. The values
   525    525          are:
   526    526   
   527    527          (0) == no extra indentation.
   528         -
          528  +       
   529    529          (1) == 1 TAB character for each level.
   530    530   
   531    531          (>1) == that number of SPACES for each level.
   532    532       */
   533    533       unsigned char indentation;
   534    534   
   535    535       /**
   536    536          Maximum object/array depth to traverse. Traversing deeply can
   537    537          be indicative of cycles in the object/array tree, and this
   538    538          value is used to figure out when to abort the traversal.
   539    539       */
   540    540       unsigned short maxDepth;
   541         -
          541  +    
   542    542       /**
   543    543          If true, a newline will be added to generated output,
   544    544          else not.
   545    545       */
   546    546       char addNewline;
   547    547   
   548    548       /**
................................................................................
   634    634      returns, so the implementation must copy or ignore the data, but not
   635    635      hold a copy of the src pointer.
   636    636   
   637    637      Must return 0 on success, non-0 on error (preferably a value from
   638    638      cson_rc).
   639    639   
   640    640      These functions are called relatively often during the JSON-output
   641         -   process, and should try to be fast.
          641  +   process, and should try to be fast.   
   642    642   */
   643    643   typedef int (*cson_data_dest_f)( void * state, void const * src, unsigned int n );
   644    644   
   645    645   /**
   646    646       Reads JSON-formatted string data (in ASCII, UTF8, or UTF16), using the
   647    647       src function to fetch all input. This function fetches each input character
   648    648       from the source function, which is calls like src(srcState, buffer, bufferSize),
................................................................................
   662    662       which contains any settings the caller wants. If it is NULL then
   663    663       default settings (the values defined in cson_parse_opt_empty) are
   664    664       used.
   665    665   
   666    666       The info argument may be NULL. If it is not NULL then the parser
   667    667       populates it with information which is useful in error
   668    668       reporting. Namely, it contains the line/column of parse errors.
   669         -
          669  +    
   670    670       The srcState argument is ignored by this function but is passed on to src,
   671    671       so any output-destination-specific state can be stored there and accessed
   672    672       via the src callback.
   673         -
          673  +    
   674    674       Non-parse error conditions include:
   675    675   
   676    676       - (!tgt) or !src: cson_rc.ArgError
   677    677       - cson_rc.AllocError can happen at any time during the input phase
   678    678   
   679    679       Here's a complete example of using a custom input source:
   680    680   
................................................................................
   723    723       cson_parse_FILE() or cson_parse_string().
   724    724   
   725    725       TODOs:
   726    726   
   727    727       - Buffer the input in larger chunks. We currently read
   728    728       byte-by-byte, but i'm too tired to write/test the looping code for
   729    729       the buffering.
   730         -
          730  +    
   731    731       @see cson_parse_FILE()
   732    732       @see cson_parse_string()
   733    733   */
   734    734   int cson_parse( cson_value ** tgt, cson_data_source_f src, void * srcState,
   735    735                   cson_parse_opt const * opt, cson_parse_info * info );
   736    736   /**
   737    737      A cson_data_source_f() implementation which requires the state argument
................................................................................
   784    784      cson_rc.RangeError is returned.
   785    785   
   786    786      The destState parameter is ignored by this function and is passed
   787    787      on to the dest function.
   788    788   
   789    789      Returns 0 on success. On error, any amount of output might have been
   790    790      generated before the error was triggered.
   791         -
          791  +   
   792    792      Example:
   793    793   
   794    794      @code
   795    795      int rc = cson_output( myValue, cson_data_dest_FILE, stdout, NULL );
   796    796      // basically equivalent to: cson_output_FILE( myValue, stdout, NULL );
   797    797      // but note that cson_output_FILE() actually uses different defaults
   798    798      // for the output options.
................................................................................
   923    923   typedef struct cson_string cson_string;
   924    924   
   925    925   /**
   926    926      Converts the given value to a boolean, using JavaScript semantics depending
   927    927      on the concrete type of val:
   928    928   
   929    929      undef or null: false
   930         -
          930  +   
   931    931      boolean: same
   932         -
          932  +   
   933    933      integer, double: 0 or 0.0 == false, else true
   934         -
          934  +   
   935    935      object, array: true
   936    936   
   937    937      string: length-0 string is false, else true.
   938    938   
   939    939      Returns 0 on success and assigns *v (if v is not NULL) to either 0 or 1.
   940    940      On error (val is NULL) then v is not modified.
   941    941   */
................................................................................
   943    943   
   944    944   /**
   945    945      Similar to cson_value_fetch_bool(), but fetches an integer value.
   946    946   
   947    947      The conversion, if any, depends on the concrete type of val:
   948    948   
   949    949      NULL, null, undefined: *v is set to 0 and 0 is returned.
   950         -
          950  +   
   951    951      string, object, array: *v is set to 0 and
   952    952      cson_rc.TypeError is returned. The error may normally be safely
   953    953      ignored, but it is provided for those wanted to know whether a direct
   954    954      conversion was possible.
   955    955   
   956    956      integer: *v is set to the int value and 0 is returned.
   957         -
          957  +   
   958    958      double: *v is set to the value truncated to int and 0 is returned.
   959    959   */
   960    960   int cson_value_fetch_integer( cson_value const * val, cson_int_t * v );
   961    961   
   962    962   /**
   963    963      The same conversions and return values as
   964    964      cson_value_fetch_integer(), except that the roles of int/double are
................................................................................
  1082   1082      they are equivalent, or a positive number if lhs is greater-than
  1083   1083      rhs. It has the following rules for equivalence:
  1084   1084   
  1085   1085      - The maximum number of bytes compared is the lesser of rhsLen and
  1086   1086      the length of lhs. If the strings do not match, but compare equal
  1087   1087      up to the just-described comparison length, the shorter string is
  1088   1088      considered to be less-than the longer one.
  1089         -
         1089  +   
  1090   1090      - If lhs and rhs are both NULL, or both have a length of 0 then they will
  1091   1091      compare equal.
  1092   1092   
  1093   1093      - If lhs is null/length-0 but rhs is not then lhs is considered to be less-than
  1094   1094      rhs.
  1095   1095   
  1096   1096      - If rhs is null/length-0 but lhs is not then rhs is considered to be less-than
................................................................................
  1108   1108   
  1109   1109   /**
  1110   1110      Returns the length, in bytes, of str, or 0 if str is NULL. This is
  1111   1111      an O(1) operation.
  1112   1112   
  1113   1113      TODO: add cson_string_length_chars() (is O(N) unless we add another
  1114   1114      member to store the char length).
  1115         -
         1115  +   
  1116   1116      @see cson_string_cstr()
  1117   1117   */
  1118   1118   unsigned int cson_string_length_bytes( cson_string const * str );
  1119   1119   
  1120   1120   /**
  1121   1121       Returns the number of UTF8 characters in str. This value will
  1122   1122       be at most as long as cson_string_length_bytes() for the
................................................................................
  1205   1205      freed before inserting the new item.
  1206   1206   
  1207   1207      ar is expanded, if needed, to be able to hold at least (ndx+1)
  1208   1208      items, and any new entries created by that expansion are empty
  1209   1209      (NULL values).
  1210   1210   
  1211   1211      On success, 0 is returned and ownership of v is transfered to ar.
  1212         -
         1212  +  
  1213   1213      On error ownership of v is NOT modified, and the caller may still
  1214   1214      need to clean it up. For example, the following code will introduce
  1215   1215      a leak if this function fails:
  1216   1216   
  1217   1217      @code
  1218   1218      cson_array_append( myArray, cson_value_new_integer(42) );
  1219   1219      @endcode
................................................................................
  1238   1238      v to ar. On error, ownership of v is not modified. Ownership of ar
  1239   1239      is never changed by this function.
  1240   1240   
  1241   1241      This is functionally equivalent to
  1242   1242      cson_array_set(ar,cson_array_length_get(ar),v), but this
  1243   1243      implementation has slightly different array-preallocation policy
  1244   1244      (it grows more eagerly).
  1245         -
         1245  +   
  1246   1246      Returns 0 on success, non-zero on error. Error cases include:
  1247   1247   
  1248   1248      - ar or v are NULL: cson_rc.ArgError
  1249   1249   
  1250   1250      - Array cannot be expanded to hold enough elements: cson_rc.AllocError.
  1251   1251   
  1252   1252      - Appending would cause a numeric overlow in the array's size:
................................................................................
  1281   1281      Alias for cson_value_new_bool(v).
  1282   1282   */
  1283   1283   cson_value * cson_new_bool(char v);
  1284   1284   
  1285   1285   /**
  1286   1286      Returns the special JSON "null" value. When outputing JSON,
  1287   1287      its string representation is "null" (without the quotes).
  1288         -
         1288  +   
  1289   1289      See cson_value_new_bool() for notes regarding the returned
  1290   1290      value's memory.
  1291   1291   */
  1292   1292   cson_value * cson_value_null();
  1293   1293   
  1294   1294   /**
  1295   1295      Equivalent to cson_value_new_bool(1).
................................................................................
  1321   1321   */
  1322   1322   cson_value * cson_new_double(cson_double_t v);
  1323   1323   
  1324   1324   /**
  1325   1325      Semantically the same as cson_value_new_bool(), but for strings.
  1326   1326      This creates a JSON value which copies the first n bytes of str.
  1327   1327      The string will automatically be NUL-terminated.
  1328         -
         1328  +   
  1329   1329      Note that if str is NULL or n is 0, this function still
  1330   1330      returns non-NULL value representing that empty string.
  1331         -
         1331  +   
  1332   1332      Returns NULL on allocation error.
  1333         -
         1333  +   
  1334   1334      See cson_value_new_bool() for important information about the
  1335   1335      returned memory.
  1336   1336   */
  1337   1337   cson_value * cson_value_new_string( char const * str, unsigned int n );
  1338   1338   
  1339   1339   /**
  1340   1340      Allocates a new "object" value and transfers ownership of it to the
................................................................................
  1352   1352   
  1353   1353   /**
  1354   1354      This works like cson_value_new_object() but returns an Object
  1355   1355      handle directly.
  1356   1356   
  1357   1357      The value handle for the returned object can be fetched with
  1358   1358      cson_object_value(theObject).
  1359         -
         1359  +   
  1360   1360      Ownership is transfered to the caller, who must eventually free it
  1361   1361      by passing the Value handle (NOT the Object handle) to
  1362   1362      cson_value_free() or passing ownership to a parent container.
  1363   1363   
  1364   1364      Returns NULL on error (out of memory).
  1365   1365   */
  1366   1366   cson_object * cson_new_object();
................................................................................
  1417   1417      value if its reference count drops to 0. Reference counts are
  1418   1418      increased by either inserting the value into a container or via
  1419   1419      cson_value_add_reference(). Even if this function does not
  1420   1420      immediately destroy the value, the value must be considered, from
  1421   1421      the perspective of that client code, to have been
  1422   1422      destroyed/invalidated by this call.
  1423   1423   
  1424         -
         1424  +   
  1425   1425      @see cson_value_new_object()
  1426   1426      @see cson_value_new_array()
  1427   1427      @see cson_value_add_reference()
  1428   1428   */
  1429   1429   void cson_value_free(cson_value * v);
  1430   1430   
  1431   1431   /**
................................................................................
  1443   1443      cson_object_unset(obj,key). Note that (v==NULL) is treated
  1444   1444      differently from v having the special null value. In the latter
  1445   1445      case, the key is set to the special null value.
  1446   1446   
  1447   1447      The key may be encoded as ASCII or UTF8. Results are undefined
  1448   1448      with other encodings, and the errors won't show up here, but may
  1449   1449      show up later, e.g. during output.
  1450         -
         1450  +   
  1451   1451      Returns 0 on success, non-0 on error. It has the following error
  1452   1452      cases:
  1453   1453   
  1454   1454      - cson_rc.ArgError: obj or key are NULL or strlen(key) is 0.
  1455   1455   
  1456   1456      - cson_rc.AllocError: an out-of-memory error
  1457   1457   
................................................................................
  1496   1496      increased refcounts unless they are replacing themselves (which is
  1497   1497      a harmless no-op).
  1498   1498   */
  1499   1499   int cson_object_set_s( cson_object * obj, cson_string * key, cson_value * v );
  1500   1500   
  1501   1501   /**
  1502   1502      Removes a property from an object.
  1503         -
         1503  +   
  1504   1504      If obj contains the given key, it is removed and 0 is returned. If
  1505   1505      it is not found, cson_rc.NotFoundError is returned (which can
  1506   1506      normally be ignored by client code).
  1507   1507   
  1508   1508      cson_rc.ArgError is returned if obj or key are NULL or key has
  1509   1509      a length of 0.
  1510   1510   
................................................................................
  1567   1567       and traversing its properties as the path specifies. If a given part of the
  1568   1568       path is not found, then this function fails with cson_rc.NotFoundError.
  1569   1569   
  1570   1570       If it finds the given path, it returns the value by assiging *tgt
  1571   1571       to it.  If tgt is NULL then this function has no side-effects but
  1572   1572       will return 0 if the given path is found within the object, so it can be used
  1573   1573       to test for existence without fetching it.
  1574         -
         1574  +    
  1575   1575       Returns 0 if it finds an entry, cson_rc.NotFoundError if it finds
  1576   1576       no item, and any other non-zero error code on a "real" error. Errors include:
  1577   1577   
  1578   1578      - obj or path are NULL: cson_rc.ArgError
  1579         -
         1579  +    
  1580   1580       - separator is 0, or path is an empty string or contains only
  1581   1581       separator characters: cson_rc.RangeError
  1582   1582   
  1583   1583       - There is an upper limit on how long a single path component may
  1584   1584       be (some "reasonable" internal size), and cson_rc.RangeError is
  1585   1585       returned if that length is violated.
  1586   1586   
  1587         -
         1587  +    
  1588   1588       Limitations:
  1589   1589   
  1590   1590       - It has no way to fetch data from arrays this way. i could
  1591   1591       imagine, e.g., a path of "subobj.subArray.0" for
  1592   1592       subobj.subArray[0], or "0.3.1" for [0][3][1]. But i'm too
  1593   1593       lazy/tired to add this.
  1594   1594   
  1595   1595       Example usage:
  1596         -
         1596  +    
  1597   1597   
  1598   1598       Assume we have a JSON structure which abstractly looks like:
  1599   1599   
  1600   1600       @code
  1601   1601       {"subobj":{"subsubobj":{"myValue":[1,2,3]}}}
  1602   1602       @endcode
  1603   1603   
................................................................................
  1611   1611       Note that because keys in JSON may legally contain a '.', the
  1612   1612       separator must be specified by the caller. e.g. the path
  1613   1613       "subobj/subsubobj/myValue" with separator='/' is equivalent the
  1614   1614       path "subobj.subsubobj.myValue" with separator='.'. The value of 0
  1615   1615       is not legal as a separator character because we cannot
  1616   1616       distinguish that use from the real end-of-string without requiring
  1617   1617       the caller to also pass in the length of the string.
  1618         -
         1618  +   
  1619   1619       Multiple successive separators in the list are collapsed into a
  1620   1620       single separator for parsing purposes. e.g. the path "a...b...c"
  1621   1621       (separator='.') is equivalent to "a.b.c".
  1622   1622   
  1623   1623       @see cson_object_get_sub()
  1624   1624       @see cson_object_get_sub2()
  1625   1625   */
................................................................................
  1698   1698      code.
  1699   1699   
  1700   1700      @see cson_object_iter_init()
  1701   1701      @see cson_object_iter_next()
  1702   1702   */
  1703   1703   struct cson_object_iterator
  1704   1704   {
  1705         -
         1705  +    
  1706   1706       /** @internal
  1707   1707           The underlying object.
  1708   1708       */
  1709   1709       cson_object const * obj;
  1710   1710       /** @internal
  1711   1711           Current position in the property list.
  1712   1712        */
................................................................................
  1769   1769          key = cson_kvp_key(kvp);
  1770   1770          val = cson_kvp_value(kvp);
  1771   1771          ...
  1772   1772      }
  1773   1773      @endcode
  1774   1774   
  1775   1775      There is no need to clean up an iterator, as it holds no dynamic resources.
  1776         -
         1776  +   
  1777   1777      @see cson_kvp_key()
  1778   1778      @see cson_kvp_value()
  1779   1779   */
  1780   1780   cson_kvp * cson_object_iter_next( cson_object_iterator * iter );
  1781   1781   
  1782   1782   
  1783   1783   /**
................................................................................
  1861   1861          @code
  1862   1862          void * myptr = buf.mem;
  1863   1863          buf = cson_buffer_empty;
  1864   1864          @endcode
  1865   1865   
  1866   1866          (You might also need to store buf.used and buf.capacity,
  1867   1867          depending on what you want to do with the memory.)
  1868         -
         1868  +       
  1869   1869          When doing so, the memory must eventually be passed to free()
  1870   1870          to deallocate it.
  1871   1871       */
  1872   1872       unsigned char * mem;
  1873   1873   };
  1874   1874   /** Convenience typedef. */
  1875   1875   typedef struct cson_buffer cson_buffer;
................................................................................
  1892   1892      contents, and it should not be used except to free its contents.
  1893   1893   
  1894   1894      On error non-zero is returned. Errors include:
  1895   1895   
  1896   1896      - Invalid arguments: cson_rc.ArgError
  1897   1897   
  1898   1898      - Buffer cannot be expanded (runs out of memory): cson_rc.AllocError
  1899         -
         1899  +   
  1900   1900      Example usage:
  1901   1901   
  1902   1902      @code
  1903   1903      cson_buffer buf = cson_buffer_empty;
  1904   1904      // optional: cson_buffer_reserve(&buf, 1024 * 10);
  1905   1905      int rc = cson_output_buffer( myValue, &buf, NULL );
  1906   1906      if( 0 != rc ) {
................................................................................
  1916   1916      {
  1917   1917          char * mem = (char *)buf.mem;
  1918   1918          buf = cson_buffer_empty;
  1919   1919          ...
  1920   1920          free(mem);
  1921   1921      }
  1922   1922      @endcode
  1923         -
         1923  +   
  1924   1924      @see cson_output()
  1925         -
         1925  +   
  1926   1926   */
  1927   1927   int cson_output_buffer( cson_value const * v, cson_buffer * buf,
  1928   1928                           cson_output_opt const * opt );
  1929   1929   
  1930   1930   /**
  1931   1931      This works identically to cson_parse_string(), but takes a
  1932   1932      cson_buffer object as its input.  buf->used bytes of buf->mem are
................................................................................
  1991   1991      Whether or not this function succeeds, dest still owns any memory
  1992   1992      pointed to by dest->mem, and the client must eventually free it by
  1993   1993      calling cson_buffer_reserve(dest,0).
  1994   1994   
  1995   1995      dest->mem might (and possibly will) be (re)allocated by this
  1996   1996      function, so any pointers to it held from before this call might be
  1997   1997      invalidated by this call.
  1998         -
         1998  +   
  1999   1999      On error non-0 is returned and dest has almost certainly been
  2000   2000      modified but its state must be considered incomplete.
  2001   2001   
  2002   2002      Errors include:
  2003   2003   
  2004   2004      - dest or src are NULL (cson_rc.ArgError)
  2005   2005   
................................................................................
  2040   2040   
  2041   2041       @code
  2042   2042       void * mem = buf.mem;
  2043   2043       buf = cson_buffer_empty;
  2044   2044       @endcode
  2045   2045   
  2046   2046       In which case the memory must eventually be passed to free() to
  2047         -    free it.
         2047  +    free it.    
  2048   2048   */
  2049   2049   int cson_buffer_fill_from( cson_buffer * dest, cson_data_source_f src, void * state );
  2050   2050   
  2051   2051   
  2052   2052   /**
  2053   2053      Increments the reference count for the given value. This is a
  2054   2054      low-level operation and should not normally be used by client code
................................................................................
  2070   2070      point adds a reference and simply passed the value to
  2071   2071      cson_value_free() when they're done. The object will be kept alive
  2072   2072      for other sharing points which added a reference.
  2073   2073   
  2074   2074      Normally any such value handles would be invalidated when the
  2075   2075      parent container(s) is/are cleaned up, but this function can be
  2076   2076      used to effectively delay the cleanup.
  2077         -
         2077  +   
  2078   2078      This function, at its lowest level, increments the value's
  2079   2079      reference count by 1.
  2080   2080   
  2081   2081      To decrement the reference count, pass the value to
  2082   2082      cson_value_free(), after which the value must be considered, from
  2083   2083      the perspective of that client code, to be destroyed (though it
  2084   2084      will not be if there are still other live references to
................................................................................
  2089   2089      (cson_rc.ArgError) or if the reference increment would overflow
  2090   2090      (cson_rc.RangeError). In theory a client would get allocation
  2091   2091      errors long before the reference count could overflow (assuming
  2092   2092      those reference counts come from container insertions, as opposed
  2093   2093      to via this function).
  2094   2094   
  2095   2095      Insider notes which clients really need to know:
  2096         -
         2096  +   
  2097   2097      For shared/constant value instances, such as those returned by
  2098   2098      cson_value_true() and cson_value_null(), this function has no side
  2099   2099      effects - it does not actually modify the reference count because
  2100   2100      (A) those instances are shared across all client code and (B) those
  2101   2101      objects are static and never get cleaned up. However, that is an
  2102   2102      implementation detail which client code should not rely on. In
  2103   2103      other words, if you call cson_value_add_reference() 3 times using
................................................................................
  2171   2171      eventually free the value using cson_value_free() or add it to a
  2172   2172      container object/array to transfer ownership to the container. The
  2173   2173      returned object will be of the same logical type as orig.
  2174   2174   
  2175   2175      ACHTUNG: if orig contains any cyclic references at any depth level
  2176   2176      this function will endlessly recurse. (Having _any_ cyclic
  2177   2177      references violates this library's requirements.)
  2178         -
         2178  +   
  2179   2179      Returns NULL if orig is NULL or if cloning fails. Assuming that
  2180   2180      orig is in a valid state, the only "likely" error case is that an
  2181   2181      allocation fails while constructing the clone. In other words, if
  2182   2182      cloning fails due to something other than an allocation error then
  2183   2183      either orig is in an invalid state or there is a bug.
  2184   2184   
  2185   2185      When this function clones Objects or Arrays it shares any immutable
................................................................................
  2276   2276      --key : Treats key as a boolean with a true value.
  2277   2277   
  2278   2278      --key=VAL : Treats VAL as either a double, integer, or string.
  2279   2279   
  2280   2280      --key= : Treats key as a JSON null (not literal NULL) value.
  2281   2281   
  2282   2282      Arguments not starting with a dash are skipped.
  2283         -
         2283  +   
  2284   2284      Each key/value pair is inserted into an object.  If a given key
  2285   2285      appears more than once then only the final entry is actually
  2286   2286      stored.
  2287   2287   
  2288   2288      argc and argv are expected to be values from main() (or similar,
  2289   2289      possibly adjusted to remove argv[0]).
  2290   2290   
................................................................................
  2432   2432      type depending on the field type reported by sqlite3_column_type(st,col):
  2433   2433   
  2434   2434      Integer, double, null, or string (TEXT and BLOB data, though not
  2435   2435      all blob data is legal for a JSON string).
  2436   2436   
  2437   2437      st must be a sqlite3_step()'d row and col must be a 0-based column
  2438   2438      index within that result row.
  2439         - */
         2439  + */       
  2440   2440   cson_value * cson_sqlite3_column_to_value( sqlite3_stmt * st, int col );
  2441   2441   
  2442   2442   /**
  2443   2443      Creates a JSON Array object containing the names of all columns
  2444         -   of the given prepared statement handle.
  2445         -
         2444  +   of the given prepared statement handle. 
         2445  +    
  2446   2446      Returns a new array value on success, which the caller owns. Its elements
  2447   2447      are in the same order as in the underlying query.
  2448   2448   
  2449   2449      On error NULL is returned.
  2450         -
         2450  +    
  2451   2451      st is not traversed or freed by this function - only the column
  2452   2452      count and names are read.
  2453   2453   */
  2454   2454   cson_value * cson_sqlite3_column_names( sqlite3_stmt * st );
  2455   2455   
  2456   2456   /**
  2457   2457      Creates a JSON Object containing key/value pairs corresponding
................................................................................
  2492   2492      value which contains the JSON-form values of the given result
  2493   2493      set row.
  2494   2494   */
  2495   2495   cson_value * cson_sqlite3_row_to_array( sqlite3_stmt * st );
  2496   2496   /**
  2497   2497       Converts the results of an sqlite3 SELECT statement to JSON,
  2498   2498       in the form of a cson_value object tree.
  2499         -
         2499  +    
  2500   2500       st must be a prepared, but not yet traversed, SELECT query.
  2501   2501       tgt must be a pointer to NULL (see the example below). If
  2502   2502       either of those arguments are NULL, cson_rc.ArgError is returned.
  2503         -
         2503  +    
  2504   2504       This walks the query results and returns a JSON object which
  2505   2505       has a different structure depending on the value of the 'fat'
  2506   2506       argument.
  2507         -
  2508         -
         2507  +    
         2508  +    
  2509   2509       If 'fat' is 0 then the structure is:
  2510         -
         2510  +    
  2511   2511       @code
  2512   2512       {
  2513   2513           "columns":["colName1",..."colNameN"],
  2514   2514           "rows":[
  2515   2515               [colVal0, ... colValN],
  2516   2516               [colVal0, ... colValN],
  2517   2517               ...
  2518   2518           ]
  2519   2519       }
  2520   2520       @endcode
  2521         -
         2521  +    
  2522   2522       In the "non-fat" format the order of the columns and row values is
  2523   2523       guaranteed to be the same as that of the underlying query.
  2524         -
         2524  +    
  2525   2525       If 'fat' is not 0 then the structure is:
  2526         -
         2526  +    
  2527   2527       @code
  2528   2528       {
  2529   2529           "columns":["colName1",..."colNameN"],
  2530   2530           "rows":[
  2531   2531               {"colName1":value1,..."colNameN":valueN},
  2532   2532               {"colName1":value1,..."colNameN":valueN},
  2533   2533               ...
................................................................................
  2541   2541       change when passed through different JSON implementations,
  2542   2542       depending on how they implement object key/value pairs.
  2543   2543   
  2544   2544       On success it returns 0 and assigns *tgt to a newly-allocated
  2545   2545       JSON object tree (using the above structure), which the caller owns.
  2546   2546       If the query returns no rows, the "rows" value will be an empty
  2547   2547       array, as opposed to null.
  2548         -
         2548  +    
  2549   2549       On error non-0 is returned and *tgt is not modified.
  2550         -
         2550  +    
  2551   2551       The error code cson_rc.IOError is used to indicate a db-level
  2552   2552       error, and cson_rc.TypeError is returned if sqlite3_column_count(st)
  2553   2553       returns 0 or less (indicating an invalid or non-SELECT statement).
  2554         -
         2554  +    
  2555   2555       The JSON data types are determined by the column type as reported
  2556   2556       by sqlite3_column_type():
  2557         -
         2557  +    
  2558   2558       SQLITE_INTEGER: integer
  2559         -
         2559  +    
  2560   2560       SQLITE_FLOAT: double
  2561         -
         2561  +    
  2562   2562       SQLITE_TEXT or SQLITE_BLOB: string, and this will only work if
  2563   2563       the data is UTF8 compatible.
  2564         -
         2564  +    
  2565   2565       If the db returns a literal or SQL NULL for a value it is converted
  2566   2566       to a JSON null. If it somehow finds a column type it cannot handle,
  2567   2567       the value is also converted to a NULL in the output.
  2568   2568   
  2569   2569       Example
  2570         -
         2570  +    
  2571   2571       @code
  2572   2572       cson_value * json = NULL;
  2573   2573       int rc = cson_sqlite3_stmt_to_json( myStatement, &json, 1 );
  2574   2574       if( 0 != rc ) { ... error ... }
  2575   2575       else {
  2576   2576           cson_output_FILE( json, stdout, NULL );
  2577   2577           cson_value_free( json );
................................................................................
  2601   2601      position (starting and ndx, though the array uses 0-based offsets).
  2602   2602   
  2603   2603      TODO: add Object support for named parameters.
  2604   2604   
  2605   2605      Returns 0 on success, non-0 on error.
  2606   2606    */
  2607   2607   int cson_sqlite3_bind_value( sqlite3_stmt * st, int ndx, cson_value const * v );
  2608         -
         2608  +    
  2609   2609   #if defined(__cplusplus)
  2610   2610   } /*extern "C"*/
  2611   2611   #endif
  2612         -
         2612  +    
  2613   2613   #endif /* CSON_ENABLE_SQLITE3 */
  2614   2614   #endif /* WANDERINGHORSE_NET_CSON_SQLITE3_H_INCLUDED */
  2615   2615   /* end file include/wh/cson/cson_sqlite3.h */
  2616   2616   #endif /* FOSSIL_ENABLE_JSON */

Changes to src/db.c.

    52     52   ** structure.
    53     53   */
    54     54   struct Stmt {
    55     55     Blob sql;               /* The SQL for this statement */
    56     56     sqlite3_stmt *pStmt;    /* The results of sqlite3_prepare_v2() */
    57     57     Stmt *pNext, *pPrev;    /* List of all unfinalized statements */
    58     58     int nStep;              /* Number of sqlite3_step() calls */
           59  +  int rc;                 /* Error from db_vprepare() */
    59     60   };
    60     61   
    61     62   /*
    62     63   ** Copy this to initialize a Stmt object to a clean/empty state. This
    63     64   ** is useful to help avoid assertions when performing cleanup in some
    64     65   ** error handling cases.
    65     66   */
    66         -#define empty_Stmt_m {BLOB_INITIALIZER,NULL, NULL, NULL, 0}
           67  +#define empty_Stmt_m {BLOB_INITIALIZER,NULL, NULL, NULL, 0, 0}
    67     68   #endif /* INTERFACE */
    68     69   const struct Stmt empty_Stmt = empty_Stmt_m;
    69     70   
    70     71   /*
    71     72   ** Call this routine when a database error occurs.
    72     73   */
    73     74   static void db_err(const char *zFormat, ...){
           75  +  static int rcLooping = 0;
    74     76     va_list ap;
    75     77     char *z;
    76     78     int rc = 1;
           79  +  if( rcLooping ) exit(rcLooping);
    77     80     va_start(ap, zFormat);
    78     81     z = vmprintf(zFormat, ap);
    79     82     va_end(ap);
    80     83   #ifdef FOSSIL_ENABLE_JSON
    81     84     if( g.json.isJsonMode ){
    82     85       json_err( 0, z, 1 );
    83     86       if( g.isHTTP ){
................................................................................
    95     98       g.cgiOutput = 0;
    96     99       cgi_printf("<h1>Database Error</h1>\n<p>%h</p>\n", z);
    97    100       cgi_reply();
    98    101     }else{
    99    102       fprintf(stderr, "%s: %s\n", g.argv[0], z);
   100    103     }
   101    104     free(z);
          105  +  rcLooping = rc;
   102    106     db_force_rollback();
   103    107     fossil_exit(rc);
   104    108   }
   105    109   
   106    110   /*
   107    111   ** All static variable that a used by only this file are gathered into
   108    112   ** the following structure.
................................................................................
   271    275     }
   272    276     rc = sqlite3_prepare_v3(g.db, zSql, -1, prepFlags, &pStmt->pStmt, 0);
   273    277     if( rc!=0 && (flags & DB_PREPARE_IGNORE_ERROR)!=0 ){
   274    278       db_err("%s\n%s", sqlite3_errmsg(g.db), zSql);
   275    279     }
   276    280     pStmt->pNext = pStmt->pPrev = 0;
   277    281     pStmt->nStep = 0;
          282  +  pStmt->rc = rc;
   278    283     return rc;
   279    284   }
   280    285   int db_prepare(Stmt *pStmt, const char *zFormat, ...){
   281    286     int rc;
   282    287     va_list ap;
   283    288     va_start(ap, zFormat);
   284    289     rc = db_vprepare(pStmt, 0, zFormat, ap);
................................................................................
   357    362   
   358    363   /*
   359    364   ** Step the SQL statement.  Return either SQLITE_ROW or an error code
   360    365   ** or SQLITE_OK if the statement finishes successfully.
   361    366   */
   362    367   int db_step(Stmt *pStmt){
   363    368     int rc;
          369  +  if( pStmt->pStmt==0 ) return pStmt->rc;
   364    370     rc = sqlite3_step(pStmt->pStmt);
   365    371     pStmt->nStep++;
   366    372     return rc;
   367    373   }
   368    374   
   369    375   /*
   370    376   ** Print warnings if a query is inefficient.
................................................................................
  1272   1278     }
  1273   1279     if( zHome==0 ){
  1274   1280       if( isOptional ) return 0;
  1275   1281       fossil_fatal("cannot locate home directory - please set the "
  1276   1282                    "FOSSIL_HOME or HOME environment variables");
  1277   1283     }
  1278   1284   #endif
  1279         -  if( file_isdir(zHome)!=1 ){
         1285  +  if( file_isdir(zHome, ExtFILE)!=1 ){
  1280   1286       if( isOptional ) return 0;
  1281   1287       fossil_fatal("invalid home directory: %s", zHome);
  1282   1288     }
  1283   1289   #if defined(_WIN32) || defined(__CYGWIN__)
  1284   1290     /* . filenames give some window systems problems and many apps problems */
  1285   1291     zDbName = mprintf("%//_fossil", zHome);
  1286   1292   #else
  1287   1293     zDbName = mprintf("%s/.fossil", zHome);
  1288   1294   #endif
  1289         -  if( file_size(zDbName)<1024*3 ){
         1295  +  if( file_size(zDbName, ExtFILE)<1024*3 ){
  1290   1296       if( file_access(zHome, W_OK) ){
  1291   1297         if( isOptional ) return 0;
  1292   1298         fossil_fatal("home directory %s must be writeable", zHome);
  1293   1299       }
  1294   1300       db_init_database(zDbName, zConfigSchema, (char*)0);
  1295   1301     }
  1296   1302     if( file_access(zDbName, W_OK) ){
................................................................................
  1350   1356   ** true.  If it is not a valid local database file, return 0.
  1351   1357   */
  1352   1358   static int isValidLocalDb(const char *zDbName){
  1353   1359     i64 lsize;
  1354   1360     char *zVFileDef;
  1355   1361   
  1356   1362     if( file_access(zDbName, F_OK) ) return 0;
  1357         -  lsize = file_size(zDbName);
         1363  +  lsize = file_size(zDbName, ExtFILE);
  1358   1364     if( lsize%1024!=0 || lsize<4096 ) return 0;
  1359   1365     db_open_or_attach(zDbName, "localdb");
  1360   1366     zVFileDef = db_text(0, "SELECT sql FROM localdb.sqlite_master"
  1361   1367                            " WHERE name=='vfile'");
  1362   1368     if( zVFileDef==0 ) return 0;
  1363   1369   
  1364   1370     /* If the "isexe" column is missing from the vfile table, then
................................................................................
  1491   1497       if( g.localOpen ){
  1492   1498         zDbName = db_repository_filename();
  1493   1499       }
  1494   1500       if( zDbName==0 ){
  1495   1501         db_err("unable to find the name of a repository database");
  1496   1502       }
  1497   1503     }
  1498         -  if( file_access(zDbName, R_OK) || file_size(zDbName)<1024 ){
         1504  +  if( file_access(zDbName, R_OK) || file_size(zDbName, ExtFILE)<1024 ){
  1499   1505       if( file_access(zDbName, F_OK) ){
  1500   1506   #ifdef FOSSIL_ENABLE_JSON
  1501   1507         g.json.resultCode = FSL_JSON_E_DB_NOT_FOUND;
  1502   1508   #endif
  1503   1509         fossil_panic("repository does not exist or"
  1504   1510                      " is in an unreadable directory: %s", zDbName);
  1505   1511       }else if( file_access(zDbName, R_OK) ){
................................................................................
  1546   1552   ** option to locate the repository.  If no such option is available, then
  1547   1553   ** use the repository of the open checkout if there is one.
  1548   1554   **
  1549   1555   ** Error out if the repository cannot be opened.
  1550   1556   */
  1551   1557   void db_find_and_open_repository(int bFlags, int nArgUsed){
  1552   1558     const char *zRep = find_repository_option();
  1553         -  if( zRep && file_isdir(zRep)==1 ){
         1559  +  if( zRep && file_isdir(zRep, ExtFILE)==1 ){
  1554   1560       goto rep_not_found;
  1555   1561     }
  1556   1562     if( zRep==0 && nArgUsed && g.argc==nArgUsed+1 ){
  1557   1563       zRep = g.argv[nArgUsed];
  1558   1564     }
  1559   1565     if( zRep==0 ){
  1560   1566       if( db_open_local(0)==0 ){
................................................................................
  1981   1987     /* We should be done with options.. */
  1982   1988     verify_all_options();
  1983   1989   
  1984   1990     if( g.argc!=3 ){
  1985   1991       usage("REPOSITORY-NAME");
  1986   1992     }
  1987   1993   
  1988         -  if( -1 != file_size(g.argv[2]) ){
         1994  +  if( -1 != file_size(g.argv[2], ExtFILE) ){
  1989   1995       fossil_fatal("file already exists: %s", g.argv[2]);
  1990   1996     }
  1991   1997   
  1992   1998     db_create_repository(g.argv[2]);
  1993   1999     db_open_repository(g.argv[2]);
  1994   2000     db_open_config(0, 0);
  1995   2001     if( zTemplate ) db_attach(zTemplate, "settingSrc");
................................................................................
  2278   2284         blob_append(&versionedPathname, ".no-warn", -1);
  2279   2285         blob_zero(&noWarnFile);
  2280   2286         if( historical_blob(g.zOpenRevision, blob_str(&versionedPathname),
  2281   2287             &noWarnFile, 0) ){
  2282   2288           noWarn = 1;
  2283   2289         }
  2284   2290         blob_reset(&noWarnFile);
  2285         -    }else if( file_size(blob_str(&versionedPathname))>=0 ){
         2291  +    }else if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){
  2286   2292         /* File exists, and contains the value for this setting. Load from
  2287   2293         ** the file. */
  2288         -      if( blob_read_from_file(&setting, blob_str(&versionedPathname))>=0 ){
         2294  +      const char *zFile = blob_str(&versionedPathname);
         2295  +      if( blob_read_from_file(&setting, zFile, ExtFILE)>=0 ){
  2289   2296           found = 1;
  2290   2297         }
  2291   2298         /* See if there's a no-warn flag */
  2292   2299         blob_append(&versionedPathname, ".no-warn", -1);
  2293         -      if( file_size(blob_str(&versionedPathname))>=0 ){
         2300  +      if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){
  2294   2301           noWarn = 1;
  2295   2302         }
  2296   2303       }
  2297   2304       blob_reset(&versionedPathname);
  2298   2305       if( found ){
  2299   2306         blob_trim(&setting); /* Avoid non-obvious problems with line endings
  2300   2307                              ** on boolean properties */
................................................................................
  2470   2477   }
  2471   2478   int db_lget_int(const char *zName, int dflt){
  2472   2479     return db_int(dflt, "SELECT value FROM vvar WHERE name=%Q", zName);
  2473   2480   }
  2474   2481   void db_lset_int(const char *zName, int value){
  2475   2482     db_multi_exec("REPLACE INTO vvar(name,value) VALUES(%Q,%d)", zName, value);
  2476   2483   }
         2484  +
         2485  +/* Va-args versions of db_get(), db_set(), and db_unset()
         2486  +*/
         2487  +char *db_get_mprintf(const char *zDefault, const char *zFormat, ...){
         2488  +  va_list ap;
         2489  +  char *zName;
         2490  +  char *zResult;
         2491  +  va_start(ap, zFormat);
         2492  +  zName = vmprintf(zFormat, ap);
         2493  +  va_end(ap);
         2494  +  zResult = db_get(zName, zDefault);
         2495  +  fossil_free(zName);
         2496  +  return zResult;
         2497  +}
         2498  +void db_set_mprintf(const char *zNew, int iGlobal, const char *zFormat, ...){
         2499  +  va_list ap;
         2500  +  char *zName;
         2501  +  va_start(ap, zFormat);
         2502  +  zName = vmprintf(zFormat, ap);
         2503  +  va_end(ap);
         2504  +  db_set(zName, zNew, iGlobal);
         2505  +  fossil_free(zName);
         2506  +}
         2507  +void db_unset_mprintf(int iGlobal, const char *zFormat, ...){
         2508  +  va_list ap;
         2509  +  char *zName;
         2510  +  va_start(ap, zFormat);
         2511  +  zName = vmprintf(zFormat, ap);
         2512  +  va_end(ap);
         2513  +  db_unset(zName, iGlobal);
         2514  +  fossil_free(zName);
         2515  +}
         2516  +
         2517  +
  2477   2518   
  2478   2519   #if INTERFACE
  2479   2520   /* Manifest generation flags */
  2480   2521   #define MFESTFLG_RAW  0x01
  2481   2522   #define MFESTFLG_UUID 0x02
  2482   2523   #define MFESTFLG_TAGS 0x04
  2483   2524   #endif /* INTERFACE */
................................................................................
  2727   2768     }
  2728   2769     if( pSetting->versionable && g.localOpen ){
  2729   2770       /* Check to see if this is overridden by a versionable settings file */
  2730   2771       Blob versionedPathname;
  2731   2772       blob_zero(&versionedPathname);
  2732   2773       blob_appendf(&versionedPathname, "%s.fossil-settings/%s",
  2733   2774                    g.zLocalRoot, pSetting->name);
  2734         -    if( file_size(blob_str(&versionedPathname))>=0 ){
         2775  +    if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){
  2735   2776         fossil_print("  (overridden by contents of file .fossil-settings/%s)\n",
  2736   2777                      pSetting->name);
  2737   2778       }
  2738   2779     }
  2739   2780     db_finalize(&q);
  2740   2781   }
  2741   2782   
................................................................................
  2775   2816   **
  2776   2817   ** When the admin-log setting is enabled, configuration changes are recorded
  2777   2818   ** in the "admin_log" table of the repository.
  2778   2819   */
  2779   2820   #if defined(_WIN32)
  2780   2821   /*
  2781   2822   ** SETTING: allow-symlinks  boolean default=off versionable
  2782         -** Allows symbolic links in the repository when enabled.
         2823  +**
         2824  +** When allow-symlinks is OFF, symbolic links in the repository are followed
         2825  +** and treated no differently from real files.  When allow-symlinks is ON,
         2826  +** the object to which the symbolic link points is ignored, and the content
         2827  +** of the symbolic link that is stored in the repository is the name of the
         2828  +** object to which the symbolic link points.
  2783   2829   */
  2784   2830   #endif
  2785   2831   #if !defined(_WIN32)
  2786   2832   /*
  2787   2833   ** SETTING: allow-symlinks  boolean default=on versionable
  2788         -** Allows symbolic links in the repository when enabled.
         2834  +**
         2835  +** When allow-symlinks is OFF, symbolic links in the repository are followed
         2836  +** and treated no differently from real files.  When allow-symlinks is ON,
         2837  +** the object to which the symbolic link points is ignored, and the content
         2838  +** of the symbolic link that is stored in the repository is the name of the
         2839  +** object to which the symbolic link points.
  2789   2840   */
  2790   2841   #endif
  2791   2842   /*
  2792   2843   ** SETTING: auto-captcha    boolean default=on variable=autocaptcha
  2793   2844   ** If enabled, the /login page provides a button that will automatically
  2794   2845   ** fill in the captcha password.  This makes things easier for human users,
  2795   2846   ** at the expense of also making logins easier for malecious robots.

Added src/default_css.txt.

            1  +// This is the template file for the default CSS for Fossil.  Lines
            2  +// beginning with "//" are stripped out by the pre-processor and never
            3  +// reach the web browser.
            4  +//
            5  +// Each repository skin has skin-specific CSS.  The rules contained in this
            6  +// file are appended to the skin-CSS as required.  Each rule is evaluated
            7  +// separately and is only appended to the final CSS if there is not an
            8  +// overriding rule with the same selector in the skin-CSS.
            9  +//
           10  +div.sidebox {
           11  +  float: right;
           12  +  background-color: white;
           13  +  border-width: medium;
           14  +  border-style: double;
           15  +  margin: 10px;
           16  +}
           17  +div.sideboxTitle {
           18  +  display: inline;
           19  +  font-weight: bold;
           20  +}
           21  +div.sideboxDescribed {
           22  +  display: inline;
           23  +  font-weight: bold;
           24  +}
           25  +span.disabled {
           26  +  color: red;
           27  +}
           28  +table.timelineTable {
           29  +  border-spacing: 0px 2px;
           30  +}
           31  +.timelineDate {
           32  +  white-space: nowrap;
           33  +}
           34  +span.timelineDisabled {
           35  +  font-style: italic;
           36  +  font-size: small;
           37  +}
           38  +tr.timelineCurrent {
           39  +  padding: .1em .2em;
           40  +  border: 1px dashed #446979;
           41  +  box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.5);
           42  +}
           43  +tr.timelineSelected {
           44  +  padding: .1em .2em;
           45  +  border: 2px solid lightgray;
           46  +  background-color: #ffc;
           47  +  box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.5);
           48  +}
           49  +tr.timelineSelected td {
           50  +  border-radius: 0;
           51  +  border-width: 0;
           52  +}
           53  +tr.timelineCurrent td {
           54  +  border-radius: 0;
           55  +  border-width: 0;
           56  +}
           57  +span.timelineLeaf {
           58  +  font-weight: bold;
           59  +}
           60  +span.timelineHistDsp {
           61  +  font-weight: bold;
           62  +}
           63  +td.timelineTime {
           64  +  vertical-align: top;
           65  +  text-align: right;
           66  +  white-space: nowrap;
           67  +}
           68  +td.timelineGraph {
           69  +  width: 20px;
           70  +  text-align: left;
           71  +  vertical-align: top;
           72  +}
           73  +span.timelineCompactComment {
           74  +  cursor: pointer;
           75  +}
           76  +span.timelineEllipsis {
           77  +  cursor: pointer;
           78  +}
           79  +.timelineModernCell, .timelineColumnarCell, .timelineDetailCell {
           80  +  vertical-align: top;
           81  +  text-align: left;
           82  +  padding: 0.75em;
           83  +  border-radius: 1em;
           84  +}
           85  +.timelineModernCell[id], .timelineColumnarCell[id], .timelineDetailCell[id] {
           86  +  background-color: #efefef;
           87  +}
           88  +.timelineModernDetail {
           89  +  font-size: 80%;
           90  +  text-align: right;
           91  +  float: right;
           92  +  opacity: 0.75;
           93  +  margin-top: 0.5em;
           94  +  margin-left: 1em;
           95  +}
           96  +.tl-canvas {
           97  +  margin: 0 6px 0 10px;
           98  +}
           99  +.tl-rail {
          100  +  width: 18px;
          101  +}
          102  +.tl-mergeoffset {
          103  +  width: 2px;
          104  +}
          105  +.tl-nodemark {
          106  +  margin-top: 5px;
          107  +}
          108  +.tl-node {
          109  +  width: 10px;
          110  +  height: 10px;
          111  +  border: 1px solid #000;
          112  +  background: #fff;
          113  +  cursor: pointer;
          114  +}
          115  +.tl-node.leaf:after {
          116  +  content: '';
          117  +  position: absolute;
          118  +  top: 3px;
          119  +  left: 3px;
          120  +  width: 4px;
          121  +  height: 4px;
          122  +  background: #000;
          123  +}
          124  +.tl-node.sel:after {
          125  +  content: '';
          126  +  position: absolute;
          127  +  top: 2px;
          128  +  left: 2px;
          129  +  width: 6px;
          130  +  height: 6px;
          131  +  background: red;
          132  +}
          133  +.tl-arrow {
          134  +  width: 0;
          135  +  height: 0;
          136  +  transform: scale(.999);
          137  +  border: 0 solid transparent;
          138  +}
          139  +.tl-arrow.u {
          140  +  margin-top: -1px;
          141  +  border-width: 0 3px;
          142  +  border-bottom: 7px solid #000;
          143  +}
          144  +.tl-arrow.u.sm {
          145  +  border-bottom: 5px solid #000;
          146  +}
          147  +.tl-line {
          148  +  background: #000;
          149  +  width: 2px;
          150  +}
          151  +.tl-arrow.merge {
          152  +  height: 1px;
          153  +  border-width: 2px 0;
          154  +}
          155  +.tl-arrow.merge.l {
          156  +  border-right: 3px solid #000;
          157  +}
          158  +.tl-arrow.merge.r {
          159  +  border-left: 3px solid #000;
          160  +}
          161  +.tl-line.merge {
          162  +  width: 1px;
          163  +}
          164  +.tl-arrow.warp {
          165  +  margin-left: 1px;
          166  +  border-width: 3px 0;
          167  +  border-left: 7px solid #600000;
          168  +}
          169  +.tl-line.warp {
          170  +  background: #600000;
          171  +}
          172  +span.tagDsp {
          173  +  font-weight: bold;
          174  +}
          175  +span.wikiError {
          176  +  font-weight: bold;
          177  +  color: red;
          178  +}
          179  +span.infoTagCancelled {
          180  +  font-weight: bold;
          181  +  text-decoration: line-through;
          182  +}
          183  +span.infoTag {
          184  +  font-weight: bold;
          185  +}
          186  +span.wikiTagCancelled {
          187  +  text-decoration: line-through;
          188  +}
          189  +table.browser {
          190  +  width: 100%;
          191  +  border: 0;
          192  +}
          193  +td.browser {
          194  +  width: 24%;
          195  +  vertical-align: top;
          196  +}
          197  +.filetree {
          198  +  margin: 1em 0;
          199  +  line-height: 1.5;
          200  +}
          201  +.filetree > ul {
          202  +  display: inline-block;
          203  +}
          204  +.filetree ul {
          205  +  margin: 0;
          206  +  padding: 0;
          207  +  list-style: none;
          208  +}
          209  +.filetree ul.collapsed {
          210  +  display: none;
          211  +}
          212  +.filetree ul ul {
          213  +  position: relative;
          214  +  margin: 0 0 0 21px;
          215  +}
          216  +.filetree li {
          217  +  position: relative;
          218  +  margin: 0;
          219  +  padding: 0;
          220  +}
          221  +.filetree li li:before {
          222  +  content: '';
          223  +  position: absolute;
          224  +  top: -.8em;
          225  +  left: -14px;
          226  +  width: 14px;
          227  +  height: 1.5em;
          228  +  border-left: 2px solid #aaa;
          229  +  border-bottom: 2px solid #aaa;
          230  +}
          231  +.filetree li > ul:before {
          232  +  content: '';
          233  +  position: absolute;
          234  +  top: -1.5em;
          235  +  bottom: 0;
          236  +  left: -35px;
          237  +  border-left: 2px solid #aaa;
          238  +}
          239  +.filetree li.last > ul:before {
          240  +  display: none;
          241  +}
          242  +.filetree a {
          243  +  position: relative;
          244  +  z-index: 1;
          245  +  display: table-cell;
          246  +  min-height: 16px;
          247  +  padding-left: 21px;
          248  +  background-image: url(data:image/gif;base64,R0lGODlhEAAQAJEAAP\/\/\/yEhIf\/\/\/wAAACH5BAEHAAIALAAAAAAQABAAAAIvlIKpxqcfmgOUvoaqDSCxrEEfF14GqFXImJZsu73wepJzVMNxrtNTj3NATMKhpwAAOw==);
          249  +  background-position: center left;
          250  +  background-repeat: no-repeat;
          251  +}
          252  +ul.browser {
          253  +  list-style-type: none;
          254  +  padding: 10px;
          255  +  margin: 0px;
          256  +  white-space: nowrap;
          257  +}
          258  +ul.browser li.file {
          259  +  background-image: url(data:image/gif;base64,R0lGODlhEAAQAJEAAP\/\/\/yEhIf\/\/\/wAAACH5BAEHAAIALAAAAAAQABAAAAIvlIKpxqcfmgOUvoaqDSCxrEEfF14GqFXImJZsu73wepJzVMNxrtNTj3NATMKhpwAAOw==);
          260  +  background-repeat: no-repeat;
          261  +  background-position: 0px center;
          262  +  padding-left: 20px;
          263  +  padding-top: 2px;
          264  +}
          265  +ul.browser li.dir {
          266  +  background-image: url(data:image/gif;base64,R0lGODlhEAAQAJEAAP/WVCIiIv\/\/\/wAAACH5BAEHAAIALAAAAAAQABAAAAInlI9pwa3XYniCgQtkrAFfLXkiFo1jaXpo+jUs6b5Z/K4siDu5RPUFADs=);
          267  +  background-repeat: no-repeat;
          268  +  background-position: 0px center;
          269  +  padding-left: 20px;
          270  +  padding-top: 2px;
          271  +}
          272  +div.filetreeline {
          273  +  display: table;
          274  +  width: 100%;
          275  +  white-space: nowrap;
          276  +}
          277  +.filetree .dir > div.filetreeline > a {
          278  +  background-image: url(data:image/gif;base64,R0lGODlhEAAQAJEAAP/WVCIiIv\/\/\/wAAACH5BAEHAAIALAAAAAAQABAAAAInlI9pwa3XYniCgQtkrAFfLXkiFo1jaXpo+jUs6b5Z/K4siDu5RPUFADs=);
          279  +}
          280  +div.filetreeage {
          281  + display: table-cell;
          282  + padding-left: 3em;
          283  + text-align: right;
          284  +}
          285  +div.filetreeline:hover {
          286  + background-color: #eee;
          287  +}
          288  +table.login_out {
          289  +  text-align: left;
          290  +  margin-right: 10px;
          291  +  margin-left: 10px;
          292  +  margin-top: 10px;
          293  +}
          294  +div.captcha {
          295  +  text-align: center;
          296  +  padding: 1ex;
          297  +}
          298  +table.captcha {
          299  +  margin: auto;
          300  +  padding: 10px;
          301  +  border-width: 4px;
          302  +  border-style: double;
          303  +  border-color: black;
          304  +}
          305  +td.login_out_label {
          306  +  text-align: center;
          307  +}
          308  +span.loginError {
          309  +  color: red;
          310  +}
          311  +span.note {
          312  +  font-weight: bold;
          313  +}
          314  +span.textareaLabel {
          315  +  font-weight: bold;
          316  +}
          317  +table.usetupLayoutTable {
          318  +  outline-style: none;
          319  +  padding: 0;
          320  +  margin: 25px;
          321  +}
          322  +td.usetupColumnLayout {
          323  +  vertical-align: top
          324  +}
          325  +table.usetupUserList {
          326  +  outline-style: double;
          327  +  outline-width: 1px;
          328  +  padding: 10px;
          329  +}
          330  +th.usetupListUser {
          331  +  text-align: right;
          332  +  padding-right: 20px;
          333  +}
          334  +th.usetupListCap {
          335  +  text-align: center;
          336  +  padding-right: 15px;
          337  +}
          338  +th.usetupListCon {
          339  +  text-align: left;
          340  +}
          341  +td.usetupListUser {
          342  +  text-align: right;
          343  +  padding-right: 20px;
          344  +  white-space:nowrap;
          345  +}
          346  +td.usetupListCap {
          347  +  text-align: center;
          348  +  padding-right: 15px;
          349  +}
          350  +td.usetupListCon {
          351  +  text-align: left
          352  +}
          353  +div.ueditCapBox {
          354  +  float: left;
          355  +  margin-right: 20px;
          356  +  margin-bottom: 20px;
          357  +}
          358  +td.usetupEditLabel {
          359  +  text-align: right;
          360  +  vertical-align: top;
          361  +  white-space: nowrap;
          362  +}
          363  +span.ueditInheritNobody {
          364  +  color: green;
          365  +  padding: .2em;
          366  +}
          367  +span.ueditInheritDeveloper {
          368  +  color: red;
          369  +  padding: .2em;
          370  +}
          371  +span.ueditInheritReader {
          372  +  color: black;
          373  +  padding: .2em;
          374  +}
          375  +span.ueditInheritAnonymous {
          376  +  color: blue;
          377  +  padding: .2em;
          378  +}
          379  +span.capability {
          380  +  font-weight: bold;
          381  +}
          382  +span.usertype {
          383  +  font-weight: bold;
          384  +}
          385  +span.usertype:before {
          386  +  content:"'";
          387  +}
          388  +span.usertype:after {
          389  +  content:"'";
          390  +}
          391  +div.selectedText {
          392  +  font-weight: bold;
          393  +  color: blue;
          394  +  background-color: #d5d5ff;
          395  +  border: 1px blue solid;
          396  +}
          397  +p.missingPriv {
          398  + color: blue;
          399  +}
          400  +span.wikiruleHead {
          401  +  font-weight: bold;
          402  +}
          403  +td.tktDspLabel {
          404  +  text-align: right;
          405  +}
          406  +td.tktDspValue {
          407  +  text-align: left;
          408  +  vertical-align: top;
          409  +  background-color: #d0d0d0;
          410  +}
          411  +span.tktError {
          412  +  color: red;
          413  +  font-weight: bold;
          414  +}
          415  +table.rpteditex {
          416  +  float: right;
          417  +  margin: 0;
          418  +  padding: 0;
          419  +  width: 125px;
          420  +  text-align: center;
          421  +  border-collapse: collapse;
          422  +  border-spacing: 0;
          423  +}
          424  +table.report {
          425  +  border-collapse:collapse;
          426  +  border: 1px solid #999;
          427  +  margin: 1em 0 1em 0;
          428  +  cursor: pointer;
          429  +}
          430  +td.rpteditex {
          431  +  border-width: thin;
          432  +  border-color: #000000;
          433  +  border-style: solid;
          434  +}
          435  +div.endContent {
          436  +  clear: both;
          437  +}
          438  +p.generalError {
          439  +  color: red;
          440  +}
          441  +p.tktsetupError {
          442  +  color: red;
          443  +  font-weight: bold;
          444  +}
          445  +p.xfersetupError {
          446  +  color: red;
          447  +  font-weight: bold;
          448  +}
          449  +p.thmainError {
          450  +  color: red;
          451  +  font-weight: bold;
          452  +}
          453  +span.thTrace {
          454  +  color: red;
          455  +}
          456  +p.reportError {
          457  +  color: red;
          458  +  font-weight: bold;
          459  +}
          460  +blockquote.reportError {
          461  +  color: red;
          462  +  font-weight: bold;
          463  +}
          464  +p.noMoreShun {
          465  +  color: blue;
          466  +}
          467  +p.shunned {
          468  +  color: blue;
          469  +}
          470  +span.brokenlink {
          471  +  color: red;
          472  +}
          473  +ul.filelist {
          474  +  margin-top: 3px;
          475  +  line-height: 100%;
          476  +}
          477  +ul.filelist li {
          478  +  padding-top: 1px;
          479  +}
          480  +table.sbsdiffcols {
          481  +  width: 90%;
          482  +  border-spacing: 0;
          483  +  font-size: xx-small;
          484  +}
          485  +table.sbsdiffcols td {
          486  +  padding: 0;
          487  +  vertical-align: top;
          488  +}
          489  +table.sbsdiffcols pre {
          490  +  margin: 0;
          491  +  padding: 0;
          492  +  border: 0;
          493  +  font-size: inherit;
          494  +  background: inherit;
          495  +  color: inherit;
          496  +}
          497  +div.difflncol {
          498  +  padding-right: 1em;
          499  +  text-align: right;
          500  +  color: #a0a0a0;
          501  +}
          502  +div.difftxtcol {
          503  +  width: 45em;
          504  +  overflow-x: auto;
          505  +}
          506  +div.diffmkrcol {
          507  +  padding: 0 1em;
          508  +}
          509  +span.diffchng {
          510  +  background-color: #c0c0ff;
          511  +}
          512  +span.diffadd {
          513  +  background-color: #c0ffc0;
          514  +}
          515  +span.diffrm {
          516  +  background-color: #ffc8c8;
          517  +}
          518  +span.diffhr {
          519  +  display: inline-block;
          520  +  margin: .5em 0 1em;
          521  +  color: #0000ff;
          522  +}
          523  +span.diffln {
          524  +  color: #a0a0a0;
          525  +}
          526  +span.modpending {
          527  +  color: #b03800;
          528  +  font-style: italic;
          529  +}
          530  +pre.th1result {
          531  +  white-space: pre-wrap;
          532  +  word-wrap: break-word;
          533  +}
          534  +pre.th1error {
          535  +  white-space: pre-wrap;
          536  +  word-wrap: break-word;
          537  +  color: red;
          538  +}
          539  +.statistics-report-graph-line {
          540  +  background-color: #446979;
          541  +}
          542  +.statistics-report-table-events th {
          543  +  padding: 0 1em 0 1em;
          544  +}
          545  +.statistics-report-table-events td {
          546  +  padding: 0.1em 1em 0.1em 1em;
          547  +}
          548  +.statistics-report-row-year {
          549  +  text-align: left;
          550  +}
          551  +.statistics-report-week-number-label {
          552  +  text-align: right;
          553  +  font-size: 0.8em;
          554  +}
          555  +.statistics-report-week-of-year-list {
          556  +  font-size: 0.8em;
          557  +}
          558  +#usetupEditCapability {
          559  +  font-weight: bold;
          560  +}
          561  +table.adminLogTable {
          562  +  text-align: left;
          563  +}
          564  +.adminLogTable .adminTime {
          565  +  text-align: left;
          566  +  vertical-align: top;
          567  +  white-space: nowrap;
          568  +}
          569  +.fileage table {
          570  +  border-spacing: 0;
          571  +}
          572  +.fileage tr:hover {
          573  +  background-color: #eee;
          574  +}
          575  +.fileage td {
          576  +  vertical-align: top;
          577  +  text-align: left;
          578  +  border-top: 1px solid #ddd;
          579  +  padding-top: 3px;
          580  +}
          581  +.fileage td:first-child {
          582  +  white-space: nowrap;
          583  +}
          584  +.fileage td:nth-child(2) {
          585  +  padding-left: 1em;
          586  +  padding-right: 1em;
          587  +}
          588  +.fileage td:nth-child(3) {
          589  +  word-wrap: break-word;
          590  +  max-width: 50%;
          591  +}
          592  +.brlist table {
          593  +  border-spacing: 0;
          594  +}
          595  +.brlist table th {
          596  +  text-align: left;
          597  +  padding: 0px 1em 0.5ex 0px;
          598  +  vertical-align: bottom;
          599  +}
          600  +.brlist table td {
          601  +  padding: 0px 2em 0px 0px;
          602  +  white-space: nowrap;
          603  +}
          604  +th.sort:after {
          605  +  margin-left: .4em;
          606  +  cursor: pointer;
          607  +  text-shadow: 0 0 0 #000; 
          608  +}
          609  +th.sort.none:after {
          610  +  content: '\2666';
          611  +}
          612  +th.sort.asc:after {
          613  +  content: '\2193';
          614  +}
          615  +th.sort.desc:after {
          616  +  content: '\2191';
          617  +}
          618  +span.snippet>mark {
          619  +  background-color: inherit;
          620  +  font-weight: bold;
          621  +}
          622  +div.searchForm {
          623  +  text-align: center;
          624  +}
          625  +p.searchEmpty {
          626  +  font-style: italic;
          627  +}
          628  +.clutter {
          629  +  display: none;
          630  +}
          631  +table.label-value th {
          632  +  vertical-align: top;
          633  +  text-align: right;
          634  +  padding: 0.2ex 1ex;
          635  +}

Changes to src/deltacmd.c.

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

Changes to src/diff.c.

  2013   2013   void test_rawdiff_cmd(void){
  2014   2014     Blob a, b;
  2015   2015     int r;
  2016   2016     int i;
  2017   2017     int *R;
  2018   2018     u64 diffFlags = diff_options();
  2019   2019     if( g.argc<4 ) usage("FILE1 FILE2 ...");
  2020         -  blob_read_from_file(&a, g.argv[2]);
         2020  +  blob_read_from_file(&a, g.argv[2], ExtFILE);
  2021   2021     for(i=3; i<g.argc; i++){
  2022   2022       if( i>3 ) fossil_print("-------------------------------\n");
  2023         -    blob_read_from_file(&b, g.argv[i]);
         2023  +    blob_read_from_file(&b, g.argv[i], ExtFILE);
  2024   2024       R = text_diff(&a, &b, 0, 0, diffFlags);
  2025   2025       for(r=0; R[r] || R[r+1] || R[r+2]; r += 3){
  2026   2026         fossil_print(" copy %4d  delete %4d  insert %4d\n", R[r], R[r+1], R[r+2]);
  2027   2027       }
  2028   2028       /* free(R); */
  2029   2029       blob_reset(&b);
  2030   2030     }
................................................................................
  2054   2054       const char *zErr = re_compile(&pRe, zRe, 0);
  2055   2055       if( zErr ) fossil_fatal("regex error: %s", zErr);
  2056   2056     }
  2057   2057     diffFlag = diff_options();
  2058   2058     verify_all_options();
  2059   2059     if( g.argc!=4 ) usage("FILE1 FILE2");
  2060   2060     diff_print_filenames(g.argv[2], g.argv[3], diffFlag);
  2061         -  blob_read_from_file(&a, g.argv[2]);
  2062         -  blob_read_from_file(&b, g.argv[3]);
         2061  +  blob_read_from_file(&a, g.argv[2], ExtFILE);
         2062  +  blob_read_from_file(&b, g.argv[3], ExtFILE);
  2063   2063     blob_zero(&out);
  2064   2064     text_diff(&a, &b, &out, pRe, diffFlag);
  2065   2065     blob_write_to_file(&out, "-");
  2066   2066     re_free(pRe);
  2067   2067   }
  2068   2068   
  2069   2069   /**************************************************************************
................................................................................
  2421   2421     if( zLimit ){
  2422   2422       url_add_parameter(&url, "limit", zLimit);
  2423   2423     }
  2424   2424     url_add_parameter(&url, "w", ignoreWs ? "1" : "0");
  2425   2425     url_add_parameter(&url, "log", showLog ? "1" : "0");
  2426   2426     url_add_parameter(&url, "filevers", fileVers ? "1" : "0");
  2427   2427     style_submenu_checkbox("w", "Ignore Whitespace", 0, 0);
  2428         -  style_submenu_checkbox("log", "Log", 0, "toggle_annotation_log()");
         2428  +  style_submenu_checkbox("log", "Log", 0, "toggle_annotation_log");
  2429   2429     style_submenu_checkbox("filevers", "Link to Files", 0, 0);
  2430   2430     if( ann.bMoreToDo ){
  2431   2431       style_submenu_element("All Ancestors", "%s",
  2432   2432          url_render(&url, "limit", "none", 0, 0));
  2433   2433     }
  2434   2434     if( skin_detail_boolean("white-foreground") ){
  2435   2435       clr1 = 0xa04040;
................................................................................
  2456   2456       @ check-in %z(href("%R/info/%!S",p->zMUuid))%S(p->zMUuid)</a>
  2457   2457       @ artifact %z(href("%R/artifact/%!S",p->zFUuid))%S(p->zFUuid)</a>
  2458   2458       @ </span>
  2459   2459     }
  2460   2460     @ </ol>
  2461   2461     @ <hr />
  2462   2462     @ </div>
  2463         -  @ <script>
  2464         -  @ function toggle_annotation_log(){
  2465         -  @   var w = gebi("annotation_log");
  2466         -  @   var x = document.forms["f01"].elements["log"].checked
  2467         -  @   w.style.display = x ? "block" : "none";
  2468         -  @ }
  2469         -  @ </script>
  2470   2463   
  2471   2464     if( !ann.bMoreToDo ){
  2472   2465       assert( ann.origId==0 );  /* bMoreToDo always set for a point-to-point */
  2473   2466       @ <h2>Origin for each line in
  2474   2467       @ %z(href("%R/finfo?name=%h&ci=%!S", zFilename, zCI))%h(zFilename)</a>
  2475   2468       @ from check-in %z(href("%R/info/%!S",zCI))%S(zCI)</a>:</h2>
  2476   2469     }else if( ann.origId>0 ){

Changes to src/diff.tcl.

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

Changes to src/diffcmd.c.

   178    178     if( zDiffCmd==0 ){
   179    179       Blob out;                 /* Diff output text */
   180    180       Blob file2;               /* Content of zFile2 */
   181    181       const char *zName2;       /* Name of zFile2 for display */
   182    182   
   183    183       /* Read content of zFile2 into memory */
   184    184       blob_zero(&file2);
   185         -    if( file_wd_size(zFile2)<0 ){
          185  +    if( file_size(zFile2, ExtFILE)<0 ){
   186    186         zName2 = NULL_DEVICE;
   187    187       }else{
   188         -      if( file_wd_islink(0) ){
   189         -        blob_read_link(&file2, zFile2);
   190         -      }else{
   191         -        blob_read_from_file(&file2, zFile2);
   192         -      }
          188  +      blob_read_from_file(&file2, zFile2, ExtFILE);
   193    189         zName2 = zName;
   194    190       }
   195    191   
   196    192       /* Compute and output the differences */
   197    193       if( diffFlags & DIFF_BRIEF ){
   198    194         if( blob_compare(pFile1, &file2) ){
   199    195           fossil_print("CHANGED  %s\n", zName);
................................................................................
   235    231             fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY);
   236    232             glob_free(pBinary);
   237    233             return;
   238    234           }
   239    235           glob_free(pBinary);
   240    236         }
   241    237         blob_zero(&file2);
   242         -      if( file_wd_size(zFile2)>=0 ){
   243         -        if( file_wd_islink(0) ){
   244         -          blob_read_link(&file2, zFile2);
   245         -        }else{
   246         -          blob_read_from_file(&file2, zFile2);
   247         -        }
          238  +      if( file_size(zFile2, ExtFILE)>=0 ){
          239  +        blob_read_from_file(&file2, zFile2, ExtFILE);
   248    240         }
   249    241         if( looks_like_binary(&file2) ){
   250    242           fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY);
   251    243           blob_reset(&file2);
   252    244           return;
   253    245         }
   254    246         blob_reset(&file2);
................................................................................
   487    479         if( !isNumStat ){ fossil_print("ADDED_BY_INTEGRATE %s\n", zPathname); }
   488    480         srcid = 0;
   489    481         if( !asNewFile ){ showDiff = 0; }
   490    482       }
   491    483       if( showDiff ){
   492    484         Blob content;
   493    485         int isBin;
   494         -      if( !isLink != !file_wd_islink(zFullName) ){
          486  +      if( !isLink != !file_islink(zFullName) ){
   495    487           diff_print_index(zPathname, diffFlags);
   496    488           diff_print_filenames(zPathname, zPathname, diffFlags);
   497    489           fossil_print("%s",DIFF_CANNOT_COMPUTE_SYMLINK);
   498    490           continue;
   499    491         }
   500    492         if( srcid>0 ){
   501    493           content_get(srcid, &content);
................................................................................
   963    955                         diffFlags, pFileDir);
   964    956     }
   965    957     if( pFileDir ){
   966    958       int i;
   967    959       for(i=0; pFileDir[i].zName; i++){
   968    960         if( pFileDir[i].nUsed==0
   969    961          && strcmp(pFileDir[0].zName,".")!=0
   970         -       && !file_wd_isdir(g.argv[i+2])
          962  +       && !file_isdir(g.argv[i+2], ExtFILE)
   971    963         ){
   972    964           fossil_fatal("not found: '%s'", g.argv[i+2]);
   973    965         }
   974    966         fossil_free(pFileDir[i].zName);
   975    967       }
   976    968       fossil_free(pFileDir);
   977    969     }

Changes to src/doc.c.

   380    380   void mimetype_list_page(void){
   381    381     int i;
   382    382     mimetype_verify();
   383    383     style_header("Mimetype List");
   384    384     @ <p>The Fossil <a href="%R/help?cmd=/doc">/doc</a> page uses filename
   385    385     @ suffixes and the following table to guess at the appropriate mimetype
   386    386     @ for each document.</p>
   387         -  @ <table id='mimeTable' border=1 cellpadding=0 class='mimetypetable'>
          387  +  @ <table class='sortable mimetypetable' border=1 cellpadding=0 \
          388  +  @ data-column-types='tt' data-init-sort='1'>
   388    389     @ <thead>
   389    390     @ <tr><th>Suffix<th>Mimetype
   390    391     @ </thead>
   391    392     @ <tbody>
   392    393     for(i=0; i<count(aMime); i++){
   393    394       @ <tr><td>%h(aMime[i].zSuffix)<td>%h(aMime[i].zMimetype)</tr>
   394    395     }
   395    396     @ </tbody></table>
   396         -  output_table_sorting_javascript("mimeTable","tt",1);
          397  +  style_table_sorter();
   397    398     style_footer();
   398    399   }
   399    400   
   400    401   /*
   401    402   ** Check to see if the file in the pContent blob is "embedded HTML".  Return
   402    403   ** true if it is, and fill pTitle with the document title.
   403    404   **
................................................................................
   647    648           zDfltTitle = zName;
   648    649         }
   649    650       }else if( fossil_strcmp(zCheckin,"ckout")==0 ){
   650    651         /* Read from the local checkout */
   651    652         char *zFullpath;
   652    653         db_must_be_within_tree();
   653    654         zFullpath = mprintf("%s/%s", g.zLocalRoot, zName);
   654         -      if( file_isfile(zFullpath)
   655         -       && blob_read_from_file(&filebody, zFullpath)>0 ){
          655  +      if( file_isfile(zFullpath, RepoFILE)
          656  +       && blob_read_from_file(&filebody, zFullpath, RepoFILE)>0 ){
   656    657           rid = 1;  /* Fake RID just to get the loop to end */
   657    658         }
   658    659         fossil_free(zFullpath);
   659    660       }else{
   660    661         vid = name_to_typed_rid(zCheckin, "ci");
   661    662         rid = doc_load_content(vid, zName, &filebody);
   662    663       }

Changes to src/event.c.

   341    341   
   342    342   /*
   343    343   ** WEBPAGE: technoteedit
   344    344   ** WEBPAGE: eventedit
   345    345   **
   346    346   ** Revise or create a technical note (formerly called an "event").
   347    347   **
   348         -** Parameters:
          348  +** Required query parameter:
   349    349   **
   350         -**    name=ID           Hex hash ID of the tech-note. If omitted, a new
          350  +**    name=ID           Hex hash ID of the technote. If omitted, a new
   351    351   **                      tech-note is created.
          352  +**
          353  +** POST parameters from the "Cancel", "Preview", or "Submit" buttons:
          354  +**
          355  +**    w=TEXT            Complete text of the technote.
          356  +**    t=TEXT            Time of the technote on the timeline (ISO 8601)
          357  +**    c=TEXT            Timeline comment
          358  +**    g=TEXT            Tags associated with this technote
          359  +**    mimetype=TEXT     Mimetype for w= text
          360  +**    newclr            Use a background color
          361  +**    clr=TEXT          Background color to use if newclr
   352    362   */
   353    363   void eventedit_page(void){
   354    364     char *zTag;
   355    365     int rid = 0;
   356    366     Blob event;
   357    367     const char *zId;
   358    368     int n;
   359    369     const char *z;
   360         -  char *zBody = (char*)P("w");
   361         -  char *zETime = (char*)P("t");
   362         -  const char *zComment = P("c");
   363         -  const char *zTags = P("g");
   364         -  const char *zClr;
   365         -  const char *zMimetype = P("mimetype");
          370  +  char *zBody = (char*)P("w");             /* Text of the technote */
          371  +  char *zETime = (char*)P("t");            /* Date this technote appears */
          372  +  const char *zComment = P("c");           /* Timeline comment */
          373  +  const char *zTags = P("g");              /* Tags added to this technote */
          374  +  const char *zClrFlag = "";               /* "checked" for bg color */
          375  +  const char *zClr;                        /* Name of the background color */
          376  +  const char *zMimetype = P("mimetype");   /* Mimetype of zBody */
   366    377     int isNew = 0;
   367    378   
   368    379     if( zBody ){
   369    380       zBody = mprintf("%s", zBody);
   370    381     }
   371    382     login_check_credentials();
   372    383     zId = P("name");
................................................................................
   405    416     /* Figure out the color */
   406    417     if( rid ){
   407    418       zClr = db_text("", "SELECT bgcolor FROM event WHERE objid=%d", rid);
   408    419     }else{
   409    420       zClr = "";
   410    421       isNew = 1;
   411    422     }
   412         -  zClr = PD("clr",zClr);
   413         -  if( fossil_strcmp(zClr,"##")==0 ) zClr = PD("cclr","");
   414         -
          423  +  if( P("newclr") ){
          424  +    zClr = PD("clr",zClr);
          425  +    if( zClr[0] ) zClrFlag = " checked";
          426  +  }
   415    427   
   416    428     /* If editing an existing event, extract the key fields to use as
   417    429     ** a starting point for the edit.
   418    430     */
   419    431     if( rid
   420    432      && (zBody==0 || zETime==0 || zComment==0 || zTags==0 || zMimetype==0)
   421    433     ){
................................................................................
   440    452         );
   441    453       }
   442    454     }
   443    455     zETime = db_text(0, "SELECT coalesce(datetime(%Q),datetime('now'))", zETime);
   444    456     if( P("submit")!=0 && (zBody!=0 && zComment!=0) ){
   445    457       login_verify_csrf_secret();
   446    458       if ( !event_commit_common(rid, zId, zBody, zETime,
   447         -                              zMimetype, zComment, zTags, zClr) ){
          459  +                              zMimetype, zComment, zTags,
          460  +                              zClrFlag[0] ? zClr : 0) ){
   448    461         style_header("Error");
   449    462         @ Internal error:  Fossil tried to make an invalid artifact for
   450    463         @ the edited technote.
   451    464         style_footer();
   452    465         return;
   453    466       }
   454         -    cgi_redirectf("technote?name=%T", zId);
          467  +    cgi_redirectf("%R/technote?name=%T", zId);
   455    468     }
   456    469     if( P("cancel")!=0 ){
   457         -    cgi_redirectf("technote?name=%T", zId);
          470  +    cgi_redirectf("%R/technote?name=%T", zId);
   458    471       return;
   459    472     }
   460    473     if( zBody==0 ){
   461    474       zBody = mprintf("Insert new content here...");
   462    475     }
   463    476     if( isNew ){
   464    477       style_header("New Tech-note %S", zId);
................................................................................
   466    479       style_header("Edit Tech-note %S", zId);
   467    480     }
   468    481     if( P("preview")!=0 ){
   469    482       Blob com;
   470    483       @ <p><b>Timeline comment preview:</b></p>
   471    484       @ <blockquote>
   472    485       @ <table border="0">
   473         -    if( zClr && zClr[0] ){
          486  +    if( zClrFlag[0] && zClr && zClr[0] ){
   474    487         @ <tr><td style="background-color: %h(zClr);">
   475    488       }else{
   476    489         @ <tr><td>
   477    490       }
   478    491       blob_zero(&com);
   479    492       blob_append(&com, zComment, -1);
   480    493       wiki_convert(&com, 0, WIKI_INLINE|WIKI_NOBADLINKS);
................................................................................
   507    520     @ <td valign="top">
   508    521     @ <textarea name="c" class="technoteedit" cols="80"
   509    522     @  rows="3" wrap="virtual">%h(zComment)</textarea>
   510    523     @ </td></tr>
   511    524   
   512    525     @ <tr><th align="right" valign="top">Timeline Background Color:</th>
   513    526     @ <td valign="top">
   514         -  render_color_chooser(0, zClr, 0, "clr", "cclr");
          527  +  @ <input type='checkbox' name='newclr'%s(zClrFlag) />
          528  +  @ Use custom color: \
          529  +  @ <input type='color' name='clr' value='%s(zClr[0]?zClr:"#c0f0ff")'>
   515    530     @ </td></tr>
   516    531   
   517    532     @ <tr><th align="right" valign="top">Tags:</th>
   518    533     @ <td valign="top">
   519    534     @   <input type="text" name="g" size="40" value="%h(zTags)" />
   520    535     @ </td></tr>
   521    536   
................................................................................
   527    542     @ <tr><th align="right" valign="top">Page&nbsp;Content:</th>
   528    543     @ <td valign="top">
   529    544     @ <textarea name="w" class="technoteedit" cols="80"
   530    545     @  rows="%d(n)" wrap="virtual">%h(zBody)</textarea>
   531    546     @ </td></tr>
   532    547   
   533    548     @ <tr><td colspan="2">
   534         -  @ <input type="submit" name="preview" value="Preview Your Changes" />
   535         -  @ <input type="submit" name="submit" value="Apply These Changes" />
   536    549     @ <input type="submit" name="cancel" value="Cancel" />
          550  +  @ <input type="submit" name="preview" value="Preview" />
          551  +  if( P("preview") ){
          552  +    @ <input type="submit" name="submit" value="Submit" />
          553  +  }
   537    554     @ </td></tr></table>
   538    555     @ </div></form>
   539    556     style_footer();
   540    557   }
   541    558   
   542    559   /*
   543    560   ** Add a new tech note to the repository.  The timestamp is

Changes to src/export.c.

    33     33   ** struct mark_t
    34     34   **   holds information for translating between git commits
    35     35   **   and fossil commits.
    36     36   **   -git_name: This is the mark name that identifies the commit to git.
    37     37   **              It will always begin with a ':'.
    38     38   **   -rid: The unique object ID that identifies this commit within the
    39     39   **         repository database.
    40         -**   -uuid: The SHA-1 of artifact corresponding to rid.
           40  +**   -uuid: The SHA-1/SHA-3 of artifact corresponding to rid.
    41     41   */
    42     42   struct mark_t{
    43     43     char *name;
    44     44     int rid;
    45         -  char uuid[41];
           45  +  char uuid[65];
    46     46   };
    47     47   #endif
    48     48   
    49     49   /*
    50     50   ** Output a "committer" record for the given user.
    51     51   ** NOTE: the given user name may be an email itself.
    52     52   */
................................................................................
   338    338       }
   339    339       return create_mark(mark->rid, mark, &mid);
   340    340     }else{
   341    341       mark->name = fossil_strdup(cur_tok);
   342    342     }
   343    343   
   344    344     cur_tok = strtok(NULL, "\n");
   345         -  if( !cur_tok || strlen(cur_tok)!=40 ){
          345  +  if( !cur_tok || (strlen(cur_tok)!=40 && strlen(cur_tok)!=64) ){
   346    346       free(mark->name);
   347         -    fossil_trace("Invalid SHA-1 in marks file: %s\n", cur_tok);
          347  +    fossil_trace("Invalid SHA-1/SHA-3 in marks file: %s\n", cur_tok);
   348    348       return -1;
   349    349     }else{
   350    350       sqlite3_snprintf(sizeof(mark->uuid), mark->uuid, "%s", cur_tok);
   351    351     }
   352    352   
   353    353     /* make sure that rid corresponds to UUID */
   354    354     if( fast_uuid_to_rid(mark->uuid)!=mark->rid ){
   355    355       free(mark->name);
   356         -    fossil_trace("Non-existent SHA-1 in marks file: %s\n", mark->uuid);
          356  +    fossil_trace("Non-existent SHA-1/SHA-3 in marks file: %s\n", mark->uuid);
   357    357       return -1;
   358    358     }
   359    359   
   360    360     /* insert a cross-ref into the 'xmark' table */
   361    361     insert_commit_xref(mark->rid, mark->name, mark->uuid);
   362    362     return 0;
   363    363   }

Changes to src/file.c.

     1      1   /*
     2      2   ** Copyright (c) 2006 D. Richard Hipp
     3      3   **
     4      4   ** This program is free software; you can redistribute it and/or
     5      5   ** modify it under the terms of the Simplified BSD License (also
     6      6   ** known as the "2-Clause License" or "FreeBSD License".)
     7         -
            7  +**
     8      8   ** This program is distributed in the hope that it will be useful,
     9      9   ** but without any warranty; without even the implied warranty of
    10     10   ** merchantability or fitness for a particular purpose.
    11     11   **
    12     12   ** Author contact information:
    13     13   **   drh@hwaci.com
    14     14   **   http://www.hwaci.com/drh/
    15     15   **
    16     16   *******************************************************************************
    17     17   **
    18     18   ** File utilities.
    19         -**
    20         -** Functions named file_* are generic functions that always follow symlinks.
    21         -**
    22         -** Functions named file_wd_* are to be used for files inside working
    23         -** directories. They follow symlinks depending on 'allow-symlinks' setting.
    24     19   */
    25     20   #include "config.h"
    26     21   #include <sys/types.h>
    27     22   #include <sys/stat.h>
    28     23   #include <unistd.h>
    29     24   #include <stdio.h>
    30     25   #include <string.h>
................................................................................
    40     35   # include <sys/utime.h>
    41     36   #else
    42     37   # include <sys/time.h>
    43     38   #endif
    44     39   
    45     40   #if INTERFACE
    46     41   
           42  +/* Many APIs take a eFType argument which must be one of ExtFILE, RepoFILE,
           43  +** or SymFILE.
           44  +**
           45  +** The difference is in the handling of symbolic links.  RepoFILE should be
           46  +** used for files that are under management by a Fossil repository.  ExtFILE
           47  +** should be used for files that are not under management.  SymFILE is for
           48  +** a few special cases such as the "fossil test-tarball" command when we never
           49  +** want to follow symlinks.
           50  +**
           51  +** If RepoFILE is used and if the allow-symlinks setting is true and if
           52  +** the object is a symbolic link, then the object is treated like an ordinary
           53  +** file whose content is name of the object to which the symbolic link
           54  +** points.
           55  +**
           56  +** If ExtFILE is used or allow-symlinks is false, then operations on a
           57  +** symbolic link are the same as operations on the object to which the
           58  +** symbolic link points.
           59  +**
           60  +** SymFILE is like RepoFILE except that it always uses the target filename of
           61  +** a symbolic link as the content, instead of the content of the object
           62  +** that the symlink points to.  SymFILE acts as if allow-symlinks is always ON.
           63  +*/
           64  +#define ExtFILE    0  /* Always follow symlinks */
           65  +#define RepoFILE   1  /* Follow symlinks if and only if allow-symlinks is OFF */
           66  +#define SymFILE    2  /* Never follow symlinks */
           67  +
    47     68   #include <dirent.h>
    48     69   #if defined(_WIN32)
    49     70   # define DIR _WDIR
    50     71   # define dirent _wdirent
    51     72   # define opendir _wopendir
    52     73   # define readdir _wreaddir
    53     74   # define closedir _wclosedir
    54     75   #endif /* _WIN32 */
    55     76   
    56     77   #if defined(_WIN32) && (defined(__MSVCRT__) || defined(_MSC_VER))
           78  +/*
           79  +** File status information for windows systems.
           80  +*/
    57     81   struct fossilStat {
    58     82       i64 st_size;
    59     83       i64 st_mtime;
    60     84       int st_mode;
    61     85   };
    62     86   #endif
    63     87   
................................................................................
    66     90   #else
    67     91   # define fossil_isdirsep(a)    ((a) == '/')
    68     92   #endif
    69     93   
    70     94   #endif /* INTERFACE */
    71     95   
    72     96   #if !defined(_WIN32) || !(defined(__MSVCRT__) || defined(_MSC_VER))
           97  +/*
           98  +** File status information for unix systems
           99  +*/
    73    100   # define fossilStat stat
    74    101   #endif
    75    102   
    76    103   /*
    77    104   ** On Windows S_ISLNK always returns FALSE.
    78    105   */
    79    106   #if !defined(S_ISLNK)
    80    107   # define S_ISLNK(x) (0)
    81    108   #endif
    82         -static int fileStatValid = 0;
    83         -static struct fossilStat fileStat;
    84    109   
    85    110   /*
    86         -** Fill stat buf with information received from stat() or lstat().
    87         -** lstat() is called on Unix if isWd is TRUE and allow-symlinks setting is on.
          111  +** Local state information for the file status routines
          112  +*/
          113  +static struct {
          114  +  struct fossilStat fileStat;  /* File status from last fossil_stat() */
          115  +  int fileStatValid;           /* True if fileStat is valid */
          116  +} fx;
          117  +
          118  +/*
          119  +** Fill *buf with information about zFilename.
    88    120   **
          121  +** If zFilename refers to a symbolic link:
          122  +**
          123  +**  (A) If allow-symlinks is on and eFType is RepoFILE, then fill
          124  +**      *buf with information about the symbolic link itself.
          125  +**
          126  +**  (B) If allow-symlinks is off or eFType is ExtFILE, then fill
          127  +**      *buf with information about the object that the symbolic link
          128  +**      points to.
    89    129   */
    90    130   static int fossil_stat(
    91    131     const char *zFilename,  /* name of file or directory to inspect. */
    92    132     struct fossilStat *buf, /* pointer to buffer where info should go. */
    93         -  int isWd                /* non-zero to consider look at symlink itself. */
          133  +  int eFType              /* Look at symlink itself if RepoFILE and enabled. */
    94    134   ){
    95    135     int rc;
    96    136     void *zMbcs = fossil_utf8_to_path(zFilename, 0);
    97    137   #if !defined(_WIN32)
    98         -  if( isWd && db_allow_symlinks() ){
          138  +  if( eFType>=RepoFILE && (eFType==SymFILE || db_allow_symlinks()) ){
    99    139       rc = lstat(zMbcs, buf);
   100    140     }else{
   101    141       rc = stat(zMbcs, buf);
   102    142     }
   103    143   #else
   104         -  rc = win32_stat(zMbcs, buf, isWd);
          144  +  rc = win32_stat(zMbcs, buf, eFType);
   105    145   #endif
   106    146     fossil_path_free(zMbcs);
   107    147     return rc;
   108    148   }
   109    149   
   110    150   /*
   111         -** Clears the fileStat variable and its associated validity flag.
          151  +** Clears the fx.fileStat variable and its associated validity flag.
   112    152   */
   113    153   static void resetStat(){
   114         -  fileStatValid = 0;
   115         -  memset(&fileStat, 0, sizeof(struct fossilStat));
          154  +  fx.fileStatValid = 0;
          155  +  memset(&fx.fileStat, 0, sizeof(struct fossilStat));
   116    156   }
   117    157   
   118    158   /*
   119         -** Fill in the fileStat variable for the file named zFilename.
   120         -** If zFilename==0, then use the previous value of fileStat if
          159  +** Fill in the fx.fileStat variable for the file named zFilename.
          160  +** If zFilename==0, then use the previous value of fx.fileStat if
   121    161   ** there is a previous value.
   122    162   **
   123         -** If isWd is TRUE, do lstat() instead of stat() if allow-symlinks is on.
   124         -**
   125    163   ** Return the number of errors.  No error messages are generated.
   126    164   */
   127         -static int getStat(const char *zFilename, int isWd){
          165  +static int getStat(const char *zFilename, int eFType){
   128    166     int rc = 0;
   129    167     if( zFilename==0 ){
   130         -    if( fileStatValid==0 ) rc = 1;
          168  +    if( fx.fileStatValid==0 ) rc = 1;
   131    169     }else{
   132         -    if( fossil_stat(zFilename, &fileStat, isWd)!=0 ){
   133         -      fileStatValid = 0;
          170  +    if( fossil_stat(zFilename, &fx.fileStat, eFType)!=0 ){
          171  +      fx.fileStatValid = 0;
   134    172         rc = 1;
   135    173       }else{
   136         -      fileStatValid = 1;
          174  +      fx.fileStatValid = 1;
   137    175         rc = 0;
   138    176       }
   139    177     }
   140    178     return rc;
   141    179   }
   142    180   
   143    181   /*
   144    182   ** Return the size of a file in bytes.  Return -1 if the file does not
   145    183   ** exist.  If zFilename is NULL, return the size of the most recently
   146    184   ** stat-ed file.
   147    185   */
   148         -i64 file_size(const char *zFilename){
   149         -  return getStat(zFilename, 0) ? -1 : fileStat.st_size;
   150         -}
   151         -
   152         -/*
   153         -** Same as file_size(), but takes into account symlinks.
   154         -*/
   155         -i64 file_wd_size(const char *zFilename){
   156         -  return getStat(zFilename, 1) ? -1 : fileStat.st_size;
          186  +i64 file_size(const char *zFilename, int eFType){
          187  +  return getStat(zFilename, eFType) ? -1 : fx.fileStat.st_size;
   157    188   }
   158    189   
   159    190   /*
   160    191   ** Return the modification time for a file.  Return -1 if the file
   161    192   ** does not exist.  If zFilename is NULL return the size of the most
   162    193   ** recently stat-ed file.
   163    194   */
   164         -i64 file_mtime(const char *zFilename){
   165         -  return getStat(zFilename, 0) ? -1 : fileStat.st_mtime;
   166         -}
   167         -
   168         -/*
   169         -** Same as file_mtime(), but takes into account symlinks.
   170         -*/
   171         -i64 file_wd_mtime(const char *zFilename){
   172         -  return getStat(zFilename, 1) ? -1 : fileStat.st_mtime;
          195  +i64 file_mtime(const char *zFilename, int eFType){
          196  +  return getStat(zFilename, eFType) ? -1 : fx.fileStat.st_mtime;
   173    197   }
   174    198   
   175    199   /*
   176    200   ** Return the mode bits for a file.  Return -1 if the file does not
   177    201   ** exist.  If zFilename is NULL return the size of the most recently
   178    202   ** stat-ed file.
   179    203   */
   180         -int file_mode(const char *zFilename){
   181         -  return getStat(zFilename, 0) ? -1 : fileStat.st_mode;
          204  +int file_mode(const char *zFilename, int eFType){
          205  +  return getStat(zFilename, eFType) ? -1 : fx.fileStat.st_mode;
   182    206   }
   183    207   
   184    208   /*
   185         -** Same as file_mode(), but takes into account symlinks.
          209  +** Return TRUE if either of the following are true:
          210  +**
          211  +**   (1) zFilename is an ordinary file
          212  +**
          213  +**   (2) allow_symlinks is on and zFilename is a symbolic link to
          214  +**       a file, directory, or other object
   186    215   */
   187         -int file_wd_mode(const char *zFilename){
   188         -  return getStat(zFilename, 1) ? -1 : fileStat.st_mode;
   189         -}
   190         -
   191         -/*
   192         -** Return TRUE if the named file is an ordinary file or symlink
   193         -** and symlinks are allowed.
   194         -** Return false for directories, devices, fifos, etc.
   195         -*/
   196         -int file_wd_isfile_or_link(const char *zFilename){
   197         -  return getStat(zFilename, 1) ? 0 : S_ISREG(fileStat.st_mode) ||
   198         -                                     S_ISLNK(fileStat.st_mode);
          216  +int file_isfile_or_link(const char *zFilename){
          217  +  if( getStat(zFilename, RepoFILE) ){
          218  +    return 0;  /* stat() failed.  Return false. */
          219  +  }
          220  +  return S_ISREG(fx.fileStat.st_mode) || S_ISLNK(fx.fileStat.st_mode);
   199    221   }
   200    222   
   201    223   /*
   202    224   ** Return TRUE if the named file is an ordinary file.  Return false
   203    225   ** for directories, devices, fifos, symlinks, etc.
   204    226   */
   205         -int file_isfile(const char *zFilename){
   206         -  return getStat(zFilename, 0) ? 0 : S_ISREG(fileStat.st_mode);
          227  +int file_isfile(const char *zFilename, int eFType){
          228  +  return getStat(zFilename, eFType) ? 0 : S_ISREG(fx.fileStat.st_mode);
   207    229   }
   208    230   
   209    231   /*
   210         -** Same as file_isfile(), but takes into account symlinks.
   211         -*/
   212         -int file_wd_isfile(const char *zFilename){
   213         -  return getStat(zFilename, 1) ? 0 : S_ISREG(fileStat.st_mode);
   214         -}
   215         -
   216         -/*
   217         -** Create symlink to file on Unix, or plain-text file with
   218         -** symlink target if "allow-symlinks" is off or we're on Windows.
          232  +** Create a symbolic link named zLinkFile that points to zTargetFile.
   219    233   **
   220         -** Arguments: target file (symlink will point to it), link file
          234  +** If allow-symlinks is off, create an ordinary file named zLinkFile
          235  +** with the name of zTargetFile as its content.
   221    236   **/
   222    237   void symlink_create(const char *zTargetFile, const char *zLinkFile){
   223    238   #if !defined(_WIN32)
   224    239     if( db_allow_symlinks() ){
   225    240       int i, nName;
   226    241       char *zName, zBuf[1000];
   227    242   
................................................................................
   232    247         zName = zBuf;
   233    248         memcpy(zName, zLinkFile, nName+1);
   234    249       }
   235    250       nName = file_simplify_name(zName, nName, 0);
   236    251       for(i=1; i<nName; i++){
   237    252         if( zName[i]=='/' ){
   238    253           zName[i] = 0;
   239         -          if( file_mkdir(zName, 1) ){
   240         -            fossil_fatal_recursive("unable to create directory %s", zName);
   241         -            return;
   242         -          }
          254  +        if( file_mkdir(zName, ExtFILE, 1) ){
          255  +          fossil_fatal_recursive("unable to create directory %s", zName);
          256  +          return;
          257  +        }
   243    258           zName[i] = '/';
   244    259         }
   245    260       }
   246    261       if( symlink(zTargetFile, zName)!=0 ){
   247    262         fossil_fatal_recursive("unable to create symlink \"%s\"", zName);
   248    263       }
   249    264       if( zName!=zBuf ) free(zName);
................................................................................
   268    283   }
   269    284   
   270    285   /*
   271    286   ** Return file permissions (normal, executable, or symlink):
   272    287   **   - PERM_EXE on Unix if file is executable;
   273    288   **   - PERM_LNK on Unix if file is symlink and allow-symlinks option is on;
   274    289   **   - PERM_REG for all other cases (regular file, directory, fifo, etc).
          290  +**
          291  +** If eFType is ExtFile then symbolic links are followed and so this
          292  +** routine can only return PERM_EXE and PERM_REG.
          293  +**
          294  +** On windows, this routine returns only PERM_REG.
   275    295   */
   276         -int file_wd_perm(const char *zFilename){
          296  +int file_perm(const char *zFilename, int eFType){
   277    297   #if !defined(_WIN32)
   278         -  if( !getStat(zFilename, 1) ){
   279         -     if( S_ISREG(fileStat.st_mode) && ((S_IXUSR)&fileStat.st_mode)!=0 )
          298  +  if( !getStat(zFilename, RepoFILE) ){
          299  +     if( S_ISREG(fx.fileStat.st_mode) && ((S_IXUSR)&fx.fileStat.st_mode)!=0 )
   280    300         return PERM_EXE;
   281         -    else if( db_allow_symlinks() && S_ISLNK(fileStat.st_mode) )
          301  +    else if( db_allow_symlinks() && S_ISLNK(fx.fileStat.st_mode) )
   282    302         return PERM_LNK;
   283    303     }
   284    304   #endif
   285    305     return PERM_REG;
   286    306   }
   287    307   
   288    308   /*
   289    309   ** Return TRUE if the named file is an executable.  Return false
   290    310   ** for directories, devices, fifos, symlinks, etc.
   291    311   */
   292         -int file_wd_isexe(const char *zFilename){
   293         -  return file_wd_perm(zFilename)==PERM_EXE;
          312  +int file_isexe(const char *zFilename, int eFType){
          313  +  return file_perm(zFilename, eFType)==PERM_EXE;
   294    314   }
   295    315   
   296    316   /*
   297    317   ** Return TRUE if the named file is a symlink and symlinks are allowed.
   298    318   ** Return false for all other cases.
   299    319   **
          320  +** This routines RepoFILE - that zFilename is always a file under management.
          321  +**
   300    322   ** On Windows, always return False.
   301    323   */
   302         -int file_wd_islink(const char *zFilename){
   303         -  return file_wd_perm(zFilename)==PERM_LNK;
          324  +int file_islink(const char *zFilename){
          325  +  return file_perm(zFilename, RepoFILE)==PERM_LNK;
   304    326   }
   305    327   
   306    328   /*
   307    329   ** Return 1 if zFilename is a directory.  Return 0 if zFilename
   308    330   ** does not exist.  Return 2 if zFilename exists but is something
   309    331   ** other than a directory.
   310    332   */
   311         -int file_isdir(const char *zFilename){
   312         -  int rc;
   313         -
   314         -  if( zFilename ){
   315         -    char *zFN = mprintf("%s", zFilename);
   316         -    file_simplify_name(zFN, -1, 0);
   317         -    rc = getStat(zFN, 0);
   318         -    free(zFN);
   319         -  }else{
   320         -    rc = getStat(0, 0);
   321         -  }
   322         -  return rc ? 0 : (S_ISDIR(fileStat.st_mode) ? 1 : 2);
   323         -}
   324         -
   325         -/*
   326         -** Same as file_isdir(), but takes into account symlinks.  Return 1 if
   327         -** zFilename is a directory -OR- a symlink that points to a directory.
   328         -** Return 0 if zFilename does not exist.  Return 2 if zFilename exists
   329         -** but is something other than a directory.
   330         -*/
   331         -int file_wd_isdir(const char *zFilename){
          333  +int file_isdir(const char *zFilename, int eFType){
   332    334     int rc;
   333    335     char *zFN;
   334    336   
   335    337     zFN = mprintf("%s", zFilename);
   336    338     file_simplify_name(zFN, -1, 0);
   337         -  rc = getStat(zFN, 1);
          339  +  rc = getStat(zFN, eFType);
   338    340     if( rc ){
   339    341       rc = 0; /* It does not exist at all. */
   340         -  }else if( S_ISDIR(fileStat.st_mode) ){
          342  +  }else if( S_ISDIR(fx.fileStat.st_mode) ){
   341    343       rc = 1; /* It exists and is a real directory. */
   342         -  }else if( S_ISLNK(fileStat.st_mode) ){
   343         -    Blob content;
   344         -    blob_read_link(&content, zFN); /* It exists and is a link. */
   345         -    rc = file_wd_isdir(blob_str(&content)); /* Points to directory? */
   346         -    blob_reset(&content);
   347    344     }else{
   348    345       rc = 2; /* It exists and is something else. */
   349    346     }
   350    347     free(zFN);
   351    348     return rc;
   352    349   }
   353    350   
................................................................................
   396    393   ** Space to hold the new filename is obtained form mprintf() and should
   397    394   ** be freed by the caller.
   398    395   */
   399    396   char *file_newname(const char *zBase, const char *zSuffix, int relFlag){
   400    397     char *z = 0;
   401    398     int cnt = 0;
   402    399     z = mprintf("%s-%s", zBase, zSuffix);
   403         -  while( file_size(z)>=0 ){
          400  +  while( file_size(z, ExtFILE)>=0 ){
   404    401       fossil_free(z);
   405    402       z = mprintf("%s-%s-%d", zBase, zSuffix, cnt++);
   406    403     }
   407    404     if( relFlag ){
   408    405       Blob x;
   409    406       file_relative_name(z, &x, 0);
   410    407       fossil_free(z);
................................................................................
   472    469   */
   473    470   void file_copy(const char *zFrom, const char *zTo){
   474    471     FILE *in, *out;
   475    472     int got;
   476    473     char zBuf[8192];
   477    474     in = fossil_fopen(zFrom, "rb");
   478    475     if( in==0 ) fossil_fatal("cannot open \"%s\" for reading", zFrom);
   479         -  file_mkfolder(zTo, 0, 0);
          476  +  file_mkfolder(zTo, ExtFILE, 0, 0);
   480    477     out = fossil_fopen(zTo, "wb");
   481    478     if( out==0 ) fossil_fatal("cannot open \"%s\" for writing", zTo);
   482    479     while( (got=fread(zBuf, 1, sizeof(zBuf), in))>0 ){
   483    480       fwrite(zBuf, 1, got, out);
   484    481     }
   485    482     fclose(in);
   486    483     fclose(out);
................................................................................
   501    498     }
   502    499     file_copy(g.argv[2], g.argv[3]);
   503    500   }
   504    501   
   505    502   /*
   506    503   ** Set or clear the execute bit on a file.  Return true if a change
   507    504   ** occurred and false if this routine is a no-op.
          505  +**
          506  +** This routine assumes RepoFILE as the eFType.  In other words, if
          507  +** zFilename is a symbolic link, it is the object that zFilename points
          508  +** to that is modified.
   508    509   */
   509         -int file_wd_setexe(const char *zFilename, int onoff){
          510  +int file_setexe(const char *zFilename, int onoff){
   510    511     int rc = 0;
   511    512   #if !defined(_WIN32)
   512    513     struct stat buf;
   513         -  if( fossil_stat(zFilename, &buf, 1)!=0 || S_ISLNK(buf.st_mode) ) return 0;
          514  +  if( fossil_stat(zFilename, &buf, RepoFILE)!=0 || S_ISLNK(buf.st_mode) ){
          515  +    return 0;
          516  +  }
   514    517     if( onoff ){
   515    518       int targetMode = (buf.st_mode & 0444)>>2;
   516    519       if( (buf.st_mode & 0100)==0 ){
   517    520         chmod(zFilename, buf.st_mode | targetMode);
   518    521         rc = 1;
   519    522       }
   520    523     }else{
................................................................................
   563    566     if( g.argc!=4 ){
   564    567       usage("FILENAME DATE/TIME");
   565    568     }
   566    569     db_open_or_attach(":memory:", "mem");
   567    570     iMTime = db_int64(0, "SELECT strftime('%%s',%Q)", g.argv[3]);
   568    571     zFile = g.argv[2];
   569    572     file_set_mtime(zFile, iMTime);
   570         -  iMTime = file_wd_mtime(zFile);
          573  +  iMTime = file_mtime(zFile, RepoFILE);
   571    574     zDate = db_text(0, "SELECT datetime(%lld, 'unixepoch')", iMTime);
   572    575     fossil_print("Set mtime of \"%s\" to %s (%lld)\n", zFile, zDate, iMTime);
   573    576   }
   574    577   
   575    578   /*
   576    579   ** Delete a file.
          580  +**
          581  +** If zFilename is a symbolic link, then it is the link itself that is
          582  +** removed, not the object that zFilename points to.
   577    583   **
   578    584   ** Returns zero upon success.
   579    585   */
   580    586   int file_delete(const char *zFilename){
   581    587     int rc;
   582    588   #ifdef _WIN32
   583    589     wchar_t *z = fossil_utf8_to_path(zFilename, 0);
................................................................................
   587    593     rc = unlink(zFilename);
   588    594   #endif
   589    595     fossil_path_free(z);
   590    596     return rc;
   591    597   }
   592    598   
   593    599   /*
   594         -** Create the directory named in the argument, if it does not already
   595         -** exist.  If forceFlag is 1, delete any prior non-directory object
          600  +** Create a directory called zName, if it does not already exist.
          601  +** If forceFlag is 1, delete any prior non-directory object
   596    602   ** with the same name.
   597    603   **
   598    604   ** Return the number of errors.
   599    605   */
   600         -int file_mkdir(const char *zName, int forceFlag){
   601         -  int rc = file_wd_isdir(zName);
          606  +int file_mkdir(const char *zName, int eFType, int forceFlag){
          607  +  int rc = file_isdir(zName, eFType);
   602    608     if( rc==2 ){
   603    609       if( !forceFlag ) return 1;
   604    610       file_delete(zName);
   605    611     }
   606    612     if( rc!=1 ){
   607    613   #if defined(_WIN32)
   608    614       wchar_t *zMbcs = fossil_utf8_to_path(zName, 1);
................................................................................
   620    626   /*
   621    627   ** Create the tree of directories in which zFilename belongs, if that sequence
   622    628   ** of directories does not already exist.
   623    629   **
   624    630   ** On success, return zero.  On error, return errorReturn if positive, otherwise
   625    631   ** print an error message and abort.
   626    632   */
   627         -int file_mkfolder(const char *zFilename, int forceFlag, int errorReturn){
          633  +int file_mkfolder(
          634  +  const char *zFilename,   /* Pathname showing directories to be created */
          635  +  int eFType,              /* Follow symlinks if ExtFILE */
          636  +  int forceFlag,           /* Delete non-directory objects in the way */
          637  +  int errorReturn          /* What to do when an error is seen */
          638  +){
   628    639     int nName, rc = 0;
   629    640     char *zName;
   630    641   
   631    642     nName = strlen(zFilename);
   632    643     zName = mprintf("%s", zFilename);
   633    644     nName = file_simplify_name(zName, nName, 0);
   634    645     while( nName>0 && zName[nName-1]!='/' ){ nName--; }
   635    646     if( nName ){
   636    647       zName[nName-1] = 0;
   637         -    if( file_wd_isdir(zName)!=1 ){
   638         -      rc = file_mkfolder(zName, forceFlag, errorReturn);
          648  +    if( file_isdir(zName, eFType)!=1 ){
          649  +      rc = file_mkfolder(zName, eFType, forceFlag, errorReturn);
   639    650         if( rc==0 ){
   640         -        if( file_mkdir(zName, forceFlag) && file_wd_isdir(zName)!=1 ){
          651  +        if( file_mkdir(zName, eFType, forceFlag)
          652  +         && file_isdir(zName, eFType)!=1
          653  +        ){
   641    654             if( errorReturn <= 0 ){
   642    655               fossil_fatal_recursive("unable to create directory %s", zName);
   643    656             }
   644    657             rc = errorReturn;
   645    658           }
   646    659         }
   647    660       }
................................................................................
   653    666   /*
   654    667   ** Removes the directory named in the argument, if it exists.  The directory
   655    668   ** must be empty and cannot be the current directory or the root directory.
   656    669   **
   657    670   ** Returns zero upon success.
   658    671   */
   659    672   int file_rmdir(const char *zName){
   660         -  int rc = file_wd_isdir(zName);
          673  +  int rc = file_isdir(zName, RepoFILE);
   661    674     if( rc==2 ) return 1; /* cannot remove normal file */
   662    675     if( rc==1 ){
   663    676   #if defined(_WIN32)
   664    677       wchar_t *zMbcs = fossil_utf8_to_path(zName, 1);
   665    678       rc = _wrmdir(zMbcs);
   666    679   #else
   667    680       char *zMbcs = fossil_utf8_to_path(zName, 1);
................................................................................
   958    971   /*
   959    972   ** Emits the effective or raw stat() information for the specified
   960    973   ** file or directory, optionally preserving the trailing slash and
   961    974   ** resetting the cached stat() information.
   962    975   */
   963    976   static void emitFileStat(
   964    977     const char *zPath,
   965         -  int raw,
   966    978     int slash,
   967    979     int reset
   968    980   ){
   969         -  char zBuf[100];
          981  +  char zBuf[200];
          982  +  char *z;
   970    983     Blob x;
          984  +  int rc;
          985  +  sqlite3_int64 iMtime;
          986  +  struct fossilStat testFileStat;
   971    987     memset(zBuf, 0, sizeof(zBuf));
   972    988     blob_zero(&x);
   973    989     file_canonical_name(zPath, &x, slash);
   974         -  fossil_print("%s[%s] -> [%s]\n", raw ? "RAW " : "", zPath, blob_buffer(&x));
          990  +  fossil_print("[%s] -> [%s]\n", zPath, blob_buffer(&x));
   975    991     blob_reset(&x);
   976         -  if( raw ){
   977         -    int rc;
   978         -    struct fossilStat testFileStat;
   979         -    memset(&testFileStat, 0, sizeof(struct fossilStat));
   980         -    rc = fossil_stat(zPath, &testFileStat, 0);
   981         -    fossil_print("  stat_rc      = %d\n", rc);
   982         -    sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", testFileStat.st_size);
   983         -    fossil_print("  stat_size    = %s\n", zBuf);
   984         -    sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", testFileStat.st_mtime);
   985         -    fossil_print("  stat_mtime   = %s\n", zBuf);
   986         -    fossil_print("  stat_mode    = %d\n", testFileStat.st_mode);
   987         -    memset(&testFileStat, 0, sizeof(struct fossilStat));
   988         -    rc = fossil_stat(zPath, &testFileStat, 1);
   989         -    fossil_print("  l_stat_rc    = %d\n", rc);
   990         -    sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", testFileStat.st_size);
   991         -    fossil_print("  l_stat_size  = %s\n", zBuf);
   992         -    sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", testFileStat.st_mtime);
   993         -    fossil_print("  l_stat_mtime = %s\n", zBuf);
   994         -    fossil_print("  l_stat_mode  = %d\n", testFileStat.st_mode);
   995         -  }else{
   996         -    if( reset ) resetStat();
   997         -    sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_wd_size(zPath));
   998         -    fossil_print("  file_size           = %s\n", zBuf);
   999         -    sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_wd_mtime(zPath));
  1000         -    fossil_print("  file_mtime          = %s\n", zBuf);
  1001         -    fossil_print("  file_mode           = %d\n", file_wd_mode(zPath));
  1002         -    fossil_print("  file_isfile         = %d\n", file_wd_isfile(zPath));
  1003         -    fossil_print("  file_isfile_or_link = %d\n", file_wd_isfile_or_link(zPath));
  1004         -    fossil_print("  file_islink         = %d\n", file_wd_islink(zPath));
  1005         -    fossil_print("  file_isexe          = %d\n", file_wd_isexe(zPath));
  1006         -    fossil_print("  file_isdir          = %d\n", file_wd_isdir(zPath));
  1007         -    if( reset ) resetStat();
  1008         -  }
          992  +  memset(&testFileStat, 0, sizeof(struct fossilStat));
          993  +  rc = fossil_stat(zPath, &testFileStat, 0);
          994  +  fossil_print("  stat_rc                = %d\n", rc);
          995  +  sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", testFileStat.st_size);
          996  +  fossil_print("  stat_size              = %s\n", zBuf);
          997  +  z = db_text(0, "SELECT datetime(%lld, 'unixepoch')", testFileStat.st_mtime);
          998  +  sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld (%s)", testFileStat.st_mtime, z);
          999  +  fossil_free(z);
         1000  +  fossil_print("  stat_mtime             = %s\n", zBuf);
         1001  +  fossil_print("  stat_mode              = 0%o\n", testFileStat.st_mode);
         1002  +  memset(&testFileStat, 0, sizeof(struct fossilStat));
         1003  +  rc = fossil_stat(zPath, &testFileStat, 1);
         1004  +  fossil_print("  l_stat_rc              = %d\n", rc);
         1005  +  sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", testFileStat.st_size);
         1006  +  fossil_print("  l_stat_size            = %s\n", zBuf);
         1007  +  z = db_text(0, "SELECT datetime(%lld, 'unixepoch')", testFileStat.st_mtime);
         1008  +  sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld (%s)", testFileStat.st_mtime, z);
         1009  +  fossil_free(z);
         1010  +  fossil_print("  l_stat_mtime           = %s\n", zBuf);
         1011  +  fossil_print("  l_stat_mode            = 0%o\n", testFileStat.st_mode);
         1012  +  if( reset ) resetStat();
         1013  +  sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_size(zPath,ExtFILE));
         1014  +  fossil_print("  file_size(ExtFILE)     = %s\n", zBuf);
         1015  +  iMtime = file_mtime(zPath, ExtFILE);
         1016  +  z = db_text(0, "SELECT datetime(%lld, 'unixepoch')", iMtime);
         1017  +  sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld (%s)", iMtime, z);
         1018  +  fossil_free(z);
         1019  +  fossil_print("  file_mtime(ExtFILE)    = %s\n", zBuf);
         1020  +  fossil_print("  file_mode(ExtFILE)     = 0%o\n", file_mode(zPath,ExtFILE));
         1021  +  fossil_print("  file_isfile(ExtFILE)   = %d\n", file_isfile(zPath,ExtFILE));
         1022  +  fossil_print("  file_isdir(ExtFILE)    = %d\n", file_isdir(zPath,ExtFILE));
         1023  +  if( reset ) resetStat();
         1024  +  sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_size(zPath,RepoFILE));
         1025  +  fossil_print("  file_size(RepoFILE)    = %s\n", zBuf);
         1026  +  iMtime = file_mtime(zPath,RepoFILE);
         1027  +  z = db_text(0, "SELECT datetime(%lld, 'unixepoch')", iMtime);
         1028  +  sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld (%s)", iMtime, z);
         1029  +  fossil_free(z);
         1030  +  fossil_print("  file_mtime(RepoFILE)   = %s\n", zBuf);
         1031  +  fossil_print("  file_mode(RepoFILE)    = 0%o\n", file_mode(zPath,RepoFILE));
         1032  +  fossil_print("  file_isfile(RepoFILE)  = %d\n", file_isfile(zPath,RepoFILE));
         1033  +  fossil_print("  file_isfile_or_link    = %d\n", file_isfile_or_link(zPath));
         1034  +  fossil_print("  file_islink            = %d\n", file_islink(zPath));
         1035  +  fossil_print("  file_isexe(RepoFILE)   = %d\n", file_isexe(zPath,RepoFILE));
         1036  +  fossil_print("  file_isdir(RepoFILE)   = %d\n", file_isdir(zPath,RepoFILE));
         1037  +  if( reset ) resetStat();
  1009   1038   }
  1010   1039   
  1011   1040   /*
  1012   1041   ** COMMAND: test-file-environment
  1013   1042   **
  1014   1043   ** Usage: %fossil test-file-environment FILENAME...
  1015   1044   **
  1016   1045   ** Display the effective file handling subsystem "settings" and then
  1017   1046   ** display file system information about the files specified, if any.
  1018   1047   **
  1019   1048   ** Options:
  1020   1049   **
  1021         -**     --open-config        Open the configuration database first.
  1022         -**     --slash              Trailing slashes, if any, are retained.
  1023         -**     --reset              Reset cached stat() info for each file.
         1050  +**     --allow-symlinks BOOLEAN     Temporarily turn allow-symlinks on/off
         1051  +**     --open-config                Open the configuration database first.
         1052  +**     --slash                      Trailing slashes, if any, are retained.
         1053  +**     --reset                      Reset cached stat() info for each file.
  1024   1054   */
  1025   1055   void cmd_test_file_environment(void){
  1026   1056     int i;
  1027   1057     int slashFlag = find_option("slash",0,0)!=0;
  1028   1058     int resetFlag = find_option("reset",0,0)!=0;
         1059  +  const char *zAllow = find_option("allow-symlinks",0,1);
  1029   1060     if( find_option("open-config", 0, 0)!=0 ){
  1030   1061       Th_OpenConfig(1);
  1031   1062     }
         1063  +  db_find_and_open_repository(OPEN_ANY_SCHEMA, 0);
  1032   1064     fossil_print("filenames_are_case_sensitive() = %d\n",
  1033   1065                  filenames_are_case_sensitive());
  1034   1066     fossil_print("db_allow_symlinks_by_default() = %d\n",
  1035   1067                  db_allow_symlinks_by_default());
         1068  +  if( zAllow ){
         1069  +    g.allowSymlinks = !is_false(zAllow);
         1070  +  }
  1036   1071     fossil_print("db_allow_symlinks() = %d\n", db_allow_symlinks());
  1037   1072     for(i=2; i<g.argc; i++){
  1038         -    emitFileStat(g.argv[i], 1, slashFlag, resetFlag);
  1039         -    emitFileStat(g.argv[i], 0, slashFlag, resetFlag);
         1073  +    emitFileStat(g.argv[i], slashFlag, resetFlag);
  1040   1074     }
  1041   1075   }
  1042   1076   
  1043   1077   /*
  1044   1078   ** COMMAND: test-canonical-name
  1045   1079   **
  1046   1080   ** Usage: %fossil test-canonical-name FILENAME...
................................................................................
  1055   1089     blob_zero(&x);
  1056   1090     for(i=2; i<g.argc; i++){
  1057   1091       char zBuf[100];
  1058   1092       const char *zName = g.argv[i];
  1059   1093       file_canonical_name(zName, &x, slashFlag);
  1060   1094       fossil_print("[%s] -> [%s]\n", zName, blob_buffer(&x));
  1061   1095       blob_reset(&x);
  1062         -    sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_wd_size(zName));
  1063         -    fossil_print("  file_size   = %s\n", zBuf);
  1064         -    sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_wd_mtime(zName));
  1065         -    fossil_print("  file_mtime  = %s\n", zBuf);
  1066         -    fossil_print("  file_isfile = %d\n", file_wd_isfile(zName));
  1067         -    fossil_print("  file_isfile_or_link = %d\n",file_wd_isfile_or_link(zName));
  1068         -    fossil_print("  file_islink = %d\n", file_wd_islink(zName));
  1069         -    fossil_print("  file_isexe  = %d\n", file_wd_isexe(zName));
  1070         -    fossil_print("  file_isdir  = %d\n", file_wd_isdir(zName));
         1096  +    sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_size(zName,RepoFILE));
         1097  +    fossil_print("  file_size           = %s\n", zBuf);
         1098  +    sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_mtime(zName,RepoFILE));
         1099  +    fossil_print("  file_mtime          = %s\n", zBuf);
         1100  +    fossil_print("  file_isfile         = %d\n", file_isfile(zName,RepoFILE));
         1101  +    fossil_print("  file_isfile_or_link = %d\n", file_isfile_or_link(zName));
         1102  +    fossil_print("  file_islink         = %d\n", file_islink(zName));
         1103  +    fossil_print("  file_isexe          = %d\n", file_isexe(zName,RepoFILE));
         1104  +    fossil_print("  file_isdir          = %d\n", file_isdir(zName,RepoFILE));
  1071   1105     }
  1072   1106   }
  1073   1107   
  1074   1108   /*
  1075   1109   ** Return TRUE if the given filename is canonical.
  1076   1110   **
  1077   1111   ** Canonical names are full pathnames using "/" not "\" and which
................................................................................
  1403   1437     azDirs[2] = fossil_getenv("TMP");
  1404   1438   #else
  1405   1439     azDirs[0] = fossil_getenv("TMPDIR");
  1406   1440   #endif
  1407   1441   
  1408   1442     for(i=0; i<count(azDirs); i++){
  1409   1443       if( azDirs[i]==0 ) continue;
  1410         -    if( !file_isdir(azDirs[i]) ) continue;
         1444  +    if( !file_isdir(azDirs[i], ExtFILE) ) continue;
  1411   1445       zDir = azDirs[i];
  1412   1446       break;
  1413   1447     }
  1414   1448   
  1415   1449     do{
  1416   1450       blob_zero(pBuf);
  1417   1451       if( cnt++>20 ) fossil_panic("cannot generate a temporary filename");
  1418   1452       sqlite3_randomness(15, zRand);
  1419   1453       for(i=0; i<15; i++){
  1420   1454         zRand[i] = (char)zChars[ ((unsigned char)zRand[i])%(sizeof(zChars)-1) ];
  1421   1455       }
  1422   1456       zRand[15] = 0;
  1423   1457       blob_appendf(pBuf, "%s/%s-%s.txt", zDir, zPrefix ? zPrefix : "", zRand);
  1424         -  }while( file_size(blob_str(pBuf))>=0 );
         1458  +  }while( file_size(blob_str(pBuf), ExtFILE)>=0 );
  1425   1459   
  1426   1460   #if defined(_WIN32)
  1427   1461     fossil_path_free((char *)azDirs[0]);
  1428   1462     fossil_path_free((char *)azDirs[1]);
  1429   1463     fossil_path_free((char *)azDirs[2]);
  1430   1464     /* Change all \ characters in the windows path into / so that they can
  1431   1465     ** be safely passed to a subcommand, such as by gdiff */
................................................................................
  1454   1488   }
  1455   1489   
  1456   1490   
  1457   1491   /*
  1458   1492   ** Return true if a file named zName exists and has identical content
  1459   1493   ** to the blob pContent.  If zName does not exist or if the content is
  1460   1494   ** different in any way, then return false.
         1495  +**
         1496  +** This routine assumes RepoFILE
  1461   1497   */
  1462   1498   int file_is_the_same(Blob *pContent, const char *zName){
  1463   1499     i64 iSize;
  1464   1500     int rc;
  1465   1501     Blob onDisk;
  1466   1502   
  1467         -  iSize = file_wd_size(zName);
         1503  +  iSize = file_size(zName, RepoFILE);
  1468   1504     if( iSize<0 ) return 0;
  1469   1505     if( iSize!=blob_size(pContent) ) return 0;
  1470         -  if( file_wd_islink(zName) ){
  1471         -    blob_read_link(&onDisk, zName);
  1472         -  }else{
  1473         -    blob_read_from_file(&onDisk, zName);
  1474         -  }
         1506  +  blob_read_from_file(&onDisk, zName, RepoFILE);
  1475   1507     rc = blob_compare(&onDisk, pContent);
  1476   1508     blob_reset(&onDisk);
  1477   1509     return rc==0;
  1478   1510   }
  1479   1511   
  1480   1512   /*
  1481   1513   ** Return the value of an environment variable as UTF8.
................................................................................
  1510   1542     /* fossil_free(zString); */
  1511   1543   #endif
  1512   1544     return rc;
  1513   1545   }
  1514   1546   
  1515   1547   /*
  1516   1548   ** Like fopen() but always takes a UTF8 argument.
         1549  +**
         1550  +** This function assumes ExtFILE. In other words, symbolic links
         1551  +** are always followed.
  1517   1552   */
  1518   1553   FILE *fossil_fopen(const char *zName, const char *zMode){
  1519   1554   #ifdef _WIN32
  1520   1555     wchar_t *uMode = fossil_utf8_to_unicode(zMode);
  1521   1556     wchar_t *uName = fossil_utf8_to_path(zName, 0);
  1522   1557     FILE *f = _wfopen(uName, uMode);
  1523   1558     fossil_path_free(uName);

Changes to src/finfo.c.

   278    278   **
   279    279   ** Show the change history for a single file.
   280    280   **
   281    281   ** Additional query parameters:
   282    282   **
   283    283   **    a=DATETIME Only show changes after DATETIME
   284    284   **    b=DATETIME Only show changes before DATETIME
          285  +**    m=HASH     Mark this particular file version
   285    286   **    n=NUM      Show the first NUM changes only
   286    287   **    brbg       Background color by branch name
   287    288   **    ubg        Background color by user name
   288    289   **    ci=UUID    Ancestors of a particular check-in
   289    290   **    orig=UUID  If both ci and orig are supplied, only show those
   290    291   **                 changes on a direct path from orig to ci.
   291    292   **    showid     Show RID values for debugging
................................................................................
   311    312     GraphContext *pGraph;
   312    313     int brBg = P("brbg")!=0;
   313    314     int uBg = P("ubg")!=0;
   314    315     int fDebug = atoi(PD("debug","0"));
   315    316     int fShowId = P("showid")!=0;
   316    317     Stmt qparent;
   317    318     int iTableId = timeline_tableid();
          319  +  int tmFlags = 0;            /* Viewing mode */
          320  +  const char *zStyle;         /* Viewing mode name */
          321  +  const char *zMark;          /* Mark this version of the file */
          322  +  int selRid = 0;             /* RID of the marked file version */
   318    323   
   319    324     login_check_credentials();
   320    325     if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
   321    326     style_header("File History");
   322    327     login_anonymous_available();
          328  +  tmFlags = timeline_ss_submenu();
          329  +  if( tmFlags & TIMELINE_COLUMNAR ){
          330  +    zStyle = "Columnar";
          331  +  }else if( tmFlags & TIMELINE_COMPACT ){
          332  +    zStyle = "Compact";
          333  +  }else if( tmFlags & TIMELINE_VERBOSE ){
          334  +    zStyle = "Verbose";
          335  +  }else{
          336  +    zStyle = "Modern";
          337  +  }
   323    338     url_initialize(&url, "finfo");
   324    339     if( brBg ) url_add_parameter(&url, "brbg", 0);
   325    340     if( uBg ) url_add_parameter(&url, "ubg", 0);
   326    341     baseCheckin = name_to_rid_www("ci");
   327    342     zPrevDate[0] = 0;
   328    343     zFilename = PD("name","");
          344  +  cookie_render();
   329    345     fnid = db_int(0, "SELECT fnid FROM filename WHERE name=%Q", zFilename);
   330    346     if( fnid==0 ){
   331    347       @ No such file: %h(zFilename)
   332    348       style_footer();
   333    349       return;
   334    350     }
   335    351     if( g.perm.Admin ){
................................................................................
   399    415       blob_append_sql(&sql, " LIMIT %d", n);
   400    416       url_add_parameter(&url, "n", P("n"));
   401    417     }
   402    418     db_prepare(&q, "%s", blob_sql_text(&sql));
   403    419     if( P("showsql")!=0 ){
   404    420       @ <p>SQL: %h(blob_str(&sql))</p>
   405    421     }
          422  +  zMark = P("m");
          423  +  if( zMark ){
          424  +    selRid = symbolic_name_to_rid(zMark, "*");
          425  +  }
   406    426     blob_reset(&sql);
   407    427     blob_zero(&title);
   408    428     if( baseCheckin ){
   409    429       char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", baseCheckin);
   410    430       char *zLink = href("%R/info/%!S", zUuid);
   411    431       if( origCheckin ){
   412    432         blob_appendf(&title, "Changes to file ");
................................................................................
   491    511         sqlite3_snprintf(sizeof(zPrevDate), zPrevDate, "%.10s", zDate);
   492    512         @ <tr><td>
   493    513         @   <div class="divider timelineDate">%s(zPrevDate)</div>
   494    514         @ </td><td></td><td></td></tr>
   495    515       }
   496    516       memcpy(zTime, &zDate[11], 5);
   497    517       zTime[5] = 0;
   498         -    @ <tr><td class="timelineTime">
   499         -    @ %z(href("%R/timeline?c=%t",zDate))%s(zTime)</a></td>
          518  +    if( frid==selRid ){
          519  +      @ <tr class='timelineSelected'>
          520  +    }else{
          521  +      @ <tr>
          522  +    }
          523  +    @ <td class="timelineTime">\
          524  +    @ %z(href("%R/artifact/%!S",zUuid))%s(zTime)</a></td>
   500    525       @ <td class="timelineGraph"><div id="m%d(gidx)" class="tl-nodemark"></div>
   501    526       @ </td>
   502    527       if( zBgClr && zBgClr[0] ){
   503         -      @ <td class="timelineTableCell" style="background-color: %h(zBgClr);">
          528  +      @ <td class="timeline%s(zStyle)Cell" id='mc%d(gidx)'>
          529  +    }else{
          530  +      @ <td class="timeline%s(zStyle)Cell">
          531  +    }
          532  +    if( tmFlags & TIMELINE_COMPACT ){
          533  +      @ <span class='timelineCompactComment' data-id='%d(frid)'>
   504    534       }else{
   505         -      @ <td class="timelineTableCell">
          535  +      @ <span class='timeline%s(zStyle)Comment'>
          536  +      if( (tmFlags & TIMELINE_VERBOSE)!=0 && zUuid ){
          537  +        hyperlink_to_uuid(zUuid);
          538  +        @ part of check-in \
          539  +        hyperlink_to_uuid(zCkin);
          540  +      }
          541  +    }
          542  +    @ %W(zCom)</span>
          543  +    if( (tmFlags & TIMELINE_COMPACT)!=0 ){
          544  +      @ <span class='timelineEllipsis' data-id='%d(frid)' \
          545  +      @ id='ellipsis-%d(frid)'>...</span>
          546  +      @ <span class='clutter timelineCompactDetail'
          547  +    }
          548  +    if( tmFlags & TIMELINE_COLUMNAR ){
          549  +      if( zBgClr && zBgClr[0] ){
          550  +        @ <td class="timelineDetailCell" id='md%d(gidx)'>
          551  +      }else{
          552  +        @ <td class="timelineDetailCell">
          553  +      }
          554  +    }
          555  +    if( tmFlags & TIMELINE_COMPACT ){
          556  +      cgi_printf("<span class='clutter' id='detail-%d'>",frid);
   506    557       }
   507         -    if( zUuid ){
   508         -      if( origCheckin==0 ){
   509         -        if( nParent==0 ){
   510         -          @ <b>Added</b>
   511         -        }else if( pfnid ){
   512         -          char *zPrevName = db_text(0,"SELECT name FROM filename WHERE fnid=%d",
   513         -                                    pfnid);
   514         -          @ <b>Renamed</b> from
   515         -          @ %z(href("%R/finfo?name=%t", zPrevName))%h(zPrevName)</a>
   516         -        }
   517         -      }
   518         -      @ %z(href("%R/artifact/%!S",zUuid))[%S(zUuid)]</a>
          558  +    cgi_printf("<span class='timeline%sDetail'>", zStyle);
          559  +    if( tmFlags & (TIMELINE_COMPACT|TIMELINE_VERBOSE) ) cgi_printf("(");
          560  +    if( zUuid && (tmFlags & TIMELINE_VERBOSE)==0 ){
          561  +      @ file:&nbsp;%z(href("%R/artifact/%!S",zUuid))[%S(zUuid)]</a>
   519    562         if( fShowId ){
   520    563           int srcId = delta_source_rid(frid);
   521    564           if( srcId>0 ){
   522         -          @ (%d(frid)&larr;%d(srcId))
          565  +          @ id:&nbsp;%d(frid)&larr;%d(srcId)
   523    566           }else{
   524         -          @ (%d(frid))
          567  +          @ id:&nbsp;%d(frid)
   525    568           }
   526    569         }
   527         -      @ part of check-in
          570  +    }
          571  +    @ check-in:&nbsp;\
          572  +    hyperlink_to_uuid(zCkin);
          573  +    if( fShowId ){
          574  +      @ (%d(fmid))
          575  +    }
          576  +    @ user:&nbsp;\
          577  +    hyperlink_to_user(zUser, zDate, ",");
          578  +    @ branch:&nbsp;%z(href("%R/timeline?t=%T&n=200",zBr))%h(zBr)</a>,
          579  +    if( tmFlags & (TIMELINE_COMPACT|TIMELINE_VERBOSE) ){
          580  +      @ size:&nbsp;%d(szFile))
   528    581       }else{
          582  +      @ size:&nbsp;%d(szFile)
          583  +    }
          584  +    if( zUuid && origCheckin==0 ){
          585  +      if( nParent==0 ){
          586  +        @ <b>Added</b>
          587  +      }else if( pfnid ){
          588  +        char *zPrevName = db_text(0,"SELECT name FROM filename WHERE fnid=%d",
          589  +                                  pfnid);
          590  +        @ <b>Renamed</b> from
          591  +        @ %z(href("%R/finfo?name=%t", zPrevName))%h(zPrevName)</a>
          592  +      }
          593  +    }
          594  +    if( zUuid==0 ){
   529    595         char *zNewName;
   530    596         zNewName = db_text(0,
   531    597           "SELECT name FROM filename WHERE fnid = "
   532    598           "   (SELECT fnid FROM mlink"
   533    599           "     WHERE mid=%d"
   534    600           "       AND pfnid IN (SELECT fnid FROM filename WHERE name=%Q))",
   535    601           fmid, zFilename);
   536    602         if( zNewName ){
   537    603           @ <b>Renamed</b> to
   538         -        @ %z(href("%R/finfo?name=%t",zNewName))%h(zNewName)</a> by check-in
          604  +        @ %z(href("%R/finfo?name=%t",zNewName))%h(zNewName)</a>
   539    605           fossil_free(zNewName);
   540    606         }else{
   541         -        @ <b>Deleted</b> by check-in
          607  +        @ <b>Deleted</b>
   542    608         }
   543    609       }
   544         -    hyperlink_to_uuid(zCkin);
   545         -    if( fShowId ){
   546         -      @ (%d(fmid))
   547         -    }
   548         -    @ %W(zCom) (user:
   549         -    hyperlink_to_user(zUser, zDate, ",");
   550         -    @ branch: %z(href("%R/timeline?t=%T&n=200",zBr))%h(zBr)</a>,
   551         -    @ size: %d(szFile))
   552    610       if( g.perm.Hyperlink && zUuid ){
   553    611         const char *z = zFilename;
          612  +      @ <span id='links-%d(frid)'><span class='timelineExtraLinks'>
   554    613         @ %z(href("%R/annotate?filename=%h&checkin=%s",z,zCkin))
   555    614         @ [annotate]</a>
   556    615         @ %z(href("%R/blame?filename=%h&checkin=%s",z,zCkin))
   557    616         @ [blame]</a>
   558         -      @ %z(href("%R/timeline?n=200&uf=%!S",zUuid))[check-ins&nbsp;using]</a>
          617  +      @ %z(href("%R/timeline?n=all&uf=%!S",zUuid))[check-ins&nbsp;using]</a>
   559    618         if( fpid>0 ){
   560         -        @ %z(href("%R/fdiff?sbs=1&v1=%!S&v2=%!S",zPUuid,zUuid))[diff]</a>
          619  +        @ %z(href("%R/fdiff?v1=%!S&v2=%!S",zPUuid,zUuid))[diff]</a>
   561    620         }
          621  +      @ </span></span>
   562    622       }
   563    623       if( fDebug & FINFO_DEBUG_MLINK ){
   564    624         int ii;
   565    625         char *zAncLink;
   566    626         @ <br />fid=%d(frid) pid=%d(fpid) mid=%d(fmid)
   567    627         if( nParent>0 ){
   568    628           @ parents=%d(aParent[0])
................................................................................
   570    630             @ %d(aParent[ii])
   571    631           }
   572    632         }
   573    633         zAncLink = href("%R/finfo?name=%T&ci=%!S&debug=1",zFilename,zCkin);
   574    634         @ %z(zAncLink)[ancestry]</a>
   575    635       }
   576    636       tag_private_status(frid);
          637  +    /* End timelineDetail */
          638  +    if( tmFlags & TIMELINE_COMPACT ){
          639  +      @ </span></span>
          640  +    }else{
          641  +      @ </span>
          642  +    }
   577    643       @ </td></tr>
   578    644     }
   579    645     db_finalize(&q);
   580    646     db_finalize(&qparent);
   581    647     if( pGraph ){
   582    648       graph_finish(pGraph, 1);
   583    649       if( pGraph->nErr ){
................................................................................
   584    650         graph_free(pGraph);
   585    651         pGraph = 0;
   586    652       }else{
   587    653         @ <tr class="timelineBottom"><td></td><td></td><td></td></tr>
   588    654       }
   589    655     }
   590    656     @ </table>
   591         -  timeline_output_graph_javascript(pGraph, 0, iTableId, 1);
          657  +  timeline_output_graph_javascript(pGraph, TIMELINE_FILEDIFF, iTableId);
   592    658     style_footer();
   593    659   }
   594    660   
   595    661   /*
   596    662   ** WEBPAGE: mlink
   597    663   ** URL: /mlink?name=FILENAME
   598    664   ** URL: /mlink?ci=NAME
   599    665   **
   600    666   ** Show all MLINK table entries for a particular file, or for
   601    667   ** a particular check-in.
   602    668   **
   603    669   ** This screen is intended for use by Fossil developers to help
   604         -** in debugging Fossil itself.  Ordinary Fossil users are not 
          670  +** in debugging Fossil itself.  Ordinary Fossil users are not
   605    671   ** expected to know what the MLINK table is or why it is important.
   606    672   **
   607    673   ** To avoid confusing ordinary users, this page is only available
   608         -** to adminstrators.
          674  +** to administrators.
   609    675   */
   610    676   void mlink_page(void){
   611    677     const char *zFName = P("name");
   612    678     const char *zCI = P("ci");
   613    679     Stmt q;
   614    680   
   615    681     login_check_credentials();
................................................................................
   635    701          /* 8 */ "  (SELECT name FROM filename WHERE fnid=mlink.pfnid)"
   636    702          "  FROM mlink, event"
   637    703          " WHERE mlink.fnid=%d"
   638    704          "   AND event.objid=mlink.mid"
   639    705          " ORDER BY 1 DESC",
   640    706          fnid
   641    707       );
          708  +    style_table_sorter();
   642    709       @ <h1>MLINK table for file
   643    710       @ <a href='%R/finfo?name=%t(zFName)'>%h(zFName)</a></h1>
   644    711       @ <div class='brlist'>
   645         -    @ <table id='mlinktable'>
          712  +    @ <table class='sortable' data-column-types='tttxtttt' data-init-sort='1'>
   646    713       @ <thead><tr>
   647    714       @ <th>Date</th>
   648    715       @ <th>Check-in</th>
   649    716       @ <th>Parent<br>Check-in</th>
   650    717       @ <th>Merge?</th>
   651    718       @ <th>New</th>
   652    719       @ <th>Old</th>
................................................................................
   692    759         }
   693    760         @ </tr>
   694    761       }
   695    762       db_finalize(&q);
   696    763       @ </tbody>
   697    764       @ </table>
   698    765       @ </div>
   699         -    output_table_sorting_javascript("mlinktable","tttxtttt",1);
   700    766     }else{
   701    767       int mid = name_to_rid_www("ci");
   702    768       db_prepare(&q,
   703    769          "SELECT"
   704    770          /* 0 */ "  (SELECT name FROM filename WHERE fnid=mlink.fnid),"
   705    771          /* 1 */ "  (SELECT uuid FROM blob WHERE rid=mlink.fid),"
   706    772          /* 2 */ "  pid,"
................................................................................
   710    776          /* 6 */ "  mperm,"
   711    777          /* 7 */ "  isaux"
   712    778          "  FROM mlink WHERE mid=%d ORDER BY 1",
   713    779          mid
   714    780       );
   715    781       @ <h1>MLINK table for check-in %h(zCI)</h1>
   716    782       render_checkin_context(mid, 1);
          783  +    style_table_sorter();
   717    784       @ <hr />
   718    785       @ <div class='brlist'>
   719         -    @ <table id='mlinktable'>
          786  +    @ <table class='sortable' data-column-types='ttxtttt' data-init-sort='1'>
   720    787       @ <thead><tr>
   721    788       @ <th>File</th>
   722    789       @ <th>Parent<br>Check-in</th>
   723    790       @ <th>Merge?</th>
   724    791       @ <th>New</th>
   725    792       @ <th>Old</th>
   726    793       @ <th>Exe<br>Bit?</th>
................................................................................
   763    830         }
   764    831         @ </tr>
   765    832       }
   766    833       db_finalize(&q);
   767    834       @ </tbody>
   768    835       @ </table>
   769    836       @ </div>
   770         -    output_table_sorting_javascript("mlinktable","ttxtttt",1);
   771    837     }
   772    838     style_footer();
   773    839   }

Changes to src/fusefs.c.

   321    321     int doDebug = find_option("debug","d",0)!=0;
   322    322   
   323    323     db_find_and_open_repository(0,0);
   324    324     verify_all_options();
   325    325     blob_init(&fusefs.content, 0, 0);
   326    326     if( g.argc!=3 ) usage("DIRECTORY");
   327    327     zMountPoint = g.argv[2];
   328         -  if( file_mkdir(zMountPoint, 0) ){
          328  +  if( file_mkdir(zMountPoint, ExtFILE, 0) ){
   329    329       fossil_fatal("cannot make directory [%s]", zMountPoint);
   330    330     }
   331    331     azNewArgv[0] = g.argv[0];
   332    332     azNewArgv[1] = doDebug ? "-d" : "-f";
   333    333     azNewArgv[2] = "-s";
   334    334     azNewArgv[3] = zMountPoint;
   335    335     azNewArgv[4] = 0;

Added src/graph.js.

            1  +/* This module contains javascript needed to render timeline graphs in Fossil.
            2  +**
            3  +** Prior to sourcing this script, there should be a separate
            4  +** <script type='application/json' id='timeline-data-NN'> for each graph,
            5  +** each containing JSON like this:
            6  +**
            7  +**   { "iTableId": INTEGER,         // Table sequence number (NN)
            8  +**     "circleNodes": BOOLEAN,      // True for circle nodes. False for squares
            9  +**     "showArrowheads": BOOLEAN,   // True for arrowheads. False to omit
           10  +**     "iRailPitch": INTEGER,       // Spacing between vertical lines (px)
           11  +**     "colorGraph": BOOLEAN,       // True to put color on graph lines
           12  +**     "nomo": BOOLEAN,             // True to join merge lines with rails
           13  +**     "iTopRow": INTEGER,          // Index of top-most row in the graph
           14  +**     "omitDescenders": BOOLEAN,   // Omit ancestor lines off bottom of screen
           15  +**     "fileDiff": BOOLEAN,         // True for file diff. False for check-in
           16  +**     "scrollToSelect": BOOLEAN,   // Scroll to selection on first render
           17  +**     "nrail": INTEGER,            // Number of vertical "rails"
           18  +**     "baseUrl": TEXT,             // Top-level URL
           19  +**     "rowinfo": ROWINFO-ARRAY }
           20  +**
           21  +** The rowinfo field is an array of structures, one per entry in the timeline,
           22  +** where each structure has the following fields:
           23  +**
           24  +**   id:  The id of the <div> element for the row. This is an integer.
           25  +**        to get an actual id, prepend "m" to the integer.  The top node
           26  +**        is iTopRow and numbers increase moving down the tx.
           27  +**   bg:  The background color for this row
           28  +**    r:  The "rail" that the node for this row sits on.  The left-most
           29  +**        rail is 0 and the number increases to the right.
           30  +**    d:  True if there is a "descender" - an arrow coming from the bottom
           31  +**        of the page straight up to this node.
           32  +**   mo:  "merge-out".  If non-negative, this is the rail position
           33  +**        for the upward portion of a merge arrow.  The merge arrow goes up
           34  +**        to the row identified by mu:.  If this value is negative then
           35  +**        node has no merge children and no merge-out line is drawn.
           36  +**   mu:  The id of the row which is the top of the merge-out arrow.
           37  +**    u:  Draw a thick child-line out of the top of this node and up to
           38  +**        the node with an id equal to this value.  0 if it is straight to
           39  +**        the top of the page, -1 if there is no thick-line riser.
           40  +**    f:  0x01: a leaf node.
           41  +**   au:  An array of integers that define thick-line risers for branches.
           42  +**        The integers are in pairs.  For each pair, the first integer is
           43  +**        is the rail on which the riser should run and the second integer
           44  +**        is the id of the node upto which the riser should run.
           45  +**   mi:  "merge-in".  An array of integer rail positions from which
           46  +**        merge arrows should be drawn into this node.  If the value is
           47  +**        negative, then the rail position is the absolute value of mi[]
           48  +**        and a thin merge-arrow descender is drawn to the bottom of
           49  +**        the screen.
           50  +**    h:  The artifact hash of the object being graphed
           51  +*/
           52  +var amendCssOnce = 1; // Only change the CSS one time
           53  +function amendCss(circleNodes,showArrowheads){
           54  +  if( !amendCssOnce ) return;
           55  +  var css = "";
           56  +  if( circleNodes ){
           57  +    css += ".tl-node, .tl-node:after { border-radius: 50%; }";
           58  +  }
           59  +  if( !showArrowheads ){
           60  +    css += ".tl-arrow.u { display: none; }";
           61  +  }
           62  +  if( css!=="" ){
           63  +    var style = document.createElement("style");
           64  +    style.textContent = css;
           65  +    document.querySelector("head").appendChild(style);
           66  +  }
           67  +  amendCssOnce = 0;
           68  +}
           69  +
           70  +function TimelineGraph(tx){
           71  +  var topObj = document.getElementById("timelineTable"+tx.iTableId);
           72  +  amendCss(tx.circleNodes, tx.showArrowheads);
           73  +  var canvasDiv;
           74  +  var railPitch;
           75  +  var mergeOffset;
           76  +  var node, arrow, arrowSmall, line, mArrow, mLine, wArrow, wLine;
           77  +  function initGraph(){
           78  +    var parent = topObj.rows[0].cells[1];
           79  +    parent.style.verticalAlign = "top";
           80  +    canvasDiv = document.createElement("div");
           81  +    canvasDiv.className = "tl-canvas";
           82  +    canvasDiv.style.position = "absolute";
           83  +    parent.appendChild(canvasDiv);
           84  +  
           85  +    var elems = {};
           86  +    var elemClasses = [
           87  +      "rail", "mergeoffset", "node", "arrow u", "arrow u sm", "line",
           88  +      "arrow merge r", "line merge", "arrow warp", "line warp"
           89  +    ];
           90  +    for( var i=0; i<elemClasses.length; i++ ){
           91  +      var cls = elemClasses[i];
           92  +      var elem = document.createElement("div");
           93  +      elem.className = "tl-" + cls;
           94  +      if( cls.indexOf("line")==0 ) elem.className += " v";
           95  +      canvasDiv.appendChild(elem);
           96  +      var k = cls.replace(/\s/g, "_");
           97  +      var r = elem.getBoundingClientRect();
           98  +      var w = Math.round(r.right - r.left);
           99  +      var h = Math.round(r.bottom - r.top);
          100  +      elems[k] = {w: w, h: h, cls: cls};
          101  +    }
          102  +    node = elems.node;
          103  +    arrow = elems.arrow_u;
          104  +    arrowSmall = elems.arrow_u_sm;
          105  +    line = elems.line;
          106  +    mArrow = elems.arrow_merge_r;
          107  +    mLine = elems.line_merge;
          108  +    wArrow = elems.arrow_warp;
          109  +    wLine = elems.line_warp;
          110  +  
          111  +    var minRailPitch = Math.ceil((node.w+line.w)/2 + mArrow.w + 1);
          112  +    if( tx.iRailPitch>0 ){
          113  +      railPitch = tx.iRailPitch;
          114  +    }else{
          115  +      railPitch = elems.rail.w;
          116  +      railPitch -= Math.floor((tx.nrail-1)*(railPitch-minRailPitch)/21);
          117  +    }
          118  +    railPitch = Math.max(railPitch, minRailPitch);
          119  +  
          120  +    if( tx.nomo ){
          121  +      mergeOffset = 0;
          122  +    }else{
          123  +      mergeOffset = railPitch-minRailPitch-mLine.w;
          124  +      mergeOffset = Math.min(mergeOffset, elems.mergeoffset.w);
          125  +      mergeOffset = mergeOffset>0 ? mergeOffset + line.w/2 : 0;
          126  +    }
          127  +  
          128  +    var canvasWidth = (tx.nrail-1)*railPitch + node.w;
          129  +    canvasDiv.style.width = canvasWidth + "px";
          130  +    canvasDiv.style.position = "relative";
          131  +  }
          132  +  function drawBox(cls,color,x0,y0,x1,y1){
          133  +    var n = document.createElement("div");
          134  +    x0 = Math.floor(x0);
          135  +    y0 = Math.floor(y0);
          136  +    x1 = x1 || x1===0 ? Math.floor(x1) : x0;
          137  +    y1 = y1 || y1===0 ? Math.floor(y1) : y0;
          138  +    if( x0>x1 ){ var t=x0; x0=x1; x1=t; }
          139  +    if( y0>y1 ){ var t=y0; y0=y1; y1=t; }
          140  +    var w = x1-x0;
          141  +    var h = y1-y0;
          142  +    n.style.position = "absolute";
          143  +    n.style.left = x0+"px";
          144  +    n.style.top = y0+"px";
          145  +    if( w ) n.style.width = w+"px";
          146  +    if( h ) n.style.height = h+"px";
          147  +    if( color ) n.style.backgroundColor = color;
          148  +    n.className = "tl-"+cls;
          149  +    canvasDiv.appendChild(n);
          150  +    return n;
          151  +  }
          152  +  function absoluteY(obj){
          153  +    var top = 0;
          154  +    if( obj.offsetParent ){
          155  +      do{
          156  +        top += obj.offsetTop;
          157  +      }while( obj = obj.offsetParent );
          158  +    }
          159  +    return top;
          160  +  }
          161  +  function miLineY(p){
          162  +    return p.y + node.h - mLine.w - 1;
          163  +  }
          164  +  function drawLine(elem,color,x0,y0,x1,y1){
          165  +    var cls = elem.cls + " ";
          166  +    if( x1===null ){
          167  +      x1 = x0+elem.w;
          168  +      cls += "v";
          169  +    }else{
          170  +      y1 = y0+elem.w;
          171  +      cls += "h";
          172  +    }
          173  +    drawBox(cls,color,x0,y0,x1,y1);
          174  +  }
          175  +  function drawUpArrow(from,to,color){
          176  +    var y = to.y + node.h;
          177  +    var arrowSpace = from.y - y + (!from.id || from.r!=to.r ? node.h/2 : 0);
          178  +    var arw = arrowSpace < arrow.h*1.5 ? arrowSmall : arrow;
          179  +    var x = to.x + (node.w-line.w)/2;
          180  +    var y0 = from.y + node.h/2;
          181  +    var y1 = Math.ceil(to.y + node.h + arw.h/2);
          182  +    drawLine(line,color,x,y0,null,y1);
          183  +    x = to.x + (node.w-arw.w)/2;
          184  +    var n = drawBox(arw.cls,null,x,y);
          185  +    if(color) n.style.borderBottomColor = color;
          186  +  }
          187  +  function drawMergeLine(x0,y0,x1,y1){
          188  +    drawLine(mLine,null,x0,y0,x1,y1);
          189  +  }
          190  +  function drawMergeArrow(p,rail){
          191  +    var x0 = rail*railPitch + node.w/2;
          192  +    if( rail in mergeLines ){
          193  +      x0 += mergeLines[rail];
          194  +      if( p.r<rail ) x0 += mLine.w;
          195  +    }else{
          196  +      x0 += (p.r<rail ? -1 : 1)*line.w/2;
          197  +    }
          198  +    var x1 = mArrow.w ? mArrow.w/2 : -node.w/2;
          199  +    x1 = p.x + (p.r<rail ? node.w + Math.ceil(x1) : -x1);
          200  +    var y = miLineY(p);
          201  +    drawMergeLine(x0,y,x1,null);
          202  +    var x = p.x + (p.r<rail ? node.w : -mArrow.w);
          203  +    var cls = "arrow merge " + (p.r<rail ? "l" : "r");
          204  +    drawBox(cls,null,x,y+(mLine.w-mArrow.h)/2);
          205  +  }
          206  +  function drawNode(p, btm){
          207  +    if( p.bg ){
          208  +      var e = document.getElementById("mc"+p.id);
          209  +      if(e) e.style.backgroundColor = p.bg;
          210  +      e = document.getElementById("md"+p.id);
          211  +      if(e) e.style.backgroundColor = p.bg;
          212  +    }
          213  +    if( p.u>0 ) drawUpArrow(p,tx.rowinfo[p.u-tx.iTopRow],p.fg);
          214  +    var cls = node.cls;
          215  +    if( p.mi.length ) cls += " merge";
          216  +    if( p.f&1 ) cls += " leaf";
          217  +    var n = drawBox(cls,p.bg,p.x,p.y);
          218  +    n.id = "tln"+p.id;
          219  +    n.onclick = clickOnNode;
          220  +    n.style.zIndex = 10;
          221  +    if( !tx.omitDescenders ){
          222  +      if( p.u==0 ) drawUpArrow(p,{x: p.x, y: -node.h},p.fg);
          223  +      if( p.d ) drawUpArrow({x: p.x, y: btm-node.h/2},p,p.fg);
          224  +    }
          225  +    if( p.mo>=0 ){
          226  +      var x0 = p.x + node.w/2;
          227  +      var x1 = p.mo*railPitch + node.w/2;
          228  +      var u = tx.rowinfo[p.mu-tx.iTopRow];
          229  +      var y1 = miLineY(u);
          230  +      if( p.u<0 || p.mo!=p.r ){
          231  +        x1 += mergeLines[p.mo] = -mLine.w/2;
          232  +        var y0 = p.y+2;
          233  +        if( p.r!=p.mo ) drawMergeLine(x0,y0,x1+(x0<x1 ? mLine.w : 0),null);
          234  +        drawMergeLine(x1,y0+mLine.w,null,y1);
          235  +      }else if( mergeOffset ){
          236  +        mergeLines[p.mo] = u.r<p.r ? -mergeOffset-mLine.w : mergeOffset;
          237  +        x1 += mergeLines[p.mo];
          238  +        drawMergeLine(x1,p.y+node.h/2,null,y1);
          239  +      }else{
          240  +        delete mergeLines[p.mo];
          241  +      }
          242  +    }
          243  +    for( var i=0; i<p.au.length; i+=2 ){
          244  +      var rail = p.au[i];
          245  +      var x0 = p.x + node.w/2;
          246  +      var x1 = rail*railPitch + (node.w-line.w)/2;
          247  +      if( x0<x1 ){
          248  +        x0 = Math.ceil(x0);
          249  +        x1 += line.w;
          250  +      }
          251  +      var y0 = p.y + (node.h-line.w)/2;
          252  +      var u = tx.rowinfo[p.au[i+1]-tx.iTopRow];
          253  +      if( u.id<p.id ){
          254  +        drawLine(line,u.fg,x0,y0,x1,null);
          255  +        drawUpArrow(p,u,u.fg);
          256  +      }else{
          257  +        var y1 = u.y + (node.h-line.w)/2;
          258  +        drawLine(wLine,u.fg,x0,y0,x1,null);
          259  +        drawLine(wLine,u.fg,x1-line.w,y0,null,y1+line.w);
          260  +        drawLine(wLine,u.fg,x1,y1,u.x-wArrow.w/2,null);
          261  +        var x = u.x-wArrow.w;
          262  +        var y = u.y+(node.h-wArrow.h)/2;
          263  +        var n = drawBox(wArrow.cls,null,x,y);
          264  +        if( u.fg ) n.style.borderLeftColor = u.fg;
          265  +      }
          266  +    }
          267  +    for( var i=0; i<p.mi.length; i++ ){
          268  +      var rail = p.mi[i];
          269  +      if( rail<0 ){
          270  +        rail = -rail;
          271  +        mergeLines[rail] = -mLine.w/2;
          272  +        var x = rail*railPitch + (node.w-mLine.w)/2;
          273  +        drawMergeLine(x,miLineY(p),null,btm);
          274  +      }
          275  +      drawMergeArrow(p,rail);
          276  +    }
          277  +  }
          278  +  var mergeLines;
          279  +  function renderGraph(){
          280  +    mergeLines = {};
          281  +    canvasDiv.innerHTML = "";
          282  +    var canvasY = absoluteY(canvasDiv);
          283  +    for(var i=0; i<tx.rowinfo.length; i++ ){
          284  +      var e = document.getElementById("m"+tx.rowinfo[i].id);
          285  +      tx.rowinfo[i].y = absoluteY(e) - canvasY;
          286  +      tx.rowinfo[i].x = tx.rowinfo[i].r*railPitch;
          287  +    }
          288  +    var tlBtm = document.querySelector(".timelineBottom");
          289  +    if( tlBtm.offsetHeight<node.h ){
          290  +      tlBtm.style.height = node.h + "px";
          291  +    }
          292  +    var btm = absoluteY(tlBtm) - canvasY + tlBtm.offsetHeight;
          293  +    for( var i=tx.rowinfo.length-1; i>=0; i-- ){
          294  +      drawNode(tx.rowinfo[i], btm);
          295  +    }
          296  +  }
          297  +  var selRow;
          298  +  function clickOnNode(){
          299  +    var p = tx.rowinfo[parseInt(this.id.match(/\d+$/)[0], 10)-tx.iTopRow];
          300  +    if( !selRow ){
          301  +      selRow = p;
          302  +      this.className += " sel";
          303  +      canvasDiv.className += " sel";
          304  +    }else if( selRow==p ){
          305  +      selRow = null;
          306  +      this.className = this.className.replace(" sel", "");
          307  +      canvasDiv.className = canvasDiv.className.replace(" sel", "");
          308  +    }else{
          309  +      if( tx.fileDiff ){
          310  +        location.href=tx.baseUrl + "/fdiff?v1="+selRow.h+"&v2="+p.h
          311  +      }else{
          312  +        location.href=tx.baseUrl + "/vdiff?from="+selRow.h+"&to="+p.h
          313  +      }
          314  +    }
          315  +  }
          316  +  function changeDisplay(selector,value){
          317  +    var x = document.getElementsByClassName(selector);
          318  +    var n = x.length;
          319  +    for(var i=0; i<n; i++) {x[i].style.display = value;}
          320  +  }
          321  +  function changeDisplayById(id,value){
          322  +    var x = document.getElementById(id);
          323  +    if(x) x.style.display=value;
          324  +  }
          325  +  function toggleDetail(){
          326  +    var id = parseInt(this.getAttribute('data-id'))
          327  +    var x = document.getElementById("detail-"+id);
          328  +    if( x.style.display=="inline" ){
          329  +      x.style.display="none";
          330  +      changeDisplayById("ellipsis-"+id,"inline");
          331  +      changeDisplayById("links-"+id,"none");
          332  +    }else{
          333  +      x.style.display="inline";
          334  +      changeDisplayById("ellipsis-"+id,"none");
          335  +      changeDisplayById("links-"+id,"inline");
          336  +    }
          337  +    checkHeight();
          338  +  }
          339  +  function scrollToSelected(){
          340  +    var x = document.getElementsByClassName('timelineSelected');
          341  +    if(x[0]){
          342  +      var h = window.innerHeight;
          343  +      var y = absoluteY(x[0]) - h/2;
          344  +      if( y>0 ) window.scrollTo(0, y);
          345  +    }
          346  +  }
          347  +  var lastRow = document.getElementById("m"+tx.rowinfo[tx.rowinfo.length-1].id);
          348  +  var lastY = 0;
          349  +  function checkHeight(){
          350  +    var h = absoluteY(lastRow);
          351  +    if( h!=lastY ){
          352  +      renderGraph();
          353  +      lastY = h;
          354  +    }
          355  +    setTimeout(checkHeight, 1000);
          356  +  }
          357  +  initGraph();
          358  +  checkHeight();
          359  +  if( tx.scrollToSelect ){
          360  +    scrollToSelected();
          361  +  }
          362  +
          363  +  /* Set the onclick= attributes for elements of the "Compact" display
          364  +  ** mode so that clicking turns the details on and off.
          365  +  */
          366  +  var lx = topObj.getElementsByClassName('timelineEllipsis');
          367  +  var i;
          368  +  for(i=0; i<lx.length; i++){
          369  +    if( lx[i].hasAttribute('data-id') ) lx[i].onclick = toggleDetail;
          370  +  }
          371  +  lx = topObj.getElementsByClassName('timelineCompactComment');
          372  +  for(i=0; i<lx.length; i++){
          373  +    if( lx[i].hasAttribute('data-id') ) lx[i].onclick = toggleDetail;
          374  +  }
          375  +}
          376  +  
          377  +/* Look for all timeline-data-NN objects.  Load each one and draw
          378  +** a graph for each one.
          379  +*/
          380  +(function(){
          381  +  var i;
          382  +  for(i=0; 1; i++){
          383  +    var dataObj = document.getElementById("timeline-data-"+i);
          384  +    if(!dataObj) break;
          385  +    var txJson = dataObj.textContent || dataObj.innerText;
          386  +    var tx = JSON.parse(txJson);
          387  +    if(tx.rowinfo) TimelineGraph(tx);
          388  +  }
          389  +}())

Changes to src/gzip.c.

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

Changes to src/hname.c.

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

Added src/href.js.

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

Changes to src/http_socket.c.

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

Changes to src/http_ssl.c.

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

Changes to src/info.c.

   199    199   void info_cmd(void){
   200    200     i64 fsize;
   201    201     int verboseFlag = find_option("verbose","v",0)!=0;
   202    202     if( !verboseFlag ){
   203    203       verboseFlag = find_option("detail","l",0)!=0; /* deprecated */
   204    204     }
   205    205   
   206         -  if( g.argc==3 && (fsize = file_size(g.argv[2]))>0 && (fsize&0x1ff)==0 ){
          206  +  if( g.argc==3
          207  +   && (fsize = file_size(g.argv[2], ExtFILE))>0
          208  +   && (fsize&0x1ff)==0
          209  +  ){
   207    210       db_open_config(0, 0);
   208    211       db_open_repository(g.argv[2]);
   209    212       db_record_repository_filename(g.argv[2]);
   210    213       fossil_print("project-name: %s\n", db_get("project-name", "<unnamed>"));
   211    214       fossil_print("project-code: %s\n", db_get("project-code", "<none>"));
   212    215       showParentProject();
   213    216       extraRepoInfo();
................................................................................
   242    245       if( rid==0 ){
   243    246         fossil_fatal("no such object: %s", g.argv[2]);
   244    247       }
   245    248       show_common_info(rid, "uuid:", 1, 1);
   246    249     }
   247    250   }
   248    251   
   249         -/*
   250         -** Show information about all tags on a given check-in.
   251         -*/
   252         -static void showTags(int rid){
   253         -  Stmt q;
   254         -  int cnt = 0;
   255         -  db_prepare(&q,
   256         -    "SELECT tag.tagid, tagname, "
   257         -    "       (SELECT uuid FROM blob WHERE rid=tagxref.srcid AND rid!=%d),"
   258         -    "       value, datetime(tagxref.mtime,toLocal()), tagtype,"
   259         -    "       (SELECT uuid FROM blob WHERE rid=tagxref.origid AND rid!=%d)"
   260         -    "  FROM tagxref JOIN tag ON tagxref.tagid=tag.tagid"
   261         -    " WHERE tagxref.rid=%d"
   262         -    " ORDER BY tagname /*sort*/", rid, rid, rid
   263         -  );
   264         -  while( db_step(&q)==SQLITE_ROW ){
   265         -    const char *zTagname = db_column_text(&q, 1);
   266         -    const char *zSrcUuid = db_column_text(&q, 2);
   267         -    const char *zValue = db_column_text(&q, 3);
   268         -    const char *zDate = db_column_text(&q, 4);
   269         -    int tagtype = db_column_int(&q, 5);
   270         -    const char *zOrigUuid = db_column_text(&q, 6);
   271         -    cnt++;
   272         -    if( cnt==1 ){
   273         -      @ <div class="section">Tags And Properties</div>
   274         -      @ <ul>
   275         -    }
   276         -    @ <li>
   277         -    if( tagtype==0 ){
   278         -      @ <span class="infoTagCancelled">%h(zTagname)</span> cancelled
   279         -    }else if( zValue ){
   280         -      @ <span class="infoTag">%h(zTagname)=%h(zValue)</span>
   281         -    }else {
   282         -      @ <span class="infoTag">%h(zTagname)</span>
   283         -    }
   284         -    if( tagtype==2 ){
   285         -      if( zOrigUuid && zOrigUuid[0] ){
   286         -        @ inherited from
   287         -        hyperlink_to_uuid(zOrigUuid);
   288         -      }else{
   289         -        @ propagates to descendants
   290         -      }
   291         -#if 0
   292         -      if( zValue && fossil_strcmp(zTagname,"branch")==0 ){
   293         -        @ &nbsp;&nbsp;
   294         -        @ %z(href("%R/timeline?r=%T",zValue))branch timeline</a>
   295         -      }
   296         -#endif
   297         -    }
   298         -    if( zSrcUuid && zSrcUuid[0] ){
   299         -      if( tagtype==0 ){
   300         -        @ by
   301         -      }else{
   302         -        @ added by
   303         -      }
   304         -      hyperlink_to_uuid(zSrcUuid);
   305         -      @ on
   306         -      hyperlink_to_date(zDate,0);
   307         -    }
   308         -    @ </li>
   309         -  }
   310         -  db_finalize(&q);
   311         -  if( cnt ){
   312         -    @ </ul>
   313         -  }
   314         -}
   315         -
   316    252   /*
   317    253   ** Show the context graph (immediate parents and children) for
   318    254   ** check-in rid.
   319    255   */
   320    256   void render_checkin_context(int rid, int parentsOnly){
   321    257     Blob sql;
   322    258     Stmt q;
................................................................................
   332    268     if( !parentsOnly ){
   333    269       db_multi_exec(
   334    270         "INSERT OR IGNORE INTO ok SELECT cid FROM plink WHERE pid=%d;", rid
   335    271       );
   336    272     }
   337    273     blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
   338    274     db_prepare(&q, "%s", blob_sql_text(&sql));
   339         -  www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH, 0, 0, rid, 0);
          275  +  www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL,
          276  +                     0, 0, rid, 0);
   340    277     db_finalize(&q);
   341    278   }
   342    279   
   343    280   /*
   344    281   ** Show a graph all wiki, tickets, and check-ins that refer to object zUuid.
   345    282   **
   346    283   ** If zLabel is not NULL and the graph is not empty, then output zLabel as
................................................................................
   362    299     );
   363    300     if( !db_exists("SELECT 1 FROM ok") ) return;
   364    301     if( zLabel ) cgi_printf("%s", zLabel);
   365    302     blob_zero(&sql);
   366    303     blob_append(&sql, timeline_query_for_www(), -1);
   367    304     blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
   368    305     db_prepare(&q, "%s", blob_sql_text(&sql));
   369         -  www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH, 0, 0, 0, 0);
          306  +  www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL,
          307  +                     0, 0, 0, 0);
   370    308     db_finalize(&q);
   371    309   }
   372    310   
   373    311   /*
   374    312   ** WEBPAGE: test-backlinks
   375    313   **
   376    314   ** Show a timeline of all check-ins and other events that have entries
................................................................................
   394    332        " SELECT blob.rid FROM backlink, blob"
   395    333        "  WHERE blob.uuid BETWEEN backlink.target AND (backlink.target||'x')"
   396    334     );
   397    335     blob_zero(&sql);
   398    336     blob_append(&sql, timeline_query_for_www(), -1);
   399    337     blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
   400    338     db_prepare(&q, "%s", blob_sql_text(&sql));
   401         -  www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH, 0, 0, 0, 0);
          339  +  www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL,
          340  +                     0, 0, 0, 0);
   402    341     db_finalize(&q);
   403    342     style_footer();
   404    343   }
   405    344   
   406    345   
   407    346   /*
   408    347   ** Append the difference between artifacts to the output
................................................................................
   478    417       }
   479    418       if( diffFlags ){
   480    419         append_diff(zOld, zNew, diffFlags, pRe);
   481    420       }
   482    421     }else{
   483    422       if( zOld && zNew ){
   484    423         if( fossil_strcmp(zOld, zNew)!=0 ){
   485         -        @ Modified %z(href("%R/finfo?name=%T",zName))%h(zName)</a>
          424  +        @ Modified %z(href("%R/finfo?name=%T&m=%!S",zName,zNew))%h(zName)</a>
   486    425           @ from %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a>
   487    426           @ to %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>.
   488    427         }else if( zOldName!=0 && fossil_strcmp(zName,zOldName)!=0 ){
   489    428           @ Name change
   490         -        @ from %z(href("%R/finfo?name=%T",zOldName))%h(zOldName)</a>
   491         -        @ to %z(href("%R/finfo?name=%T",zName))%h(zName)</a>.
          429  +        @ from %z(href("%R/finfo?name=%T&m=%!S",zOldName,zOld))%h(zOldName)</a>
          430  +        @ to %z(href("%R/finfo?name=%T&m=%!S",zName,zNew))%h(zName)</a>.
   492    431         }else{
   493         -        @ %z(href("%R/finfo?name=%T",zName))%h(zName)</a> became
          432  +        @ %z(href("%R/finfo?name=%T&m=%!S",zName,zNew))%h(zName)</a> became
   494    433           if( mperm==PERM_EXE ){
   495    434             @ executable with contents
   496    435           }else if( mperm==PERM_LNK ){
   497    436             @ a symlink with target
   498    437           }else{
   499    438             @ a regular file with contents
   500    439           }
   501    440           @ %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>.
   502    441         }
   503    442       }else if( zOld ){
   504         -      @ Deleted %z(href("%R/finfo?name=%T",zName))%h(zName)</a>
          443  +      @ Deleted %z(href("%R/finfo?name=%T&m=%!S",zName,zOld))%h(zName)</a>
   505    444         @ version %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a>.
   506    445       }else{
   507         -      @ Added %z(href("%R/finfo?name=%T",zName))%h(zName)</a>
          446  +      @ Added %z(href("%R/finfo?name=%T&m=%!S",zName,zNew))%h(zName)</a>
   508    447         @ version %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>.
   509    448       }
   510    449       if( diffFlags ){
   511    450         append_diff(zOld, zNew, diffFlags, pRe);
   512    451       }else if( zOld && zNew && fossil_strcmp(zOld,zNew)!=0 ){
   513    452         @ &nbsp;&nbsp;
   514         -      @ %z(href("%R/fdiff?v1=%!S&v2=%!S&sbs=1",zOld,zNew))[diff]</a>
          453  +      @ %z(href("%R/fdiff?v1=%!S&v2=%!S",zOld,zNew))[diff]</a>
   515    454       }
   516    455     }
   517    456     @ </p>
   518    457   }
   519    458   
   520    459   /*
   521    460   ** Generate javascript to enhance HTML diffs.
   522    461   */
   523    462   void append_diff_javascript(int sideBySide){
   524    463     if( !sideBySide ) return;
   525         -  @ <script>(function(){
   526         -  @ var SCROLL_LEN = 25;
   527         -  @ function initSbsDiff(diff){
   528         -  @   var txtCols = diff.querySelectorAll('.difftxtcol');
   529         -  @   var txtPres = diff.querySelectorAll('.difftxtcol pre');
   530         -  @   var width = Math.max(txtPres[0].scrollWidth, txtPres[1].scrollWidth);
   531         -  @   for(var i=0; i<2; i++){
   532         -  @     txtPres[i].style.width = width + 'px';
   533         -  @     txtCols[i].onscroll = function(e){
   534         -  @       txtCols[0].scrollLeft = txtCols[1].scrollLeft = this.scrollLeft;
   535         -  @     };
   536         -  @   }
   537         -  @   diff.tabIndex = 0;
   538         -  @   diff.onkeydown = function(e){
   539         -  @     e = e || event;
   540         -  @     var len = {37: -SCROLL_LEN, 39: SCROLL_LEN}[e.keyCode];
   541         -  @     if( !len ) return;
   542         -  @     txtCols[0].scrollLeft += len;
   543         -  @     return false;
   544         -  @   };
   545         -  @ }
   546         -  @
   547         -  @ var diffs = document.querySelectorAll('.sbsdiffcols');
   548         -  @ for(var i=0; i<diffs.length; i++){
   549         -  @   initSbsDiff(diffs[i]);
   550         -  @ }
   551         -  @ }())</script>
          464  +  style_load_one_js_file("sbsdiff.js");
   552    465   }
   553    466   
   554    467   /*
   555    468   ** Construct an appropriate diffFlag for text_diff() based on query
   556    469   ** parameters and the to boolean arguments.
   557    470   */
   558         -u64 construct_diff_flags(int verboseFlag, int sideBySide){
          471  +u64 construct_diff_flags(int diffType){
   559    472     u64 diffFlags = 0;  /* Zero means do not show any diff */
   560         -  if( verboseFlag!=0 ){
          473  +  if( diffType>0 ){
   561    474       int x;
   562         -    if( sideBySide ){
          475  +    if( diffType==2 ){
   563    476         diffFlags = DIFF_SIDEBYSIDE;
   564    477   
   565    478         /* "dw" query parameter determines width of each column */
   566    479         x = atoi(PD("dw","80"))*(DIFF_CONTEXT_MASK+1);
   567    480         if( x<0 || x>DIFF_WIDTH_MASK ) x = DIFF_WIDTH_MASK;
   568    481         diffFlags += x;
   569    482       }
................................................................................
   578    491   
   579    492       /* The "noopt" parameter disables diff optimization */
   580    493       if( PD("noopt",0)!=0 ) diffFlags |= DIFF_NOOPT;
   581    494       diffFlags |= DIFF_STRIP_EOLCR;
   582    495     }
   583    496     return diffFlags;
   584    497   }
          498  +
          499  +/*
          500  +** WEBPAGE: ci_tags
          501  +** URL:    /ci_tags?name=ARTIFACTID
          502  +**
          503  +** Show all tags and properties for a given check-in.
          504  +**
          505  +** This information used to be part of the main /ci page, but it is of
          506  +** marginal usefulness.  Better to factor it out into a sub-screen.
          507  +*/
          508  +void ci_tags_page(void){
          509  +  const char *zHash;
          510  +  int rid;
          511  +  Stmt q;
          512  +  int cnt = 0;
          513  +  Blob sql;
          514  +
          515  +  login_check_credentials();
          516  +  if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
          517  +  rid = name_to_rid_www("name");
          518  +  if( rid==0 ){
          519  +    style_header("Check-in Information Error");
          520  +    @ No such object: %h(g.argv[2])
          521  +    style_footer();
          522  +    return;
          523  +  }
          524  +  zHash = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
          525  +  style_header("Tags and Properties");
          526  +  @ <h1>Tags and Properties for Check-In \
          527  +  @ %z(href("%R/ci/%!S",zHash))%S(zHash)</a></h1>
          528  +  db_prepare(&q,
          529  +    "SELECT tag.tagid, tagname, "
          530  +    "       (SELECT uuid FROM blob WHERE rid=tagxref.srcid AND rid!=%d),"
          531  +    "       value, datetime(tagxref.mtime,toLocal()), tagtype,"
          532  +    "       (SELECT uuid FROM blob WHERE rid=tagxref.origid AND rid!=%d)"
          533  +    "  FROM tagxref JOIN tag ON tagxref.tagid=tag.tagid"
          534  +    " WHERE tagxref.rid=%d"
          535  +    " ORDER BY tagname /*sort*/", rid, rid, rid
          536  +  );
          537  +  while( db_step(&q)==SQLITE_ROW ){
          538  +    const char *zTagname = db_column_text(&q, 1);
          539  +    const char *zSrcUuid = db_column_text(&q, 2);
          540  +    const char *zValue = db_column_text(&q, 3);
          541  +    const char *zDate = db_column_text(&q, 4);
          542  +    int tagtype = db_column_int(&q, 5);
          543  +    const char *zOrigUuid = db_column_text(&q, 6);
          544  +    cnt++;
          545  +    if( cnt==1 ){
          546  +      @ <ul>
          547  +    }
          548  +    @ <li>
          549  +    if( tagtype==0 ){
          550  +      @ <span class="infoTagCancelled">%h(zTagname)</span> cancelled
          551  +    }else if( zValue ){
          552  +      @ <span class="infoTag">%h(zTagname)=%h(zValue)</span>
          553  +    }else {
          554  +      @ <span class="infoTag">%h(zTagname)</span>
          555  +    }
          556  +    if( tagtype==2 ){
          557  +      if( zOrigUuid && zOrigUuid[0] ){
          558  +        @ inherited from
          559  +        hyperlink_to_uuid(zOrigUuid);
          560  +      }else{
          561  +        @ propagates to descendants
          562  +      }
          563  +    }
          564  +    if( zSrcUuid && zSrcUuid[0] ){
          565  +      if( tagtype==0 ){
          566  +        @ by
          567  +      }else{
          568  +        @ added by
          569  +      }
          570  +      hyperlink_to_uuid(zSrcUuid);
          571  +      @ on
          572  +      hyperlink_to_date(zDate,0);
          573  +    }
          574  +    @ </li>
          575  +  }
          576  +  db_finalize(&q);
          577  +  if( cnt ){
          578  +    @ </ul>
          579  +  }
          580  +  @ <div class="section">Context</div>
          581  +  db_multi_exec(
          582  +     "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY);"
          583  +     "DELETE FROM ok;"
          584  +     "INSERT INTO ok VALUES(%d);"
          585  +     "INSERT OR IGNORE INTO ok "
          586  +     " SELECT tagxref.srcid"
          587  +     "   FROM tagxref JOIN tag ON tagxref.tagid=tag.tagid"
          588  +     "  WHERE tagxref.rid=%d;"
          589  +     "INSERT OR IGNORE INTO ok "
          590  +     " SELECT tagxref.origid"
          591  +     "   FROM tagxref JOIN tag ON tagxref.tagid=tag.tagid"
          592  +     "  WHERE tagxref.rid=%d;",
          593  +     rid, rid, rid
          594  +  );
          595  +  db_multi_exec(
          596  +    "SELECT tag.tagid, tagname, "
          597  +    "       (SELECT uuid FROM blob WHERE rid=tagxref.srcid AND rid!=%d),"
          598  +    "       value, datetime(tagxref.mtime,toLocal()), tagtype,"
          599  +    "       (SELECT uuid FROM blob WHERE rid=tagxref.origid AND rid!=%d)"
          600  +    "  FROM tagxref JOIN tag ON tagxref.tagid=tag.tagid"
          601  +    " WHERE tagxref.rid=%d"
          602  +    " ORDER BY tagname /*sort*/", rid, rid, rid
          603  +  );
          604  +  blob_zero(&sql);
          605  +  blob_append(&sql, timeline_query_for_www(), -1);
          606  +  blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
          607  +  db_prepare(&q, "%s", blob_sql_text(&sql));
          608  +  www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL,
          609  +                     0, 0, rid, 0);
          610  +  db_finalize(&q);
          611  +  style_footer();
          612  +}
   585    613   
   586    614   /*
   587    615   ** WEBPAGE: vinfo
   588    616   ** WEBPAGE: ci
   589    617   ** URL:  /ci?name=ARTIFACTID
   590    618   ** URL:  /vinfo?name=ARTIFACTID
   591    619   **
   592    620   ** Display information about a particular check-in.
   593    621   **
   594    622   ** We also jump here from /info if the name is a check-in
   595    623   **
   596         -** If the /ci page is used (instead of /vinfo or /info) then the
   597         -** default behavior is to show unified diffs of all file changes.
   598         -** With /vinfo and /info, only a list of the changed files are
   599         -** shown, without diffs.  This behavior is inverted if the
   600         -** "show-version-diffs" setting is turned on.
          624  +** If the /ci and /vinfo pages used to differ in their default
          625  +** diff settings, but now diff settings persist with a cookie and
          626  +** so /ci and /vinfo behave the same.
   601    627   */
   602    628   void ci_page(void){
   603    629     Stmt q1, q2, q3;
   604    630     int rid;
   605    631     int isLeaf;
   606         -  int verboseFlag;     /* True to show diffs */
   607         -  int sideBySide;      /* True for side-by-side diffs */
          632  +  int diffType;        /* 0: no diff,  1: unified,  2: side-by-side */
   608    633     u64 diffFlags;       /* Flag parameter for text_diff() */
   609    634     const char *zName;   /* Name of the check-in to be displayed */
   610    635     const char *zUuid;   /* UUID of zName */
   611    636     const char *zParent; /* UUID of the parent check-in (if any) */
   612    637     const char *zRe;     /* regex parameter */
   613    638     ReCompiled *pRe = 0; /* regex */
   614    639     const char *zW;      /* URL param for ignoring whitespace */
................................................................................
   629    654     if( zRe ) re_compile(&pRe, zRe, 0);
   630    655     zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
   631    656     zParent = db_text(0,
   632    657       "SELECT uuid FROM plink, blob"
   633    658       " WHERE plink.cid=%d AND blob.rid=plink.pid AND plink.isprim",
   634    659       rid
   635    660     );
   636         -  isLeaf = is_a_leaf(rid);
          661  +  isLeaf = !db_exists("SELECT 1 FROM plink WHERE pid=%d", rid);
   637    662     db_prepare(&q1,
   638    663        "SELECT uuid, datetime(mtime,toLocal()), user, comment,"
   639    664        "       datetime(omtime,toLocal()), mtime"
   640    665        "  FROM blob, event"
   641    666        " WHERE blob.rid=%d"
   642    667        "   AND event.objid=%d",
   643    668        rid, rid
   644    669     );
   645         -  sideBySide = !is_false(PD("sbs","1"));
          670  +  
          671  +  cookie_link_parameter("diff","diff","2");
          672  +  diffType = atoi(PD("diff","2"));
   646    673     if( db_step(&q1)==SQLITE_ROW ){
   647    674       const char *zUuid = db_column_text(&q1, 0);
   648    675       int nUuid = db_column_bytes(&q1, 0);
   649    676       char *zEUser, *zEComment;
   650    677       const char *zUser;
          678  +    const char *zOrigUser;
   651    679       const char *zComment;
   652    680       const char *zDate;
   653    681       const char *zOrigDate;
   654    682   
   655    683       style_header("Check-in [%S]", zUuid);
   656    684       login_anonymous_available();
   657    685       zEUser = db_text(0,
   658    686                      "SELECT value FROM tagxref"
   659    687                      " WHERE tagid=%d AND rid=%d AND tagtype>0",
   660    688                       TAG_USER, rid);
   661    689       zEComment = db_text(0,
   662    690                      "SELECT value FROM tagxref WHERE tagid=%d AND rid=%d",
   663    691                      TAG_COMMENT, rid);
   664         -    zUser = db_column_text(&q1, 2);
          692  +    zOrigUser = db_column_text(&q1, 2);
          693  +    zUser = zEUser ? zEUser : zOrigUser;
   665    694       zComment = db_column_text(&q1, 3);
   666    695       zDate = db_column_text(&q1,1);
   667    696       zOrigDate = db_column_text(&q1, 4);
          697  +    if( zOrigDate==0 ) zOrigDate = zDate;
   668    698       @ <div class="section">Overview</div>
   669    699       @ <table class="label-value">
          700  +    @ <tr><th>Comment:</th><td class="infoComment">%!W(zComment)</td></tr>
          701  +
          702  +    /* The Download: line */
          703  +    if( g.perm.Zip  ){
          704  +      char *zPJ = db_get("short-project-name", 0);
          705  +      char *zUrl;
          706  +      Blob projName;
          707  +      int jj;
          708  +      if( zPJ==0 ) zPJ = db_get("project-name", "unnamed");
          709  +      blob_zero(&projName);
          710  +      blob_append(&projName, zPJ, -1);
          711  +      blob_trim(&projName);
          712  +      zPJ = blob_str(&projName);
          713  +      for(jj=0; zPJ[jj]; jj++){
          714  +        if( (zPJ[jj]>0 && zPJ[jj]<' ') || strchr("\"*/:<>?\\|", zPJ[jj]) ){
          715  +          zPJ[jj] = '_';
          716  +        }
          717  +      }
          718  +      zUrl = mprintf("%R/tarball/%t-%S.tar.gz?r=%s", zPJ, zUuid, zUuid);
          719  +      @ <tr><th>Downloads:</th><td>
          720  +      @ %z(href("%s",zUrl))Tarball</a>
          721  +      @ | %z(href("%R/zip/%t-%S.zip?r=%!S",zPJ,zUuid,zUuid))ZIP archive</a>
          722  +      @ | %z(href("%R/sqlar/%t-%S.sqlar?r=%!S",zPJ,zUuid,zUuid))\
          723  +      @ SQL archive</a></td></tr>
          724  +      fossil_free(zUrl);
          725  +      blob_reset(&projName);
          726  +    }
          727  +
          728  +    @ <tr><th>Timelines:</th><td>
          729  +    @   %z(href("%R/timeline?f=%!S&unhide",zUuid))family</a>
          730  +    if( zParent ){
          731  +      @ | %z(href("%R/timeline?p=%!S&unhide",zUuid))ancestors</a>
          732  +    }
          733  +    if( !isLeaf ){
          734  +      @ | %z(href("%R/timeline?d=%!S&unhide",zUuid))descendants</a>
          735  +    }
          736  +    if( zParent && !isLeaf ){
          737  +      @ | %z(href("%R/timeline?dp=%!S&unhide",zUuid))both</a>
          738  +    }
          739  +    db_prepare(&q2,"SELECT substr(tag.tagname,5) FROM tagxref, tag "
          740  +                   " WHERE rid=%d AND tagtype>0 "
          741  +                   "   AND tag.tagid=tagxref.tagid "
          742  +                   "   AND +tag.tagname GLOB 'sym-*'", rid);
          743  +    while( db_step(&q2)==SQLITE_ROW ){
          744  +      const char *zTagName = db_column_text(&q2, 0);
          745  +      @  | %z(href("%R/timeline?r=%T&unhide",zTagName))%h(zTagName)</a>
          746  +    }
          747  +    db_finalize(&q2);
          748  +    @ </td></tr>
          749  +
          750  +    @ <tr><th>Files:</th>
          751  +    @   <td>
          752  +    @     %z(href("%R/tree?ci=%!S",zUuid))files</a>
          753  +    @   | %z(href("%R/fileage?name=%!S",zUuid))file ages</a>
          754  +    @   | %z(href("%R/tree?nofiles&type=tree&ci=%!S",zUuid))folders</a>
          755  +    @   </td>
          756  +    @ </tr>
          757  +
   670    758       @ <tr><th>%s(hname_alg(nUuid)):</th><td>%s(zUuid)
   671    759       if( g.perm.Setup ){
   672    760         @ (Record ID: %d(rid))
   673    761       }
   674    762       @ </td></tr>
   675         -    @ <tr><th>Date:</th><td>
          763  +    @ <tr><th>User&nbsp;&amp;&nbsp;Date:</th><td>
          764  +    hyperlink_to_user(zUser,zDate," on ");
   676    765       hyperlink_to_date(zDate, "</td></tr>");
   677         -    if( zOrigDate && fossil_strcmp(zDate, zOrigDate)!=0 ){
   678         -      @ <tr><th>Original&nbsp;Date:</th><td>
   679         -      hyperlink_to_date(zOrigDate, "</td></tr>");
   680         -    }
   681         -    if( zEUser ){
   682         -      @ <tr><th>Edited&nbsp;User:</th><td>
   683         -      hyperlink_to_user(zEUser,zDate,"</td></tr>");
   684         -      @ <tr><th>Original&nbsp;User:</th><td>
   685         -      hyperlink_to_user(zUser,zDate,"</td></tr>");
   686         -    }else{
   687         -      @ <tr><th>User:</th><td>
   688         -      hyperlink_to_user(zUser,zDate,"</td></tr>");
   689         -    }
   690    766       if( zEComment ){
   691         -      @ <tr><th>Edited&nbsp;Comment:</th>
   692         -      @     <td class="infoComment">%!W(zEComment)</td></tr>
   693    767         @ <tr><th>Original&nbsp;Comment:</th>
   694    768         @     <td class="infoComment">%!W(zComment)</td></tr>
   695         -    }else{
   696         -      @ <tr><th>Comment:</th><td class="infoComment">%!W(zComment)</td></tr>
          769  +    }
          770  +    if( fossil_strcmp(zDate, zOrigDate)!=0
          771  +     || fossil_strcmp(zOrigUser, zUser)!=0
          772  +    ){
          773  +      @ <tr><th>Original&nbsp;User&nbsp;&amp;&nbsp;Date:</th><td>
          774  +      hyperlink_to_user(zOrigUser,zOrigDate," on ");
          775  +      hyperlink_to_date(zOrigDate, "</td></tr>");
   697    776       }
   698    777       if( g.perm.Admin ){
   699    778         db_prepare(&q2,
   700    779            "SELECT rcvfrom.ipaddr, user.login, datetime(rcvfrom.mtime)"
   701    780            "  FROM blob JOIN rcvfrom USING(rcvid) LEFT JOIN user USING(uid)"
   702    781            " WHERE blob.rid=%d",
   703    782            rid
................................................................................
   709    788           if( zUser==0 || zUser[0]==0 ) zUser = "unknown";
   710    789           @ <tr><th>Received&nbsp;From:</th>
   711    790           @ <td>%h(zUser) @ %h(zIpAddr) on %s(zDate)</td></tr>
   712    791         }
   713    792         db_finalize(&q2);
   714    793       }
   715    794       if( g.perm.Hyperlink ){
   716         -      char *zPJ = db_get("short-project-name", 0);
   717         -      Blob projName;
   718         -      int jj;
   719         -      if( zPJ==0 ) zPJ = db_get("project-name", "unnamed");
   720         -      blob_zero(&projName);
   721         -      blob_append(&projName, zPJ, -1);
   722         -      blob_trim(&projName);
   723         -      zPJ = blob_str(&projName);
   724         -      for(jj=0; zPJ[jj]; jj++){
   725         -        if( (zPJ[jj]>0 && zPJ[jj]<' ') || strchr("\"*/:<>?\\|", zPJ[jj]) ){
   726         -          zPJ[jj] = '_';
   727         -        }
   728         -      }
   729         -      @ <tr><th>Timelines:</th><td>
   730         -      @   %z(href("%R/timeline?f=%!S&unhide",zUuid))family</a>
   731         -      if( zParent ){
   732         -        @ | %z(href("%R/timeline?p=%!S&unhide",zUuid))ancestors</a>
   733         -      }
   734         -      if( !isLeaf ){
   735         -        @ | %z(href("%R/timeline?d=%!S&unhide",zUuid))descendants</a>
   736         -      }
   737         -      if( zParent && !isLeaf ){
   738         -        @ | %z(href("%R/timeline?dp=%!S&unhide",zUuid))both</a>
   739         -      }
   740         -      db_prepare(&q2,"SELECT substr(tag.tagname,5) FROM tagxref, tag "
   741         -                     " WHERE rid=%d AND tagtype>0 "
   742         -                     "   AND tag.tagid=tagxref.tagid "
   743         -                     "   AND +tag.tagname GLOB 'sym-*'", rid);
   744         -      while( db_step(&q2)==SQLITE_ROW ){
   745         -        const char *zTagName = db_column_text(&q2, 0);
   746         -        @  | %z(href("%R/timeline?r=%T&unhide",zTagName))%h(zTagName)</a>
   747         -      }
   748         -      db_finalize(&q2);
   749         -
   750         -
   751         -      /* The Download: line */
   752         -      if( g.anon.Zip ){
   753         -        char *zUrl = mprintf("%R/tarball/%t-%S.tar.gz?uuid=%s",
   754         -                             zPJ, zUuid, zUuid);
   755         -        @ </td></tr>
   756         -        @ <tr><th>Downloads:</th><td>
   757         -        @ %z(href("%s",zUrl))Tarball</a>
   758         -        @ | %z(href("%R/zip/%t-%S.zip?uuid=%!S",zPJ,zUuid,zUuid))
   759         -        @         ZIP archive</a>
   760         -        fossil_free(zUrl);
   761         -      }
   762         -      @ </td></tr>
   763    795         @ <tr><th>Other&nbsp;Links:</th>
   764    796         @   <td>
   765         -      @     %z(href("%R/tree?ci=%!S",zUuid))files</a>
   766         -      @   | %z(href("%R/fileage?name=%!S",zUuid))file ages</a>
   767         -      @   | %z(href("%R/tree?nofiles&type=tree&ci=%!S",zUuid))folders</a>
   768         -      @   | %z(href("%R/artifact/%!S",zUuid))manifest</a>
          797  +      @   %z(href("%R/artifact/%!S",zUuid))manifest</a>
          798  +      @ | %z(href("%R/ci_tags/%!S",zUuid))tags</a>
   769    799         if( g.perm.Admin ){
   770    800           @   | %z(href("%R/mlink?ci=%!S",zUuid))mlink table</a>
   771    801         }
   772    802         if( g.anon.Write ){
   773    803           @   | %z(href("%R/ci_edit?r=%!S",zUuid))edit</a>
   774    804         }
   775    805         @   </td>
   776    806         @ </tr>
   777         -      blob_reset(&projName);
   778    807       }
   779    808       @ </table>
   780    809     }else{
   781    810       style_header("Check-in Information");
   782    811       login_anonymous_available();
   783    812     }
   784    813     db_finalize(&q1);
   785    814     render_backlink_graph(zUuid, "<div class=\"section\">References</div>\n");
   786         -  showTags(rid);
   787    815     @ <div class="section">Context</div>
   788    816     render_checkin_context(rid, 0);
   789    817     @ <div class="section">Changes</div>
   790    818     @ <div class="sectionmenu">
   791         -  verboseFlag = g.zPath[0]!='c';
   792         -  if( db_get_boolean("show-version-diffs", 0)==0 ){
   793         -    verboseFlag = !verboseFlag;
   794         -    zPage = "ci";
   795         -    zPageHide = "vinfo";
   796         -  }
   797         -  diffFlags = construct_diff_flags(verboseFlag, sideBySide);
          819  +  diffFlags = construct_diff_flags(diffType);
   798    820     zW = (diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
   799         -  if( verboseFlag ){
   800         -    @ %z(xhref("class='button'","%R/%s/%T",zPageHide,zName))
          821  +  if( diffType!=0 ){
          822  +    @ %z(chref("button","%R/%s/%T?diff=0",zPageHide,zName))\
   801    823       @ Hide&nbsp;Diffs</a>
   802         -    if( sideBySide ){
   803         -      @ %z(xhref("class='button'","%R/%s/%T?sbs=0%s",zPage,zName,zW))
   804         -      @ Unified&nbsp;Diffs</a>
   805         -    }else{
   806         -      @ %z(xhref("class='button'","%R/%s/%T?sbs=1%s",zPage,zName,zW))
   807         -      @ Side-by-Side&nbsp;Diffs</a>
   808         -    }
          824  +  }
          825  +  if( diffType!=1 ){
          826  +    @ %z(chref("button","%R/%s/%T?diff=1%s",zPage,zName,zW))\
          827  +    @ Unified&nbsp;Diffs</a>
          828  +  }
          829  +  if( diffType!=2 ){
          830  +    @ %z(chref("button","%R/%s/%T?diff=2%s",zPage,zName,zW))\
          831  +    @ Side-by-Side&nbsp;Diffs</a>
          832  +  }
          833  +  if( diffType!=0 ){
   809    834       if( *zW ){
   810         -      @ %z(xhref("class='button'","%R/%s/%T?sbs=%d",zPage,zName,sideBySide))
          835  +      @ %z(chref("button","%R/%s/%T",zPage,zName))
   811    836         @ Show&nbsp;Whitespace&nbsp;Changes</a>
   812    837       }else{
   813         -      @ %z(xhref("class='button'","%R/%s/%T?sbs=%d&w",zPage,zName,sideBySide))
          838  +      @ %z(chref("button","%R/%s/%T?w",zPage,zName))
   814    839         @ Ignore&nbsp;Whitespace</a>
   815    840       }
   816         -  }else{
   817         -    @ %z(xhref("class='button'","%R/%s/%T?sbs=0",zPage,zName))
   818         -    @ Show&nbsp;Unified&nbsp;Diffs</a>
   819         -    @ %z(xhref("class='button'","%R/%s/%T?sbs=1",zPage,zName))
   820         -    @ Show&nbsp;Side-by-Side&nbsp;Diffs</a>
   821    841     }
   822    842     if( zParent ){
   823         -    @ %z(xhref("class='button'","%R/vpatch?from=%!S&to=%!S",zParent,zUuid))
          843  +    @ %z(chref("button","%R/vpatch?from=%!S&to=%!S",zParent,zUuid))
   824    844       @ Patch</a>
   825    845     }
   826    846     if( g.perm.Admin ){
   827         -    @ %z(xhref("class='button'","%R/mlink?ci=%!S",zUuid))MLink Table</a>
          847  +    @ %z(chref("button","%R/mlink?ci=%!S",zUuid))MLink Table</a>
   828    848     }
   829    849     @</div>
   830    850     if( pRe ){
   831    851       @ <p><b>Only differences that match regular expression "%h(zRe)"
   832    852       @ are shown.</b></p>
   833    853     }
   834    854     db_prepare(&q3,
................................................................................
   849    869       int mperm = db_column_int(&q3, 1);
   850    870       const char *zOld = db_column_text(&q3,2);
   851    871       const char *zNew = db_column_text(&q3,3);
   852    872       const char *zOldName = db_column_text(&q3, 4);
   853    873       append_file_change_line(zName, zOld, zNew, zOldName, diffFlags,pRe,mperm);
   854    874     }
   855    875     db_finalize(&q3);
   856         -  append_diff_javascript(sideBySide);
          876  +  append_diff_javascript(diffType==2);
          877  +  cookie_render();
   857    878     style_footer();
   858    879   }
   859    880   
   860    881   /*
   861    882   ** WEBPAGE: winfo
   862    883   ** URL:  /winfo?name=UUID
   863    884   **
................................................................................
  1068   1089   ** to= query parameters.
  1069   1090   **
  1070   1091   ** Query parameters:
  1071   1092   **
  1072   1093   **   from=TAG        Left side of the comparison
  1073   1094   **   to=TAG          Right side of the comparison
  1074   1095   **   branch=TAG      Show all changes on a particular branch
  1075         -**   v=BOOLEAN       Default true.  If false, only list files that have changed
  1076         -**   sbs=BOOLEAN     Side-by-side diff if true.  Unified diff if false
         1096  +**   diff=INTEGER    0: none, 1: unified, 2: side-by-side
  1077   1097   **   glob=STRING     only diff files matching this glob
  1078   1098   **   dc=N            show N lines of context around each diff
  1079   1099   **   w=BOOLEAN       ignore whitespace when computing diffs
  1080   1100   **   nohdr           omit the description at the top of the page
  1081   1101   **
  1082   1102   **
  1083   1103   ** Show all differences between two check-ins.
  1084   1104   */
  1085   1105   void vdiff_page(void){
  1086   1106     int ridFrom, ridTo;
  1087         -  int verboseFlag;
  1088         -  int sideBySide;
         1107  +  int diffType = 0;        /* 0: none, 1: unified, 2: side-by-side */
  1089   1108     u64 diffFlags = 0;
  1090   1109     Manifest *pFrom, *pTo;
  1091   1110     ManifestFile *pFileFrom, *pFileTo;
  1092   1111     const char *zBranch;
  1093   1112     const char *zFrom;
  1094   1113     const char *zTo;
  1095   1114     const char *zRe;
  1096   1115     const char *zW;
  1097         -  const char *zVerbose;
  1098   1116     const char *zGlob;
  1099   1117     ReCompiled *pRe = 0;
  1100   1118     login_check_credentials();
  1101   1119     if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
  1102   1120     login_anonymous_available();
         1121  +  cookie_link_parameter("diff","diff","2");
         1122  +  diffType = atoi(PD("diff","2"));
         1123  +  cookie_render();
  1103   1124     zRe = P("regex");
  1104   1125     if( zRe ) re_compile(&pRe, zRe, 0);
  1105   1126     zBranch = P("branch");
  1106   1127     if( zBranch && zBranch[0] ){
  1107   1128       cgi_replace_parameter("from", mprintf("root:%s", zBranch));
  1108   1129       cgi_replace_parameter("to", zBranch);
  1109   1130     }
  1110   1131     pTo = vdiff_parse_manifest("to", &ridTo);
  1111   1132     if( pTo==0 ) return;
  1112   1133     pFrom = vdiff_parse_manifest("from", &ridFrom);
  1113   1134     if( pFrom==0 ) return;
  1114         -  sideBySide = !is_false(PD("sbs","1"));
  1115         -  zVerbose = P("v");
  1116         -  if( !zVerbose ){
  1117         -    zVerbose = P("verbose");
  1118         -  }
  1119         -  if( !zVerbose ){
  1120         -    zVerbose = P("detail"); /* deprecated */
  1121         -  }
  1122         -  verboseFlag = (zVerbose!=0) && !is_false(zVerbose);
  1123         -  if( !verboseFlag && sideBySide ) verboseFlag = 1;
  1124   1135     zGlob = P("glob");
  1125   1136     zFrom = P("from");
  1126   1137     zTo = P("to");
  1127   1138     if(zGlob && !*zGlob){
  1128   1139       zGlob = NULL;
  1129   1140     }
  1130         -  diffFlags = construct_diff_flags(verboseFlag, sideBySide);
         1141  +  diffFlags = construct_diff_flags(diffType);
  1131   1142     zW = (diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
  1132   1143     style_submenu_element("Path", "%R/timeline?me=%T&you=%T", zFrom, zTo);
  1133         -  if( sideBySide || verboseFlag ){
  1134         -    style_submenu_element("Hide Diff", "%R/vdiff?from=%T&to=%T&sbs=0%s%T%s",
         1144  +  if( diffType!=0 ){
         1145  +    style_submenu_element("Hide Diff", "%R/vdiff?from=%T&to=%T&diff=0%s%T%s",
  1135   1146                             zFrom, zTo,
  1136   1147                             zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW);
  1137   1148     }
  1138         -  if( !sideBySide ){
         1149  +  if( diffType!=2 ){
  1139   1150       style_submenu_element("Side-by-Side Diff",
  1140         -                          "%R/vdiff?from=%T&to=%T&sbs=1%s%T%s",
         1151  +                          "%R/vdiff?from=%T&to=%T&diff=2%s%T%s",
  1141   1152                             zFrom, zTo,
  1142   1153                             zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW);
  1143   1154     }
  1144         -  if( sideBySide || !verboseFlag ) {
         1155  +  if( diffType!=1 ) {
  1145   1156       style_submenu_element("Unified Diff",
  1146         -                          "%R/vdiff?from=%T&to=%T&sbs=0&v%s%T%s",
         1157  +                          "%R/vdiff?from=%T&to=%T&diff=1%s%T%s",
  1147   1158                             zFrom, zTo,
  1148   1159                             zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW);
  1149   1160     }
  1150   1161     style_submenu_element("Invert",
  1151         -                        "%R/vdiff?from=%T&to=%T&sbs=%d%s%s%T%s", zTo, zFrom,
  1152         -                        sideBySide, (verboseFlag && !sideBySide)?"&v":"",
         1162  +                        "%R/vdiff?from=%T&to=%T&%s%T%s", zTo, zFrom,
  1153   1163                           zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW);
  1154   1164     if( zGlob ){
  1155   1165       style_submenu_element("Clear glob",
  1156         -                          "%R/vdiff?from=%T&to=%T&sbs=%d%s%s", zFrom, zTo,
  1157         -                          sideBySide, (verboseFlag && !sideBySide)?"&v":"", zW);
         1166  +                          "%R/vdiff?from=%T&to=%T&%s", zFrom, zTo, zW);
  1158   1167     }else{
  1159   1168       style_submenu_element("Patch", "%R/vpatch?from=%T&to=%T%s", zFrom, zTo, zW);
  1160   1169     }
  1161         -  if( sideBySide || verboseFlag ){
         1170  +  if( diffType!=0 ){
  1162   1171       style_submenu_checkbox("w", "Ignore Whitespace", 0, 0);
  1163   1172     }
  1164   1173     style_header("Check-in Differences");
  1165   1174     if( P("nohdr")==0 ){
  1166   1175       @ <h2>Difference From:</h2><blockquote>
  1167   1176       checkin_description(ridFrom);
  1168   1177       @ </blockquote><h2>To:</h2><blockquote>
................................................................................
  1217   1226         }
  1218   1227         pFileFrom = manifest_file_next(pFrom, 0);
  1219   1228         pFileTo = manifest_file_next(pTo, 0);
  1220   1229       }
  1221   1230     }
  1222   1231     manifest_destroy(pFrom);
  1223   1232     manifest_destroy(pTo);
  1224         -  append_diff_javascript(sideBySide);
         1233  +  append_diff_javascript(diffType==2);
  1225   1234     style_footer();
  1226   1235   }
  1227   1236   
  1228   1237   #if INTERFACE
  1229   1238   /*
  1230   1239   ** Possible return values from object_description()
  1231   1240   */
................................................................................
  1276   1285   
  1277   1286     db_prepare(&q,
  1278   1287       "SELECT filename.name, datetime(event.mtime,toLocal()),"
  1279   1288       "       coalesce(event.ecomment,event.comment),"
  1280   1289       "       coalesce(event.euser,event.user),"
  1281   1290       "       b.uuid, mlink.mperm,"
  1282   1291       "       coalesce((SELECT value FROM tagxref"
  1283         -                    "  WHERE tagid=%d AND tagtype>0 AND rid=mlink.mid),'trunk'),"
         1292  +                  "  WHERE tagid=%d AND tagtype>0 AND rid=mlink.mid),'trunk'),"
  1284   1293       "       a.size"
  1285   1294       "  FROM mlink, filename, event, blob a, blob b"
  1286   1295       " WHERE filename.fnid=mlink.fnid"
  1287   1296       "   AND event.objid=mlink.mid"
  1288   1297       "   AND a.rid=mlink.fid"
  1289   1298       "   AND b.rid=mlink.mid"
  1290   1299       "   AND mlink.fid=%d"
................................................................................
  1319   1328         }else if( mPerm==PERM_EXE ){
  1320   1329           @ <li>Executable file
  1321   1330           objType |= OBJTYPE_EXE;
  1322   1331         }else{
  1323   1332           @ <li>File
  1324   1333         }
  1325   1334         objType |= OBJTYPE_CONTENT;
  1326         -      @ %z(href("%R/finfo?name=%T",zName))%h(zName)</a>
         1335  +      @ %z(href("%R/finfo?name=%T&m=%!S",zName,zUuid))%h(zName)</a>
  1327   1336         tag_private_status(rid);
  1328   1337         if( showDetail ){
  1329   1338           @ <ul>
  1330   1339         }
  1331   1340         prevName = fossil_strdup(zName);
  1332   1341       }
  1333   1342       if( showDetail ){
................................................................................
  1344   1353       if( zBr && zBr[0] ){
  1345   1354         @ on branch %z(href("%R/timeline?r=%T",zBr))%h(zBr)</a>
  1346   1355       }
  1347   1356       @ &mdash; %!W(zCom) (user:
  1348   1357       hyperlink_to_user(zUser,zDate,",");
  1349   1358       @ size: %d(szFile))
  1350   1359       if( g.perm.Hyperlink ){
  1351         -      @ %z(href("%R/finfo?name=%T&ci=%!S",zName,zVers))[ancestry]</a>
  1352   1360         @ %z(href("%R/annotate?filename=%T&checkin=%!S",zName,zVers))
  1353   1361         @ [annotate]</a>
  1354   1362         @ %z(href("%R/blame?filename=%T&checkin=%!S",zName,zVers))
  1355   1363         @ [blame]</a>
         1364  +      @ %z(href("%R/timeline?n=all&uf=%!S",zUuid))[check-ins&nbsp;using]</a>
  1356   1365       }
  1357   1366       cnt++;
  1358   1367       if( pDownloadName && blob_size(pDownloadName)==0 ){
  1359   1368         blob_append(pDownloadName, zName, -1);
  1360   1369       }
  1361   1370     }
  1362   1371     if( prevName && showDetail ){
................................................................................
  1547   1556   **      sbs=BOOLEAN      Turn side-by-side diffs on and off (default: on)
  1548   1557   **      verbose=BOOLEAN  Show more detail when describing artifacts
  1549   1558   **      w=BOOLEAN        Ignore whitespace
  1550   1559   */
  1551   1560   void diff_page(void){
  1552   1561     int v1, v2;
  1553   1562     int isPatch = P("patch")!=0;
  1554         -  int sideBySide = PB("sbs");
  1555         -  int verbose = PB("verbose");
         1563  +  int diffType;          /* 0: none, 1: unified,  2: side-by-side */
  1556   1564     char *zV1;
  1557   1565     char *zV2;
  1558   1566     const char *zRe;
  1559   1567     ReCompiled *pRe = 0;
  1560   1568     u64 diffFlags;
  1561   1569     u32 objdescFlags = 0;
         1570  +  int verbose = PB("verbose");
  1562   1571   
  1563   1572     login_check_credentials();
  1564   1573     if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
         1574  +  cookie_link_parameter("diff","diff","2");
         1575  +  diffType = atoi(PD("diff","2"));
         1576  +  cookie_render();
  1565   1577     if( P("from") && P("to") ){
  1566   1578       v1 = artifact_from_ci_and_filename(0, "from");
  1567   1579       v2 = artifact_from_ci_and_filename(0, "to");
  1568   1580     }else{
  1569   1581       Stmt q;
  1570   1582       v1 = name_to_rid_www("v1");
  1571   1583       v2 = name_to_rid_www("v2");
................................................................................
  1613   1625       blob_reset(&c1);
  1614   1626       blob_reset(&c2);
  1615   1627       return;
  1616   1628     }
  1617   1629   
  1618   1630     zV1 = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", v1);
  1619   1631     zV2 = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", v2);
  1620         -  diffFlags = construct_diff_flags(1, sideBySide) | DIFF_HTML;
         1632  +  diffFlags = construct_diff_flags(diffType) | DIFF_HTML;
  1621   1633   
  1622   1634     style_header("Diff");
  1623   1635     style_submenu_checkbox("w", "Ignore Whitespace", 0, 0);
  1624         -  style_submenu_checkbox("sbs", "Side-by-Side Diff", 0, 0);
         1636  +  if( diffType==2 ){
         1637  +    style_submenu_element("Unified Diff", "%R/fdiff?v1=%T&v2=%T&diff=1",
         1638  +                           P("v1"), P("v2"));
         1639  +  }else{
         1640  +    style_submenu_element("Side-by-side Diff", "%R/fdiff?v1=%T&v2=%T&diff=2",
         1641  +                           P("v1"), P("v2"));
         1642  +  }
  1625   1643     style_submenu_checkbox("verbose", "Verbose", 0, 0);
  1626         -  style_submenu_element("Patch", "%s/fdiff?v1=%T&v2=%T&patch",
  1627         -                        g.zTop, P("v1"), P("v2"));
         1644  +  style_submenu_element("Patch", "%R/fdiff?v1=%T&v2=%T&patch",
         1645  +                        P("v1"), P("v2"));
  1628   1646   
  1629   1647     if( P("smhdr")!=0 ){
  1630   1648       @ <h2>Differences From Artifact
  1631   1649       @ %z(href("%R/artifact/%!S",zV1))[%S(zV1)]</a> To
  1632   1650       @ %z(href("%R/artifact/%!S",zV2))[%S(zV2)]</a>.</h2>
  1633   1651     }else{
  1634   1652       @ <h2>Differences From
................................................................................
  1639   1657     }
  1640   1658     if( pRe ){
  1641   1659       @ <b>Only differences that match regular expression "%h(zRe)"
  1642   1660       @ are shown.</b>
  1643   1661     }
  1644   1662     @ <hr />
  1645   1663     append_diff(zV1, zV2, diffFlags, pRe);
  1646         -  append_diff_javascript(sideBySide);
         1664  +  append_diff_javascript(diffType);
  1647   1665     style_footer();
  1648   1666   }
  1649   1667   
  1650   1668   /*
  1651   1669   ** WEBPAGE: raw
  1652   1670   ** URL: /raw?name=ARTIFACTID&m=TYPE
  1653   1671   ** URL: /raw?ci=BRANCH&filename=NAME
................................................................................
  1907   1925         " WHERE iStart <= %d AND iEnd >= %d", n, n);
  1908   1926       if( db_step(&q)==SQLITE_ROW ){
  1909   1927         iStart = db_column_int(&q, 0);
  1910   1928         iEnd = db_column_int(&q, 1);
  1911   1929       }
  1912   1930       db_finalize(&q);
  1913   1931       for(i=0; z[i] && z[i]!='\n'; i++){}
  1914         -    if( n==iTop ) cgi_append_content("<span id=\"topln\">", -1);
         1932  +    if( n==iTop ) cgi_append_content("<span id=\"scrollToMe\">", -1);
  1915   1933       if( n==iStart ){
  1916   1934         cgi_append_content("<div class=\"selectedText\">",-1);
  1917   1935       }
  1918   1936       cgi_printf("%6d  ", n);
  1919   1937       if( i>0 ){
  1920   1938         char *zHtml = htmlize(z, i);
  1921   1939         cgi_append_content(zHtml, -1);
................................................................................
  1926   1944       else cgi_append_content("\n", 1);
  1927   1945       z += i;
  1928   1946       if( z[0]=='\n' ) z++;
  1929   1947     }
  1930   1948     if( n<iEnd ) cgi_printf("</div>");
  1931   1949     @ </pre>
  1932   1950     if( db_int(0, "SELECT EXISTS(SELECT 1 FROM lnos)") ){
  1933         -    @ <script>gebi('topln').scrollIntoView(true);</script>
         1951  +    style_load_one_js_file("scroll.js");
  1934   1952     }
  1935   1953   }
  1936   1954   
  1937   1955   
  1938   1956   /*
  1939   1957   ** WEBPAGE: artifact
  1940   1958   ** WEBPAGE: file
................................................................................
  2207   2225         }
  2208   2226       }
  2209   2227       if( strcmp(zModAction,"approve")==0 ){
  2210   2228         moderation_approve(rid);
  2211   2229       }
  2212   2230     }
  2213   2231     zTktTitle = db_table_has_column("repository", "ticket", "title" )
  2214         -      ? db_text("(No title)", "SELECT title FROM ticket WHERE tkt_uuid=%Q", zTktName)
         2232  +      ? db_text("(No title)", 
         2233  +                "SELECT title FROM ticket WHERE tkt_uuid=%Q", zTktName)
  2215   2234         : 0;
  2216   2235     style_header("Ticket Change Details");
  2217   2236     style_submenu_element("Raw", "%R/artifact/%s", zUuid);
  2218   2237     style_submenu_element("History", "%R/tkthistory/%s", zTktName);
  2219   2238     style_submenu_element("Page", "%R/tktview/%t", zTktName);
  2220   2239     style_submenu_element("Timeline", "%R/tkttimeline/%t", zTktName);
  2221   2240     if( P("plaintext") ){
................................................................................
  2237   2256     }
  2238   2257     @ <tr><th>Ticket:</th>
  2239   2258     @ <td>%z(href("%R/tktview/%s",zTktName))%s(zTktName)</a>
  2240   2259     if( zTktTitle ){
  2241   2260           @<br />%h(zTktTitle)
  2242   2261     }
  2243   2262     @</td></tr>
  2244         -  @ <tr><th>Date:</th><td>
         2263  +  @ <tr><th>User&nbsp;&amp;&nbsp;Date:</th><td>
         2264  +  hyperlink_to_user(pTktChng->zUser, zDate, " on ");
  2245   2265     hyperlink_to_date(zDate, "</td></tr>");
  2246         -  @ <tr><th>User:</th><td>
  2247         -  hyperlink_to_user(pTktChng->zUser, zDate, "</td></tr>");
  2248   2266     @ </table>
  2249   2267     free(zDate);
  2250   2268     free(zTktTitle);
  2251   2269   
  2252   2270     if( g.perm.ModTkt && modPending ){
  2253   2271       @ <div class="section">Moderation</div>
  2254   2272       @ <blockquote>
................................................................................
  2350   2368       ainfo_page();
  2351   2369     }else
  2352   2370     {
  2353   2371       artifact_page();
  2354   2372     }
  2355   2373   }
  2356   2374   
  2357         -/*
  2358         -** Generate HTML that will present the user with a selection of
  2359         -** potential background colors for timeline entries.
  2360         -*/
  2361         -void render_color_chooser(
  2362         -  int fPropagate,             /* Default value for propagation */
  2363         -  const char *zDefaultColor,  /* The current default color */
  2364         -  const char *zIdPropagate,   /* ID of form element checkbox.  NULL for none */
  2365         -  const char *zId,            /* The ID of the form element */
  2366         -  const char *zIdCustom       /* ID of text box for custom color */
  2367         -){
  2368         -  static const struct SampleColors {
  2369         -     const char *zCName;
  2370         -     const char *zColor;
  2371         -  } aColor[] = {
  2372         -     { "(none)",  "" },
  2373         -     { "#f2dcdc", 0 },
  2374         -     { "#bde5d6", 0 },
  2375         -     { "#a0a0a0", 0 },
  2376         -     { "#b0b0b0", 0 },
  2377         -     { "#c0c0c0", 0 },
  2378         -     { "#d0d0d0", 0 },
  2379         -     { "#e0e0e0", 0 },
  2380         -
  2381         -     { "#c0fff0", 0 },
  2382         -     { "#c0f0ff", 0 },
  2383         -     { "#d0c0ff", 0 },
  2384         -     { "#ffc0ff", 0 },
  2385         -     { "#ffc0d0", 0 },
  2386         -     { "#fff0c0", 0 },
  2387         -     { "#f0ffc0", 0 },
  2388         -     { "#c0ffc0", 0 },
  2389         -
  2390         -     { "#a8d3c0", 0 },
  2391         -     { "#a8c7d3", 0 },
  2392         -     { "#aaa8d3", 0 },
  2393         -     { "#cba8d3", 0 },
  2394         -     { "#d3a8bc", 0 },
  2395         -     { "#d3b5a8", 0 },
  2396         -     { "#d1d3a8", 0 },
  2397         -     { "#b1d3a8", 0 },
  2398         -
  2399         -     { "#8eb2a1", 0 },
  2400         -     { "#8ea7b2", 0 },
  2401         -     { "#8f8eb2", 0 },
  2402         -     { "#ab8eb2", 0 },
  2403         -     { "#b28e9e", 0 },
  2404         -     { "#b2988e", 0 },
  2405         -     { "#b0b28e", 0 },
  2406         -     { "#95b28e", 0 },
  2407         -
  2408         -     { "#80d6b0", 0 },
  2409         -     { "#80bbd6", 0 },
  2410         -     { "#8680d6", 0 },
  2411         -     { "#c680d6", 0 },
  2412         -     { "#d680a6", 0 },
  2413         -     { "#d69b80", 0 },
  2414         -     { "#d1d680", 0 },
  2415         -     { "#91d680", 0 },
  2416         -
  2417         -
  2418         -     { "custom",  "##" },
  2419         -  };
  2420         -  int nColor = count(aColor)-1;
  2421         -  int stdClrFound = 0;
  2422         -  int i;
  2423         -
  2424         -  if( zIdPropagate ){
  2425         -    @ <div><label>
  2426         -    if( fPropagate ){
  2427         -      @ <input type="checkbox" name="%s(zIdPropagate)" checked="checked" />
  2428         -    }else{
  2429         -      @ <input type="checkbox" name="%s(zIdPropagate)" />
  2430         -    }
  2431         -    @ Propagate color to descendants</label></div>
  2432         -  }
  2433         -  @ <table border="0" cellpadding="0" cellspacing="1" class="colorpicker">
  2434         -  @ <tr>
  2435         -  for(i=0; i<nColor; i++){
  2436         -    const char *zClr = aColor[i].zColor;
  2437         -    if( zClr==0 ) zClr = aColor[i].zCName;
  2438         -    if( zClr[0] ){
  2439         -      @ <td style="background-color: %h(zClr);">
  2440         -    }else{
  2441         -      @ <td>
  2442         -    }
  2443         -    @ <label>
  2444         -    if( fossil_strcmp(zDefaultColor, zClr)==0 ){
  2445         -      @ <input type="radio" name="%s(zId)" value="%h(zClr)"
  2446         -      @  checked="checked" />
  2447         -      stdClrFound=1;
  2448         -    }else{
  2449         -      @ <input type="radio" name="%s(zId)" value="%h(zClr)" />
  2450         -    }
  2451         -    @ %h(aColor[i].zCName)</label></td>
  2452         -    if( (i%8)==7 && i+1<nColor ){
  2453         -      @ </tr><tr>
  2454         -    }
  2455         -  }
  2456         -  @ </tr><tr>
  2457         -  if( stdClrFound ){
  2458         -    @ <td colspan="6"><label>
  2459         -    @ <input type="radio" name="%s(zId)" value="%h(aColor[nColor].zColor)"
  2460         -    @  onclick="gebi('%s(zIdCustom)').select();" />
  2461         -  }else{
  2462         -    @ <td style="background-color: %h(zDefaultColor);" colspan="6"><label>
  2463         -    @ <input type="radio" name="%s(zId)" value="%h(aColor[nColor].zColor)"
  2464         -    @  checked="checked" onclick="gebi('%s(zIdCustom)').select();" />
  2465         -  }
  2466         -  @ %h(aColor[i].zCName)</label>&nbsp;
  2467         -  @ <input type="text" name="%s(zIdCustom)"
  2468         -  @  id="%s(zIdCustom)" class="checkinUserColor"
  2469         -  @  value="%h(stdClrFound?"":zDefaultColor)"
  2470         -  @  onfocus="this.form.elements['%s(zId)'][%d(nColor)].checked = true;"
  2471         -  @  onload="this.blur();"
  2472         -  @  onblur="this.parentElement.style.backgroundColor = this.value ? ('#'+this.value.replace('#','')) : '';" />
  2473         -  @ </td>
  2474         -  @ </tr>
  2475         -  @ </table>
  2476         -}
  2477         -
  2478   2375   /*
  2479   2376   ** Do a comment comparison.
  2480   2377   **
  2481   2378   ** +  Leading and trailing whitespace are ignored.
  2482   2379   ** +  \r\n characters compare equal to \n
  2483   2380   **
  2484   2381   ** Return true if equal and false if not equal.
................................................................................
  2628   2525   */
  2629   2526   int is_datetime(const char* zDate){
  2630   2527     return db_int(0, "SELECT datetime(%Q) NOT NULL", zDate);
  2631   2528   }
  2632   2529   
  2633   2530   /*
  2634   2531   ** WEBPAGE: ci_edit
  2635         -** URL:  /ci_edit?r=RID&c=NEWCOMMENT&u=NEWUSER
         2532  +**
         2533  +** Edit a check-in.  (Check-ins are immutable and do not really change.
         2534  +** This page really creates supplemental tags that affect the display
         2535  +** of the check-in.)
         2536  +**
         2537  +** Query parmeters:
         2538  +**
         2539  +**     rid=INTEGER        Record ID of the check-in to edit (REQUIRED)
         2540  +**
         2541  +** POST parameters after pressing "Perview", "Cancel", or "Apply":
  2636   2542   **
  2637         -** Present a dialog for updating properties of a check-in.
         2543  +**     c=TEXT             New check-in comment
         2544  +**     u=TEXT             New user name
         2545  +**     newclr             Apply a background color
         2546  +**     clr=TEXT           New background color (only if newclr)
         2547  +**     pclr               Propagate new background color (only if newclr)
         2548  +**     dt=TEXT            New check-in date/time (ISO8610 format)
         2549  +**     newtag             Add a new tag to the check-in
         2550  +**     tagname=TEXT       Name of the new tag to be added (only if newtag)
         2551  +**     newbr              Put the check-in on a new branch
         2552  +**     brname=TEXT        Name of the new branch (only if newbr)
         2553  +**     close              Close this check-in
         2554  +**     hide               Hide this check-in
         2555  +**     cNNN               Cancel tag with tagid=NNN
  2638   2556   **
  2639         -**     *  The check-in user
  2640         -**     *  The check-in comment
  2641         -**     *  The check-in time and date
  2642         -**     *  The background color.
  2643         -**     *  Add and remove tags
         2557  +**     cancel             Cancel the edit.  Return to the check-in view
         2558  +**     preview            Show a preview of the edited check-in comment
         2559  +**     apply              Apply changes
  2644   2560   */
  2645   2561   void ci_edit_page(void){
  2646   2562     int rid;
  2647   2563     const char *zComment;         /* Current comment on the check-in */
  2648   2564     const char *zNewComment;      /* Revised check-in comment */
  2649   2565     const char *zUser;            /* Current user for the check-in */
  2650   2566     const char *zNewUser;         /* Revised user */
  2651   2567     const char *zDate;            /* Current date of the check-in */
  2652   2568     const char *zNewDate;         /* Revised check-in date */
  2653         -  const char *zColor;
  2654         -  const char *zNewColor;
  2655         -  const char *zNewTagFlag;
  2656         -  const char *zNewTag;
  2657         -  const char *zNewBrFlag;
  2658         -  const char *zNewBranch;
  2659         -  const char *zCloseFlag;
  2660         -  const char *zHideFlag;
         2569  +  const char *zNewColorFlag;    /* "checked" if "Change color" is checked */
         2570  +  const char *zColor;           /* Current background color */
         2571  +  const char *zNewColor;        /* Revised background color */
         2572  +  const char *zNewTagFlag;      /* "checked" if "Add tag" is checked */
         2573  +  const char *zNewTag;          /* Name of the new tag */
         2574  +  const char *zNewBrFlag;       /* "checked" if "New branch" is checked */
         2575  +  const char *zNewBranch;       /* Name of the new branch */
         2576  +  const char *zCloseFlag;       /* "checked" if "Close" is checked */
         2577  +  const char *zHideFlag;        /* "checked" if "Hide" is checked */
  2661   2578     int fPropagateColor;          /* True if color propagates before edit */
  2662   2579     int fNewPropagateColor;       /* True if color propagates after edit */
  2663   2580     int fHasHidden = 0;           /* True if hidden tag already set */
  2664   2581     int fHasClosed = 0;           /* True if closed tag already set */
  2665         -  const char *zChngTime = 0;     /* Value of chngtime= query param, if any */
         2582  +  const char *zChngTime = 0;    /* Value of chngtime= query param, if any */
  2666   2583     char *zUuid;
  2667   2584     Blob comment;
  2668   2585     char *zBranchName = 0;
  2669   2586     Stmt q;
  2670   2587   
  2671   2588     login_check_credentials();
  2672   2589     if( !g.perm.Write ){ login_needed(g.anon.Write); return; }
  2673   2590     rid = name_to_typed_rid(P("r"), "ci");
  2674   2591     zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
  2675   2592     zComment = db_text(0, "SELECT coalesce(ecomment,comment)"
  2676   2593                           "  FROM event WHERE objid=%d", rid);
  2677   2594     if( zComment==0 ) fossil_redirect_home();
  2678   2595     if( P("cancel") ){
  2679         -    cgi_redirectf("ci?name=%s", zUuid);
         2596  +    cgi_redirectf("%R/ci/%S", zUuid);
  2680   2597     }
  2681   2598     if( g.perm.Setup ) zChngTime = P("chngtime");
  2682   2599     zNewComment = PD("c",zComment);
  2683   2600     zUser = db_text(0, "SELECT coalesce(euser,user)"
  2684   2601                        "  FROM event WHERE objid=%d", rid);
  2685   2602     if( zUser==0 ) fossil_redirect_home();
  2686   2603     zNewUser = PDT("u",zUser);
................................................................................
  2687   2604     zDate = db_text(0, "SELECT datetime(mtime)"
  2688   2605                        "  FROM event WHERE objid=%d", rid);
  2689   2606     if( zDate==0 ) fossil_redirect_home();
  2690   2607     zNewDate = PDT("dt",zDate);
  2691   2608     zColor = db_text("", "SELECT bgcolor"
  2692   2609                           "  FROM event WHERE objid=%d", rid);
  2693   2610     zNewColor = PDT("clr",zColor);
  2694         -  if( fossil_strcmp(zNewColor,"##")==0 ){
  2695         -    zNewColor = PT("clrcust");
  2696         -  }
  2697   2611     fPropagateColor = db_int(0, "SELECT tagtype FROM tagxref"
  2698   2612                                 " WHERE rid=%d AND tagid=%d",
  2699   2613                                 rid, TAG_BGCOLOR)==2;
  2700   2614     fNewPropagateColor = P("clr")!=0 ? P("pclr")!=0 : fPropagateColor;
         2615  +  zNewColorFlag = P("newclr") ? " checked" : "";
  2701   2616     zNewTagFlag = P("newtag") ? " checked" : "";
  2702   2617     zNewTag = PDT("tagname","");
  2703   2618     zNewBrFlag = P("newbr") ? " checked" : "";
  2704   2619     zNewBranch = PDT("brname","");
  2705   2620     zCloseFlag = P("close") ? " checked" : "";
  2706   2621     zHideFlag = P("hide") ? " checked" : "";
  2707   2622     if( P("apply") ){
................................................................................
  2709   2624       char *zNow;
  2710   2625   
  2711   2626       login_verify_csrf_secret();
  2712   2627       blob_zero(&ctrl);
  2713   2628       zNow = date_in_standard_format(zChngTime ? zChngTime : "now");
  2714   2629       blob_appendf(&ctrl, "D %s\n", zNow);
  2715   2630       init_newtags();
  2716         -    if( zNewColor[0]
         2631  +    if( zNewColorFlag[0]
         2632  +     && zNewColor[0]
  2717   2633        && (fPropagateColor!=fNewPropagateColor
  2718   2634                || fossil_strcmp(zColor,zNewColor)!=0)
  2719         -    ) add_color(zNewColor,fNewPropagateColor);
  2720         -    if( zNewColor[0]==0 && zColor[0]!=0 ) cancel_color();
         2635  +    ){
         2636  +      add_color(zNewColor,fNewPropagateColor);
         2637  +    }
  2721   2638       if( comment_compare(zComment,zNewComment)==0 ) add_comment(zNewComment);
  2722   2639       if( fossil_strcmp(zDate,zNewDate)!=0 ) add_date(zNewDate);
  2723   2640       if( fossil_strcmp(zUser,zNewUser)!=0 ) add_user(zNewUser);
  2724   2641       db_prepare(&q,
  2725   2642          "SELECT tag.tagid, tagname FROM tagxref, tag"
  2726   2643          " WHERE tagxref.rid=%d AND tagtype>0 AND tagxref.tagid=tag.tagid",
  2727   2644          rid
................................................................................
  2735   2652       }
  2736   2653       db_finalize(&q);
  2737   2654       if( zHideFlag[0] ) hide_branch();
  2738   2655       if( zCloseFlag[0] ) close_leaf(rid);
  2739   2656       if( zNewTagFlag[0] && zNewTag[0] ) add_tag(zNewTag);
  2740   2657       if( zNewBrFlag[0] && zNewBranch[0] ) change_branch(rid,zNewBranch);
  2741   2658       apply_newtags(&ctrl, rid, zUuid);
  2742         -    cgi_redirectf("ci?name=%s", zUuid);
         2659  +    cgi_redirectf("%R/ci/%S", zUuid);
  2743   2660     }
  2744   2661     blob_zero(&comment);
  2745   2662     blob_append(&comment, zNewComment, -1);
  2746   2663     zUuid[10] = 0;
  2747   2664     style_header("Edit Check-in [%s]", zUuid);
  2748         -  /*
  2749         -  ** chgcbn/chgbn: Handle change of (checkbox for) branch name in
  2750         -  ** remaining of form.
  2751         -  */
  2752         -  @ <script>
  2753         -  @ function chgcbn(checked, branch){
  2754         -  @   val = gebi('brname').value.trim();
  2755         -  @   if( !val || !checked ) val = branch;
  2756         -  @   if( checked ) gebi('brname').select();
  2757         -  @   gebi('hbranch').textContent = val;
  2758         -  @   cidbrid = document.getElementById('cbranch');
  2759         -  @   if( cidbrid ) cidbrid.textContent = val;
  2760         -  @ }
  2761         -  @ function chgbn(val, branch){
  2762         -  @   if( !val ) val = branch;
  2763         -  @   gebi('newbr').checked = (val!=branch);
  2764         -  @   gebi('hbranch').textContent = val;
  2765         -  @   cidbrid = document.getElementById('cbranch');
  2766         -  @   if( cidbrid ) cidbrid.textContent = val;
  2767         -  @ }
  2768         -  @ </script>
  2769   2665     if( P("preview") ){
  2770   2666       Blob suffix;
  2771   2667       int nTag = 0;
  2772   2668       @ <b>Preview:</b>
  2773   2669       @ <blockquote>
  2774   2670       @ <table border=0>
  2775         -    if( zNewColor && zNewColor[0] ){
         2671  +    if( zNewColorFlag[0] && zNewColor && zNewColor[0] ){
  2776   2672         @ <tr><td style="background-color: %h(zNewColor);">
         2673  +    }else if( zColor[0] ){
         2674  +      @ <tr><td style="background-color: %h(zColor);">
  2777   2675       }else{
  2778   2676         @ <tr><td>
  2779   2677       }
  2780   2678       @ %!W(blob_str(&comment))
  2781   2679       blob_zero(&suffix);
  2782   2680       blob_appendf(&suffix, "(user: %h", zNewUser);
  2783   2681       db_prepare(&q, "SELECT substr(tagname,5) FROM tagxref, tag"
................................................................................
  2830   2728     if( zChngTime ){
  2831   2729       @ <tr><th align="right" valign="top">Timestamp of this change:</th>
  2832   2730       @ <td valign="top">
  2833   2731       @   <input type="text" name="chngtime" size="20" value="%h(zChngTime)" />
  2834   2732       @ </td></tr>
  2835   2733     }
  2836   2734   
  2837         -  @ <tr><th align="right" valign="top">Background Color:</th>
         2735  +  @ <tr><th align="right" valign="top">Background&nbsp;Color:</th>
  2838   2736     @ <td valign="top">
  2839         -  render_color_chooser(fNewPropagateColor, zNewColor, "pclr", "clr", "clrcust");
         2737  +  @ <div><label><input type='checkbox' name='newclr'%s(zNewColorFlag) />
         2738  +  @ Change background color: \
         2739  +  @ <input type='color' name='clr'\
         2740  +  @ value='%s(zNewColor[0]?zNewColor:"#808080")'></label></div>
         2741  +  @ <div><label>
         2742  +  if( fNewPropagateColor ){
         2743  +    @ <input type="checkbox" name="pclr" checked="checked" />
         2744  +  }else{
         2745  +    @ <input type="checkbox" name="pclr" />
         2746  +  }
         2747  +  @ Propagate color to descendants</label></div>
  2840   2748     @ </td></tr>
  2841   2749   
  2842   2750     @ <tr><th align="right" valign="top">Tags:</th>
  2843   2751     @ <td valign="top">
  2844   2752     @ <label><input type="checkbox" id="newtag" name="newtag"%s(zNewTagFlag) />
  2845   2753     @ Add the following new tag name to this check-in:</label>
  2846         -  @ <input type="text" style="width:15;" name="tagname" value="%h(zNewTag)"
  2847         -  @ onkeyup="gebi('newtag').checked=!!this.value" />
         2754  +  @ <input type="text" size='15' name="tagname" value="%h(zNewTag)" \
         2755  +  @ id='tagname' />
  2848   2756     zBranchName = db_text(0, "SELECT value FROM tagxref, tag"
  2849   2757        " WHERE tagxref.rid=%d AND tagtype>0 AND tagxref.tagid=tag.tagid"
  2850   2758        " AND tagxref.tagid=%d", rid, TAG_BRANCH);
  2851   2759     db_prepare(&q,
  2852   2760        "SELECT tag.tagid, tagname, tagxref.value FROM tagxref, tag"
  2853   2761        " WHERE tagxref.rid=%d AND tagtype>0 AND tagxref.tagid=tag.tagid"
  2854   2762        " ORDER BY CASE WHEN tagname GLOB 'sym-*' THEN substr(tagname,5)"
................................................................................
  2891   2799       zBranchName = db_get("main-branch", "trunk");
  2892   2800     }
  2893   2801     if( !zNewBranch || !zNewBranch[0]){
  2894   2802       zNewBranch = zBranchName;
  2895   2803     }
  2896   2804     @ <tr><th align="right" valign="top">Branching:</th>
  2897   2805     @ <td valign="top">
  2898         -  @ <label><input id="newbr" type="checkbox" name="newbr"%s(zNewBrFlag)
  2899         -  @ onchange="chgcbn(this.checked,'%h(zBranchName)')" />
         2806  +  @ <label><input id="newbr" type="checkbox" name="newbr" \
         2807  +  @ data-branch='%h(zBranchName)'%s(zNewBrFlag) />
  2900   2808     @ Make this check-in the start of a new branch named:</label>
  2901         -  @ <input id="brname" type="text" style="width:15;" name="brname"
  2902         -  @ value="%h(zNewBranch)"
  2903         -  @ onkeyup="chgbn(this.value.trim(),'%h(zBranchName)')" /></td></tr>
         2809  +  @ <input id="brname" type="text" style="width:15;" name="brname" \
         2810  +  @ value="%h(zNewBranch)" /></td></tr>
  2904   2811     if( !fHasHidden ){
  2905   2812       @ <tr><th align="right" valign="top">Branch Hiding:</th>
  2906   2813       @ <td valign="top">
  2907   2814       @ <label><input type="checkbox" id="hidebr" name="hide"%s(zHideFlag) />
  2908   2815       @ Hide branch
  2909   2816       @ <span style="font-weight:bold" id="hbranch">%h(zBranchName)</span>
  2910   2817       @ from the timeline starting from this check-in</label>
................................................................................
  2928   2835         @ </td></tr>
  2929   2836       }
  2930   2837     }
  2931   2838     if( zBranchName ) fossil_free(zBranchName);
  2932   2839   
  2933   2840   
  2934   2841     @ <tr><td colspan="2">
         2842  +  @ <input type="submit" name="cancel" value="Cancel" />
  2935   2843     @ <input type="submit" name="preview" value="Preview" />
  2936         -  @ <input type="submit" name="apply" value="Apply Changes" />
  2937         -  @ <input type="submit" name="cancel" value="Cancel" />
         2844  +  if( P("preview") ){
         2845  +    @ <input type="submit" name="apply" value="Apply Changes" />
         2846  +  }
  2938   2847     @ </td></tr>
  2939   2848     @ </table>
  2940   2849     @ </div></form>
         2850  +  style_load_one_js_file("ci_edit.js");
  2941   2851     style_footer();
  2942   2852   }
  2943   2853   
  2944   2854   /*
  2945   2855   ** Prepare an ammended commit comment.  Let the user modify it using the
  2946   2856   ** editor specified in the global_config table or either
  2947   2857   ** the VISUAL or EDITOR environment variable.
................................................................................
  3114   3024       cancel_color();
  3115   3025     }
  3116   3026     if( fEditComment ){
  3117   3027       prepare_amend_comment(&comment, zComment, zUuid);
  3118   3028       zNewComment = blob_str(&comment);
  3119   3029     }else if( zComFile ){
  3120   3030       blob_zero(&comment);
  3121         -    blob_read_from_file(&comment, zComFile);
         3031  +    blob_read_from_file(&comment, zComFile, ExtFILE);
  3122   3032       blob_to_utf8_no_bom(&comment, 1);
  3123   3033       zNewComment = blob_str(&comment);
  3124   3034     }
  3125   3035     if( zNewComment && zNewComment[0]
  3126   3036         && comment_compare(zComment,zNewComment)==0 ) add_comment(zNewComment);
  3127   3037     if( zNewDate && zNewDate[0] && fossil_strcmp(zDate,zNewDate)!=0 ){
  3128   3038       if( is_datetime(zNewDate) ){

Changes to src/json.c.

  1606   1606         }
  1607   1607         assert( 0 && "Alloc error.");
  1608   1608         return NULL;
  1609   1609       }
  1610   1610     }
  1611   1611     cson_value_free(colNamesV);
  1612   1612     if(warnMsg){
  1613         -    json_warn( FSL_JSON_W_ROW_TO_JSON_FAILED, warnMsg );
         1613  +    json_warn( FSL_JSON_W_ROW_TO_JSON_FAILED, "%s", warnMsg );
  1614   1614     }
  1615   1615     return cson_array_value(a);
  1616   1616   }
  1617   1617   
  1618   1618   /*
  1619   1619   ** Works just like json_stmt_to_array_of_obj(), but each row in the
  1620   1620   ** result set is represented as an Array of values instead of an
................................................................................
  1926   1926     zTmp = db_get("project-name",NULL);
  1927   1927     cson_object_set(jo, "projectName", json_new_string(zTmp));
  1928   1928     free(zTmp);
  1929   1929     zTmp = db_get("project-description",NULL);
  1930   1930     cson_object_set(jo, "projectDescription", json_new_string(zTmp));
  1931   1931     free(zTmp);
  1932   1932     zTmp = NULL;
  1933         -  fsize = file_size(g.zRepositoryName);
  1934         -  cson_object_set(jo, "repositorySize", cson_value_new_integer((cson_int_t)fsize));
         1933  +  fsize = file_size(g.zRepositoryName, ExtFILE);
         1934  +  cson_object_set(jo, "repositorySize", 
         1935  +                  cson_value_new_integer((cson_int_t)fsize));
  1935   1936   
  1936   1937     if(full){
  1937   1938       n = db_int(0, "SELECT count(*) FROM blob");
  1938   1939       m = db_int(0, "SELECT count(*) FROM delta");
  1939   1940       cson_object_set(jo, "blobCount", cson_value_new_integer((cson_int_t)n));
  1940   1941       cson_object_set(jo, "deltaCount", cson_value_new_integer((cson_int_t)m));
  1941   1942       if( n>0 ){

Changes to src/json_branch.c.

   138    138         cson_array_append(list,v);
   139    139       }else if(!sawConversionError){
   140    140         sawConversionError = mprintf("Column-to-json failed @ %s:%d",
   141    141                                      __FILE__,__LINE__);
   142    142       }
   143    143     }
   144    144     if( sawConversionError ){
   145         -    json_warn(FSL_JSON_W_COL_TO_JSON_FAILED,sawConversionError);
          145  +    json_warn(FSL_JSON_W_COL_TO_JSON_FAILED,"%s",sawConversionError);
   146    146       free(sawConversionError);
   147    147     }
   148    148     return payV;
   149    149   }
   150    150   
   151    151   /*
   152    152   ** Parameters for the create-branch operation.
................................................................................
   357    357       }else{
   358    358         opt.isPrivate = 0;
   359    359       }
   360    360     }
   361    361   
   362    362     rc = json_branch_new( &opt, &rid );
   363    363     if(rc){
   364         -    json_set_err(rc, opt.rcErrMsg);
          364  +    json_set_err(rc, "%s", opt.rcErrMsg);
   365    365       goto error;
   366    366     }
   367    367     assert(0 != rid);
   368    368     payV = cson_value_new_object();
   369    369     pay = cson_value_get_object(payV);
   370    370   
   371    371     cson_object_set(pay,"name",json_new_string(opt.zName));

Changes to src/json_dir.c.

    23     23   #include "json_detail.h"
    24     24   #endif
    25     25   
    26     26   static cson_value * json_page_dir_list();
    27     27   /*
    28     28   ** Mapping of /json/wiki/XXX commands/paths to callbacks.
    29     29   */
           30  +#if 0 /* TODO: Not used? */
    30     31   static const JsonPageDef JsonPageDefs_Dir[] = {
    31     32   /* Last entry MUST have a NULL name. */
    32     33   {NULL,NULL,0}
    33     34   };
           35  +#endif
    34     36   
    35     37   #if 0 /* TODO: Not used? */
    36     38   static char const * json_dir_path_extra(){
    37     39     static char const * zP = NULL;
    38     40     if( !zP ){
    39     41       zP = g.zExtra;
    40     42       while(zP && *zP && ('/'==*zP)){

Changes to src/json_status.c.

   112    112       if( isDeleted ){
   113    113         zStatus = "deleted";
   114    114       }else if( isNew ){
   115    115         zStatus = "new" /* maintenance reminder: MUST come
   116    116                            BEFORE the isChnged checks. */;
   117    117       }else if( isRenamed ){
   118    118         zStatus = "renamed";
   119         -    }else if( !file_wd_isfile_or_link(zFullName) ){
          119  +    }else if( !file_isfile_or_link(zFullName) ){
   120    120         if( file_access(zFullName, F_OK)==0 ){
   121    121           zStatus = "notAFile";
   122    122           ++nErr;
   123    123         }else{
   124    124           zStatus = "missing";
   125    125           ++nErr;
   126    126         }

Changes to src/linenoise.c.

     1         -/* linenoise.c -- VERSION 1.0
     2         - *
     3         - * Guerrilla line editing library against the idea that a line editing lib
     4         - * needs to be 20,000 lines of C code.
            1  +/* linenoise.c -- guerrilla line editing library against the idea that a
            2  + * line editing lib needs to be 20,000 lines of C code.
     5      3    *
     6      4    * You can find the latest source code at:
     7      5    *
     8      6    *   http://github.com/antirez/linenoise
     9      7    *
    10      8    * Does a number of crazy assumptions that happen to be true in 99.9999% of
    11      9    * the 2010 UNIX computers around.
    12     10    *
    13     11    * ------------------------------------------------------------------------
    14     12    *
    15         - * Copyright (c) 2010-2014, Salvatore Sanfilippo <antirez at gmail dot com>
           13  + * Copyright (c) 2010-2016, Salvatore Sanfilippo <antirez at gmail dot com>
    16     14    * Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
    17     15    *
    18     16    * All rights reserved.
    19     17    *
    20     18    * Redistribution and use in source and binary forms, with or without
    21     19    * modification, are permitted provided that the following conditions are
    22     20    * met:
................................................................................
   103    101    *    Sequence: ESC [ 2 J
   104    102    *    Effect: clear the whole screen
   105    103    *
   106    104    */
   107    105   
   108    106   #include <termios.h>
   109    107   #include <unistd.h>
   110         -#include <stdarg.h>
   111    108   #include <stdlib.h>
   112    109   #include <stdio.h>
   113    110   #include <errno.h>
   114    111   #include <string.h>
   115    112   #include <stdlib.h>
   116    113   #include <ctype.h>
          114  +#include <sys/stat.h>
   117    115   #include <sys/types.h>
   118    116   #include <sys/ioctl.h>
   119    117   #include <unistd.h>
   120    118   #include "linenoise.h"
   121         -#include "sqlite3.h"
   122    119   
   123    120   #define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100
   124    121   #define LINENOISE_MAX_LINE 4096
   125         -static const char *unsupported_term[] = {"dumb","cons25","emacs",NULL};
          122  +static char *unsupported_term[] = {"dumb","cons25","emacs",NULL};
   126    123   static linenoiseCompletionCallback *completionCallback = NULL;
          124  +static linenoiseHintsCallback *hintsCallback = NULL;
          125  +static linenoiseFreeHintsCallback *freeHintsCallback = NULL;
   127    126   
   128    127   static struct termios orig_termios; /* In order to restore at exit.*/
   129    128   static int rawmode = 0; /* For atexit() function to check if restore is needed*/
   130    129   static int mlmode = 0;  /* Multi line mode. Default is single line. */
   131    130   static int atexit_registered = 0; /* Register atexit just 1 time. */
   132    131   static int history_max_len = LINENOISE_DEFAULT_HISTORY_MAX_LEN;
   133    132   static int history_len = 0;
................................................................................
   176    175   static void linenoiseAtExit(void);
   177    176   int linenoiseHistoryAdd(const char *line);
   178    177   static void refreshLine(struct linenoiseState *l);
   179    178   
   180    179   /* Debugging macro. */
   181    180   #if 0
   182    181   FILE *lndebug_fp = NULL;
   183         -#define lndebug(fmt, arg1) \
          182  +#define lndebug(...) \
   184    183       do { \
   185    184           if (lndebug_fp == NULL) { \
   186    185               lndebug_fp = fopen("/tmp/lndebug.txt","a"); \
   187    186               fprintf(lndebug_fp, \
   188    187               "[%d %d %d] p: %d, rows: %d, rpos: %d, max: %d, oldmax: %d\n", \
   189    188               (int)l->len,(int)l->pos,(int)l->oldpos,plen,rows,rpos, \
   190    189               (int)l->maxrows,old_rows); \
   191    190           } \
   192         -        fprintf(lndebug_fp, ", " fmt, arg1); \
          191  +        fprintf(lndebug_fp, ", " __VA_ARGS__); \
   193    192           fflush(lndebug_fp); \
   194    193       } while (0)
   195    194   #else
   196         -#define lndebug(fmt, arg1)
          195  +#define lndebug(fmt, ...)
   197    196   #endif
   198    197   
   199         -/* =========================== C89 compatibility ============================ */
   200         -
   201         -/* snprintf() is not C89, but sqlite3_vsnprintf() can be adapted. */
   202         -static int linenoiseSnprintf(char *str, size_t size, const char *format, ...) {
   203         -    va_list ap;
   204         -    int result;
   205         -
   206         -    va_start(ap,format);
   207         -    result = (int)strlen(sqlite3_vsnprintf((int)size,str,format,ap));
   208         -    va_end(ap);
   209         -
   210         -    return result;
   211         -}
   212         -#undef snprintf
   213         -#define snprintf linenoiseSnprintf
   214         -
   215         -/* strdup() is technically not standard C89 despite being in POSIX. */
   216         -static char *linenoiseStrdup(const char *s) {
   217         -    size_t size = strlen(s)+1;
   218         -    char *result = malloc(size);
   219         -
   220         -    if (result) memcpy(result,s,size);
   221         -
   222         -    return result;
   223         -}
   224         -#undef strdup
   225         -#define strdup linenoiseStrdup
   226         -
   227         -/* strcasecmp() is not standard C89.  SQLite offers a direct replacement. */
   228         -#undef strcasecmp
   229         -#define strcasecmp sqlite3_stricmp
   230         -
   231    198   /* ======================= Low level terminal handling ====================== */
   232    199   
   233    200   /* Set if to use or not the multi line mode. */
   234    201   void linenoiseSetMultiLine(int ml) {
   235    202       mlmode = ml;
   236    203   }
   237    204   
................................................................................
   243    210   
   244    211       if (term == NULL) return 0;
   245    212       for (j = 0; unsupported_term[j]; j++)
   246    213           if (!strcasecmp(term,unsupported_term[j])) return 1;
   247    214       return 0;
   248    215   }
   249    216   
   250         -/* Raw mode */
          217  +/* Raw mode: 1960 magic shit. */
   251    218   static int enableRawMode(int fd) {
   252    219       struct termios raw;
   253    220   
   254    221       if (!isatty(STDIN_FILENO)) goto fatal;
   255    222       if (!atexit_registered) {
   256    223           atexit(linenoiseAtExit);
   257    224           atexit_registered = 1;
................................................................................
   313    280       if (sscanf(buf+2,"%d;%d",&rows,&cols) != 2) return -1;
   314    281       return cols;
   315    282   }
   316    283   
   317    284   /* Try to get the number of columns in the current terminal, or assume 80
   318    285    * if it fails. */
   319    286   static int getColumns(int ifd, int ofd) {
   320         -#if !defined(__sun__)
   321    287       struct winsize ws;
   322    288   
   323    289       if (ioctl(1, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) {
   324    290           /* ioctl() failed. Try to query the terminal itself. */
   325    291           int start, cols;
   326    292   
   327    293           /* Get the initial position so we can restore it later. */
................................................................................
   343    309           }
   344    310           return cols;
   345    311       } else {
   346    312           return ws.ws_col;
   347    313       }
   348    314   
   349    315   failed:
   350         -#endif
   351    316       return 80;
   352    317   }
   353    318   
   354    319   /* Clear the screen. Used to handle ctrl+l */
   355    320   void linenoiseClearScreen(void) {
   356    321       if (write(STDOUT_FILENO,"\x1b[H\x1b[2J",7) <= 0) {
   357    322           /* nothing to do, just to avoid warning. */
................................................................................
   440    405       return c; /* Return last read character */
   441    406   }
   442    407   
   443    408   /* Register a callback function to be called for tab-completion. */
   444    409   void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn) {
   445    410       completionCallback = fn;
   446    411   }
          412  +
          413  +/* Register a hits function to be called to show hits to the user at the
          414  + * right of the prompt. */
          415  +void linenoiseSetHintsCallback(linenoiseHintsCallback *fn) {
          416  +    hintsCallback = fn;
          417  +}
          418  +
          419  +/* Register a function to free the hints returned by the hints callback
          420  + * registered with linenoiseSetHintsCallback(). */
          421  +void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *fn) {
          422  +    freeHintsCallback = fn;
          423  +}
   447    424   
   448    425   /* This function is used by the callback function registered by the user
   449    426    * in order to add completion options given the input string when the
   450    427    * user typed <tab>. See the example.c source code for a very easy to
   451    428    * understand example. */
   452    429   void linenoiseAddCompletion(linenoiseCompletions *lc, const char *str) {
   453    430       size_t len = strlen(str);
................................................................................
   489    466       ab->b = new;
   490    467       ab->len += len;
   491    468   }
   492    469   
   493    470   static void abFree(struct abuf *ab) {
   494    471       free(ab->b);
   495    472   }
          473  +
          474  +/* Helper of refreshSingleLine() and refreshMultiLine() to show hints
          475  + * to the right of the prompt. */
          476  +void refreshShowHints(struct abuf *ab, struct linenoiseState *l, int plen) {
          477  +    char seq[64];
          478  +    if (hintsCallback && plen+l->len < l->cols) {
          479  +        int color = -1, bold = 0;
          480  +        char *hint = hintsCallback(l->buf,&color,&bold);
          481  +        if (hint) {
          482  +            int hintlen = strlen(hint);
          483  +            int hintmaxlen = l->cols-(plen+l->len);
          484  +            if (hintlen > hintmaxlen) hintlen = hintmaxlen;
          485  +            if (bold == 1 && color == -1) color = 37;
          486  +            if (color != -1 || bold != 0)
          487  +                snprintf(seq,64,"\033[%d;%d;49m",bold,color);
          488  +            abAppend(ab,seq,strlen(seq));
          489  +            abAppend(ab,hint,hintlen);
          490  +            if (color != -1 || bold != 0)
          491  +                abAppend(ab,"\033[0m",4);
          492  +            /* Call the function to free the hint returned. */
          493  +            if (freeHintsCallback) freeHintsCallback(hint);
          494  +        }
          495  +    }
          496  +}
   496    497   
   497    498   /* Single line low level line refresh.
   498    499    *
   499    500    * Rewrite the currently edited line accordingly to the buffer content,
   500    501    * cursor position, and number of columns of the terminal. */
   501    502   static void refreshSingleLine(struct linenoiseState *l) {
   502    503       char seq[64];
................................................................................
   519    520       abInit(&ab);
   520    521       /* Cursor to left edge */
   521    522       snprintf(seq,64,"\r");
   522    523       abAppend(&ab,seq,strlen(seq));
   523    524       /* Write the prompt and the current buffer content */
   524    525       abAppend(&ab,l->prompt,strlen(l->prompt));
   525    526       abAppend(&ab,buf,len);
          527  +    /* Show hits if any. */
          528  +    refreshShowHints(&ab,l,plen);
   526    529       /* Erase to right */
   527    530       snprintf(seq,64,"\x1b[0K");
   528    531       abAppend(&ab,seq,strlen(seq));
   529    532       /* Move cursor to original position. */
   530    533       snprintf(seq,64,"\r\x1b[%dC", (int)(pos+plen));
   531    534       abAppend(&ab,seq,strlen(seq));
   532    535       if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */
................................................................................
   551    554       /* Update maxrows if needed. */
   552    555       if (rows > (int)l->maxrows) l->maxrows = rows;
   553    556   
   554    557       /* First step: clear all the lines used before. To do so start by
   555    558        * going to the last row. */
   556    559       abInit(&ab);
   557    560       if (old_rows-rpos > 0) {
   558         -        lndebug("go down %d", old_rows-rpos);
          561  +        /* lndebug("go down %d", old_rows-rpos); */
   559    562           snprintf(seq,64,"\x1b[%dB", old_rows-rpos);
   560    563           abAppend(&ab,seq,strlen(seq));
   561    564       }
   562    565   
   563    566       /* Now for every row clear it, go up. */
   564    567       for (j = 0; j < old_rows-1; j++) {
   565         -        lndebug("clear+up", 0);
          568  +        /* lndebug("clear+up"); */
   566    569           snprintf(seq,64,"\r\x1b[0K\x1b[1A");
   567    570           abAppend(&ab,seq,strlen(seq));
   568    571       }
   569    572   
   570    573       /* Clean the top line. */
   571         -    lndebug("clear", 0);
          574  +    /* lndebug("clear"); */
   572    575       snprintf(seq,64,"\r\x1b[0K");
   573    576       abAppend(&ab,seq,strlen(seq));
   574    577   
   575    578       /* Write the prompt and the current buffer content */
   576    579       abAppend(&ab,l->prompt,strlen(l->prompt));
   577    580       abAppend(&ab,l->buf,l->len);
   578    581   
          582  +    /* Show hits if any. */
          583  +    refreshShowHints(&ab,l,plen);
          584  +
   579    585       /* If we are at the very end of the screen with our prompt, we need to
   580    586        * emit a newline and move the prompt to the first column. */
   581    587       if (l->pos &&
   582    588           l->pos == l->len &&
   583    589           (l->pos+plen) % l->cols == 0)
   584    590       {
   585         -        lndebug("<newline>", 0);
          591  +        /* lndebug("<newline>"); */
   586    592           abAppend(&ab,"\n",1);
   587    593           snprintf(seq,64,"\r");
   588    594           abAppend(&ab,seq,strlen(seq));
   589    595           rows++;
   590    596           if (rows > (int)l->maxrows) l->maxrows = rows;
   591    597       }
   592    598   
   593    599       /* Move cursor to right position. */
   594    600       rpos2 = (plen+l->pos+l->cols)/l->cols; /* current cursor relative row. */
   595         -    lndebug("rpos2 %d", rpos2);
          601  +    /* lndebug("rpos2 %d", rpos2); */
   596    602   
   597    603       /* Go up till we reach the expected positon. */
   598    604       if (rows-rpos2 > 0) {
   599         -        lndebug("go-up %d", rows-rpos2);
          605  +        /* lndebug("go-up %d", rows-rpos2); */
   600    606           snprintf(seq,64,"\x1b[%dA", rows-rpos2);
   601    607           abAppend(&ab,seq,strlen(seq));
   602    608       }
   603    609   
   604    610       /* Set column. */
   605    611       col = (plen+(int)l->pos) % (int)l->cols;
   606         -    lndebug("set col %d", 1+col);
          612  +    /* lndebug("set col %d", 1+col); */
   607    613       if (col)
   608    614           snprintf(seq,64,"\r\x1b[%dC", col);
   609    615       else
   610    616           snprintf(seq,64,"\r");
   611    617       abAppend(&ab,seq,strlen(seq));
   612    618   
   613         -    lndebug("\n", 0);
          619  +    /* lndebug("\n"); */
   614    620       l->oldpos = l->pos;
   615    621   
   616    622       if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */
   617    623       abFree(&ab);
   618    624   }
   619    625   
   620    626   /* Calls the two low level functions refreshSingleLine() or
................................................................................
   632    638   int linenoiseEditInsert(struct linenoiseState *l, char c) {
   633    639       if (l->len < l->buflen) {
   634    640           if (l->len == l->pos) {
   635    641               l->buf[l->pos] = c;
   636    642               l->pos++;
   637    643               l->len++;
   638    644               l->buf[l->len] = '\0';
   639         -            if ((!mlmode && l->plen+l->len < l->cols) /* || mlmode */) {
          645  +            if ((!mlmode && l->plen+l->len < l->cols && !hintsCallback)) {
   640    646                   /* Avoid a full update of the line in the
   641    647                    * trivial case. */
   642    648                   if (write(l->ofd,&c,1) == -1) return -1;
   643    649               } else {
   644    650                   refreshLine(l);
   645    651               }
   646    652           } else {
................................................................................
   806    812           }
   807    813   
   808    814           switch(c) {
   809    815           case ENTER:    /* enter */
   810    816               history_len--;
   811    817               free(history[history_len]);
   812    818               if (mlmode) linenoiseEditMoveEnd(&l);
          819  +            if (hintsCallback) {
          820  +                /* Force a refresh without hints to leave the previous
          821  +                 * line as the user typed it after a newline. */
          822  +                linenoiseHintsCallback *hc = hintsCallback;
          823  +                hintsCallback = NULL;
          824  +                refreshLine(&l);
          825  +                hintsCallback = hc;
          826  +            }
   813    827               return (int)l.len;
   814    828           case CTRL_C:     /* ctrl-c */
   815    829               errno = EAGAIN;
   816    830               return -1;
   817    831           case BACKSPACE:   /* backspace */
   818    832           case 8:     /* ctrl-h */
   819    833               linenoiseEditBackspace(&l);
................................................................................
   952    966           nread = read(STDIN_FILENO,&c,1);
   953    967           if (nread <= 0) continue;
   954    968           memmove(quit,quit+1,sizeof(quit)-1); /* shift string to left. */
   955    969           quit[sizeof(quit)-1] = c; /* Insert current char on the right. */
   956    970           if (memcmp(quit,"quit",sizeof(quit)) == 0) break;
   957    971   
   958    972           printf("'%c' %02x (%d) (type quit to exit)\n",
   959         -            isprint((int)c) ? c : '?', (int)c, (int)c);
          973  +            isprint(c) ? c : '?', (int)c, (int)c);
   960    974           printf("\r"); /* Go left edge manually, we are in raw mode. */
   961    975           fflush(stdout);
   962    976       }
   963    977       disableRawMode(STDIN_FILENO);
   964    978   }
   965    979   
   966    980   /* This function calls the line editing function linenoiseEdit() using
................................................................................
   968    982   static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) {
   969    983       int count;
   970    984   
   971    985       if (buflen == 0) {
   972    986           errno = EINVAL;
   973    987           return -1;
   974    988       }
   975         -    if (!isatty(STDIN_FILENO)) {
   976         -        /* Not a tty: read from file / pipe. */
   977         -        if (fgets(buf, buflen, stdin) == NULL) return -1;
   978         -        count = strlen(buf);
   979         -        if (count && buf[count-1] == '\n') {
   980         -            count--;
   981         -            buf[count] = '\0&