Fossil

Check-in Differences
Login

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

Difference From:

[ad984a25] Fix bug in the db_get_mprintf() function and its siblings introduced by the previous check-in and caused by the parameter reordering. (user: drh tags: trunk, date: 2018-01-16 16:32:33)

To:

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

Changes to VERSION.

     1         -2.5
            1  +2.4

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 parameter may be an object with any (or all) of
          152  +    The optional opt paramater 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}
    26     25       json=0               => {Build with fossil JSON API enabled}
    27     26   }
    28     27   
    29     28   # sqlite wants these types if possible
    30     29   cc-with {-includes {stdint.h inttypes.h}} {
    31     30       cc-check-types uint32_t uint16_t int16_t uint8_t
    32     31   }
................................................................................
    33     32   
    34     33   # Use pread/pwrite system calls in place of seek + read/write if possible
    35     34   define USE_PREAD [cc-check-functions pread]
    36     35   
    37     36   # Find tclsh for the test suite. Can't yet use jimsh for this.
    38     37   cc-check-progs tclsh
    39     38   
    40         -define EXTRA_CFLAGS "-Wall"
           39  +define EXTRA_CFLAGS ""
    41     40   define EXTRA_LDFLAGS ""
    42     41   define USE_SYSTEM_SQLITE 0
    43     42   define USE_LINENOISE 0
    44     43   define FOSSIL_ENABLE_MINIZ 0
    45     44   define USE_SEE 0
    46     45   
    47     46   # This procedure is a customized version of "cc-check-function-in-lib",
................................................................................
    87     86       # search for the system SQLite once with -ldl, and once without. If
    88     87       # the library can only be found with $extralibs set to -ldl, then
    89     88       # the code below will append -ldl to LIBS.
    90     89       #
    91     90       foreach extralibs {{} {-ldl}} {
    92     91   
    93     92         # Locate the system SQLite by searching for sqlite3_open(). Then check
    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
           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
    96     95         # fossil.
    97     96         #
    98     97         if {[check-function-in-lib sqlite3_open sqlite3 $extralibs]} {
    99         -        if {![check-function-in-lib sqlite3_vtab_collation sqlite3 $extralibs]} {
   100         -          user-error "system sqlite3 too old (require >= 3.22.0)"
           98  +        if {![check-function-in-lib sqlite3_prepare_v3 sqlite3 $extralibs]} {
           99  +          user-error "system sqlite3 too old (require >= 3.20.0)"
   101    100           }
   102    101   
   103    102           # Success. Update symbols and return.
   104    103           #
   105    104           define USE_SYSTEM_SQLITE 1
   106    105           define-append LIBS -lsqlite3
   107    106           define-append LIBS $extralibs
................................................................................
   131    130   
   132    131   if {[string match *-solaris* [get-define host]]} {
   133    132       define-append EXTRA_CFLAGS {-D_XOPEN_SOURCE=500 -D__EXTENSIONS__}
   134    133   }
   135    134   
   136    135   if {[opt-bool fossil-debug]} {
   137    136       define-append EXTRA_CFLAGS -DFOSSIL_DEBUG
   138         -    define CFLAGS {-g -O0 -Wall}
          137  +    define CFLAGS {-g -O0}
   139    138       msg-result "Debugging support enabled"
   140    139   }
   141    140   
   142         -if {[opt-bool no-opt]} {
   143         -    define CFLAGS {-g -O0 -Wall}
   144         -    msg-result "Builting without compiler optimization"
   145         -}
   146         -
   147    141   if {[opt-bool with-see]} {
   148    142       define-append EXTRA_CFLAGS -DUSE_SEE
   149    143       define USE_SEE 1
   150    144       msg-result "Enabling encryption support"
   151    145   }
   152    146   
   153    147   if {[opt-bool json]} {
................................................................................
   363    357                   /usr /usr/local /usr/share /opt/local]
   364    358               set msg "on your system"
   365    359           }
   366    360       } else {
   367    361           array set tclconfig [parse-tclconfig-sh $tclpath]
   368    362           set msg "at $tclpath"
   369    363       }
   370         -    if {[opt-bool static]} {
   371         -        set tclconfig(TCL_LD_FLAGS) { }
   372         -    }
   373    364       if {![info exists tclconfig(TCL_INCLUDE_SPEC)]} {
   374    365           user-error "Cannot find Tcl $msg"
   375    366       }
   376    367       set tclstubs [opt-bool with-tcl-stubs]
   377    368       if {$tclprivatestubs} {
   378    369           define FOSSIL_ENABLE_TCL_PRIVATE_STUBS
   379    370           define USE_TCL_STUBS

Deleted skins/ardoise/README.md.

     1         -## Ardoise theme
     2         -
     3         -A black and grey skin ("Ardoise" is the french word for slate).
     4         -This skin was contributed by Antoine Chavasse.
     5         -
     6         -This theme is loosely based upon, and still contains some elements from the Blitz theme by James Moger.
     7         -
     8         -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).
     9         -
    10         -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).
    11         -
    12         -The sass version of Skeleton used in this project was made by [Seth Coelen](https://github.com/whatsnewsaes).

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

Deleted skins/ardoise/details.txt.

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

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

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

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>
     1     11   <div class="header">
     2     12     <div class="logo">
     3     13       <img src="$logo_image_url" alt="logo">
     4     14       <br />$<project_name>
     5     15     </div>
     6     16     <div class="title">$<title></div>
     7     17     <div class="status"><th1>

Changes to skins/blitz/css.txt.

  1067   1067     background-color: #ffffe8;
  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  +
         1075  +tr.timelineSpacer {
         1076  +}
  1074   1077   
  1075   1078   tr.timelineBottom td {
  1076   1079     border-bottom: 0;
  1077   1080   }
  1078   1081   
  1079   1082   div.timelineDate {
  1080   1083     font-weight: bold;

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>
            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>
     7     10       </div>
     8         -    This page was generated in about <th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s
     9         -  </div>
    10         -</div>
           11  +  </body>
           12  +</html>

Changes to skins/blitz/header.txt.

     1         -<div class="header">
     2         -  <div class="container">
            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>
            8  +
            9  +  <body>
           10  +    <div class="header">
           11  +      <div class="container">
     3     12   
     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>
           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>
    25     34   
    26         -    <!-- Main Menu -->
    27         -    <div class="mainmenu">
    28         -      <ul>
    29         -        <th1>
           35  +        <!-- Main Menu -->
           36  +        <div class="mainmenu">
           37  +          <ul>
           38  +            <th1>
    30     39   proc menulink {url name} {
    31     40     upvar current_page current
    32     41     upvar home home
    33     42     if {[string range $url 0 [string length $current]] eq "/$current"} {
    34     43       html "<li class='active'>"
    35     44     } else {
    36     45       html "<li>"

Changes to skins/blitz_no_logo/css.txt.

  1067   1067     background-color: #ffffe8;
  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  +
         1075  +tr.timelineSpacer {
         1076  +}
  1074   1077   
  1075   1078   tr.timelineBottom td {
  1076   1079     border-bottom: 0;
  1077   1080   }
  1078   1081   
  1079   1082   div.timelineDate {
  1080   1083     font-weight: bold;

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>
            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>
     7     10       </div>
     8         -    This page was generated in about <th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s
     9         -  </div>
    10         -</div>
           11  +  </body>
           12  +</html>

Changes to skins/blitz_no_logo/header.txt.

     1         -<div class="header">
     2         -  <div class="container">
            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>
            8  +
            9  +  <body>
           10  +    <div class="header">
           11  +      <div class="container">
     3     12   
     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>
           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>
    22     31   
    23         -    <!-- Main Menu -->
    24         -    <div class="mainmenu">
    25         -      <ul>
    26         -        <th1>
           32  +        <!-- Main Menu -->
           33  +        <div class="mainmenu">
           34  +          <ul>
           35  +            <th1>
    27     36   proc menulink {url name} {
    28     37     upvar current_page current
    29     38     upvar home home
    30     39     if {[string range $url 0 [string length $current]] eq "/$current"} {
    31     40       html "<li class='active'>"
    32     41     } else {
    33     42       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 h3 {
           47  +.content h2 {
    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;
................................................................................
   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    186   
   187         -span.timelineDetail {
   188         -    font-size: 90%;
   189         -}
   190         -
   191    187   .footer {
   192    188       border-top: 1px solid #ccc;
   193    189       padding: 10px;
   194    190       font-size:.7em;
   195    191       margin-top: 10px;
   196    192       color: #ccc;
   197    193   }

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         -<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">
            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">
    12     24   <th1>
    13     25   proc menulink {url name} {
    14     26     upvar current_page current
    15     27     upvar home home
    16     28     if {[string range $url 0 [string length $current]] eq "/$current"} {
    17     29       html "<a href='$home$url' class='active'>$name</a>\n"
    18     30     } 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-spacing: 0px 2px;
   170         -  // border-collapse: collapse;
          169  +  border-collapse: collapse;
   171    170   }
   172    171   
   173    172   tr.timelineSelected {
   174    173     background-color: #7EA2D9;
   175    174   }
   176    175   
   177    176   /* commit node */
................................................................................
   332    331   div.selectedText {
   333    332     background-color: #7EA2D9;
   334    333   }
   335    334   
   336    335   .statistics-report-graph-line {
   337    336     background-color: #7EA2D9;
   338    337   }
   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>
     1     11   <div class="header">
     2     12     <div class="logo">
     3     13       <th1>
     4     14       ##
     5     15       ## NOTE: The purpose of this procedure is to take the base URL of the
     6     16       ##       Fossil project and return the root of the entire web site using
     7     17       ##       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>
     1     11   <div class="header">
     2     12     <div class="logo">
     3     13       <th1>
     4     14       ##
     5     15       ## NOTE: The purpose of this procedure is to take the base URL of the
     6     16       ##       Fossil project and return the root of the entire web site using
     7     17       ##       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>
     1     11   <div class="header">
     2     12     <div class="title">$<title></div>
     3     13     <div class="status">
     4     14       <div class="logo">$<project_name></div><br/>
     5     15       <th1>
     6     16        if {[info exists login]} {
     7     17          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>
     1     11   <div class="header">
     2     12     <div class="logo">
     3     13       <img src="$logo_image_url" alt="logo" />
     4     14     </div>
     5     15     <div class="title"><small>$<project_name></small><br />$<title></div>
     6     16     <div class="status"><th1>
     7     17        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>
     1     11   <div class="header">
     2     12     <div class="title"><small>$<project_name></small><br />$<title></div>
     3     13     <div class="status"><th1>
     4     14        if {[info exists login]} {
     5     15          puts "Logged in as $login"
     6     16        } else {
     7     17          puts "Not logged in"

Changes to skins/rounded1/details.txt.

     1      1   timeline-arrowheads:        1
     2         -timeline-circle-nodes:      1
            2  +timeline-circle-nodes:      0
     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>
     1     11   <div class="header">
     2     12     <div class="logo">
     3     13       <img src="$logo_image_url" alt="logo">
     4     14       <br />$<project_name>
     5     15     </div>
     6     16     <div class="title">$<title></div>
     7     17     <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         -/* The suppressed duplicates lines in timeline, .. */
   713         -.timelineDisabled {
   714         -  font-size:  0.5rem;
   715         -  font-style: italic;
   716         -}
   717         -
   718         -/* the format for the timeline version display(no history permission!) */
   719         -.timelineHistDsp {
          712  +div.divider {
          713  +  color: #ee0;
          714  +  font-size: 1.2rem;
   720    715     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;
          716  +  margin-top: 1rem;
   740    717     white-space: nowrap;
   741    718   }
   742    719   
   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;
   752         -  vertical-align: top;
   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;
          720  +/* The suppressed duplicates lines in timeline, .. */
          721  +span.timelineDisabled {
          722  +  font-size: 0.5rem;
          723  +  font-style: italic;
          724  +}
          725  +
          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 {
   787    745     border-radius: 1rem 0 0 1rem;
   788         -  border-width:  0;
   789    746   }
   790    747   
   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;
          748  +tr.timelineSelected td.timelineTableCell {
   800    749     border-radius: 0 1rem 1rem 0;
   801    750   }
   802    751   
   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;
          752  +/* the format for the timeline data cells */
          753  +td.timelineTableCell {
          754  +  padding: 0.3rem;
          755  +  text-align: left;
          756  +  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;
   866    772   }
   867    773   
   868    774   /* the format for the timeline version links */
   869    775   a.timelineHistLink {
   870    776   }
          777  +
          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  +}
   871    796   
   872    797   
   873    798   /**************************************
   874    799    * User Edit
   875    800    */
   876    801   
   877    802   /* layout definition for the capabilities box on the user edit detail page */
................................................................................
   895    820   /* color for capabilities, inherited by developer */
   896    821   span.ueditInheritDeveloper {
   897    822     color: #f00;
   898    823   }
   899    824   
   900    825   /* color for capabilities, inherited by reader */
   901    826   span.ueditInheritReader {
   902         -  color: #ee0;
          827  +  color: black;
   903    828   }
   904    829   
   905    830   /* color for capabilities, inherited by anonymous */
   906    831   span.ueditInheritAnonymous {
   907    832     color: #00f;
   908    833   }
   909    834   
................................................................................
  1011    936     /* no special definitions, class defined, to enable color pickers,
  1012    937     * f.e.:
  1013    938     * ** add the color picker found at http:jscolor.com as java script
  1014    939     * include
  1015    940     * ** to the header and configure the java script file with
  1016    941     * ** 1. use as bindClass :checkinUserColor
  1017    942     * ** 2. change the default hash adding behaviour to ON
  1018         -  * ** or change the class definition of element identified by
          943  +  * ** or change the class defition of element identified by
  1019    944     * id="clrcust"
  1020    945     * ** to a standard jscolor definition with java script in the footer.
  1021    946     * */
  1022    947   }
  1023    948   
  1024    949   /* format for end of content area, to be used to clear page flow. */
  1025    950   div.endContent {
................................................................................
  1060    985     color: #f00;
  1061    986     font-weight: bold;
  1062    987   }
  1063    988   /* format for artifact lines, no longer shunned */
  1064    989   p.noMoreShun {
  1065    990     color: #00f;
  1066    991   }
  1067         -/* format for artifact lines being shunned */
          992  +/* format for artifact lines beeing shunned */
  1068    993   p.shunned {
  1069    994     color: #00f;
  1070    995   }
  1071    996   /* a broken hyperlink */
  1072    997   span.brokenlink {
  1073    998     color: #f00;
  1074    999   }

Changes to skins/xekri/details.txt.

     1      1   timeline-arrowheads:        1
     2      2   timeline-circle-nodes:      0
     3         -timeline-color-graph-lines: 1
            3  +timeline-color-graph-lines: 0
     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>
     1     11   <div class="header">
     2     12     <div class="logo">
     3     13       <th1>
     4     14       ##
     5     15       ## NOTE: The purpose of this procedure is to take the base URL of the
     6     16       ##       Fossil project and return the root of the entire web site using
     7     17       ##       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 x.x this value was always zero.  For Fossil-NG
    29         -**          it will probably always be one.  When this value is zero,
           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,
    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 2.x behavior when using Fossil-NG
           37  +**          To retain the Fossil version 1.x behavior when using Fossil 2.x,
    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_isexe(zFullname, RepoFILE);
          190  +    int isExe = file_wd_isexe(zFullname);
   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_islink(0));
          194  +      vid, zPath, isExe, file_wd_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_isdir(zName, RepoFILE);
          331  +    isDir = file_wd_isdir(zName);
   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_isfile_or_link(zPath) ){
          700  +    if( !file_wd_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, RepoFILE);
          802  +        int isOldDir = file_isdir(zOldName);
   803    803           if( isOldDir==1 ){
   804         -          int isNewDir = file_isdir(zNewName, RepoFILE);
          804  +          int isNewDir = file_isdir(zNewName);
   805    805             if( isNewDir==0 ){
   806    806               file_rename(zOldName, zNewName, isOldDir, isNewDir);
   807    807             }
   808    808           }else{
   809         -          if( file_islink(zOldName) ){
          809  +          if( file_wd_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_isdir(zDest, RepoFILE)!=1 ){
          905  +  if( file_wd_isdir(zDest)!=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, ExtFILE) ) continue;
          336  +      if( !file_isfile(z) ) 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, ExtFILE)!=1)
          402  +     || (useCheckouts && file_isdir(zFilename)!=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, ExtFILE);
          769  +    blob_read_from_file(&content, zFile);
   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.

   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    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.
   799         -**
   800    789   ** Any prior content of the blob is discarded, not freed.
   801    790   **
   802    791   ** Return the number of bytes read. Calls fossil_fatal() on error (i.e.
   803    792   ** it exit()s and does not return).
   804    793   */
   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         -){
          794  +sqlite3_int64 blob_read_from_file(Blob *pBlob, const char *zFilename){
   810    795     sqlite3_int64 size, got;
   811    796     FILE *in;
   812    797     if( zFilename==0 || zFilename[0]==0
   813    798           || (zFilename[0]=='-' && zFilename[1]==0) ){
   814    799       return blob_read_from_channel(pBlob, stdin, -1);
   815    800     }
   816         -  if( file_islink(zFilename) ){
   817         -    return blob_read_link(pBlob, zFilename);
   818         -  }
   819         -  size = file_size(zFilename, eFType);
          801  +  size = file_wd_size(zFilename);
   820    802     blob_zero(pBlob);
   821    803     if( size<0 ){
   822    804       fossil_fatal("no such file: %s", zFilename);
   823    805     }
   824    806     if( size==0 ){
   825    807       return 0;
   826    808     }
................................................................................
   857    839     blob_appendf(pBlob, "%s", zBuf);
   858    840     return len;
   859    841   #else
   860    842     blob_zero(pBlob);
   861    843     return 0;
   862    844   #endif
   863    845   }
          846  +
   864    847   
   865    848   /*
   866    849   ** Write the content of a blob into a file.
   867    850   **
   868    851   ** If the filename is blank or "-" then write to standard output.
   869    852   **
   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.
   874         -**
   875    853   ** Return the number of bytes written.
   876    854   */
   877    855   int blob_write_to_file(Blob *pBlob, const char *zFilename){
   878    856     FILE *out;
   879    857     int nWrote;
   880    858   
   881    859     if( zFilename[0]==0 || (zFilename[0]=='-' && zFilename[1]==0) ){
................................................................................
   888    866   #endif
   889    867       nWrote = fwrite(blob_buffer(pBlob), 1, blob_size(pBlob), stdout);
   890    868   #if defined(_WIN32)
   891    869       fflush(stdout);
   892    870       _setmode(_fileno(stdout), _O_TEXT);
   893    871   #endif
   894    872     }else{
   895         -    file_mkfolder(zFilename, ExtFILE, 1, 0);
          873  +    file_mkfolder(zFilename, 1, 0);
   896    874       out = fossil_fopen(zFilename, "wb");
   897    875       if( out==0 ){
   898    876   #if _WIN32
   899    877         const char *zReserved = file_is_win_reserved(zFilename);
   900    878         if( zReserved ){
   901    879           fossil_fatal("cannot open \"%s\" because \"%s\" is "
   902    880                "a reserved name on Windows", zFilename, zReserved);
................................................................................
   953    931   ** Run compression on INPUTFILE and write the result into OUTPUTFILE.
   954    932   **
   955    933   ** This is used to test and debug the blob_compress() routine.
   956    934   */
   957    935   void compress_cmd(void){
   958    936     Blob f;
   959    937     if( g.argc!=4 ) usage("INPUTFILE OUTPUTFILE");
   960         -  blob_read_from_file(&f, g.argv[2], ExtFILE);
          938  +  blob_read_from_file(&f, g.argv[2]);
   961    939     blob_compress(&f, &f);
   962    940     blob_write_to_file(&f, g.argv[3]);
   963    941   }
   964    942   
   965    943   /*
   966    944   ** Compress the concatenation of a blobs pIn1 and pIn2.  Store the result
   967    945   ** in pOut.
................................................................................
  1012    990   ** content, then write results into OUT.
  1013    991   **
  1014    992   ** This is used to test and debug the blob_compress2() routine.
  1015    993   */
  1016    994   void compress2_cmd(void){
  1017    995     Blob f1, f2;
  1018    996     if( g.argc!=5 ) usage("INPUTFILE1 INPUTFILE2 OUTPUTFILE");
  1019         -  blob_read_from_file(&f1, g.argv[2], ExtFILE);
  1020         -  blob_read_from_file(&f2, g.argv[3], ExtFILE);
          997  +  blob_read_from_file(&f1, g.argv[2]);
          998  +  blob_read_from_file(&f2, g.argv[3]);
  1021    999     blob_compress2(&f1, &f2, &f1);
  1022   1000     blob_write_to_file(&f1, g.argv[4]);
  1023   1001   }
  1024   1002   
  1025   1003   /*
  1026   1004   ** Uncompress blob pIn and store the result in pOut.  It is ok for pIn and
  1027   1005   ** pOut to be the same blob.
................................................................................
  1064   1042   ** Read the content of file IN, uncompress that content, and write the
  1065   1043   ** result into OUT.  This command is intended for testing of the
  1066   1044   ** blob_compress() function.
  1067   1045   */
  1068   1046   void uncompress_cmd(void){
  1069   1047     Blob f;
  1070   1048     if( g.argc!=4 ) usage("INPUTFILE OUTPUTFILE");
  1071         -  blob_read_from_file(&f, g.argv[2], ExtFILE);
         1049  +  blob_read_from_file(&f, g.argv[2]);
  1072   1050     blob_uncompress(&f, &f);
  1073   1051     blob_write_to_file(&f, g.argv[3]);
  1074   1052   }
  1075   1053   
  1076   1054   /*
  1077   1055   ** COMMAND: test-cycle-compress
  1078   1056   **
................................................................................
  1079   1057   ** Compress and uncompress each file named on the command line.
  1080   1058   ** Verify that the original content is recovered.
  1081   1059   */
  1082   1060   void test_cycle_compress(void){
  1083   1061     int i;
  1084   1062     Blob b1, b2, b3;
  1085   1063     for(i=2; i<g.argc; i++){
  1086         -    blob_read_from_file(&b1, g.argv[i], ExtFILE);
         1064  +    blob_read_from_file(&b1, g.argv[i]);
  1087   1065       blob_compress(&b1, &b2);
  1088   1066       blob_uncompress(&b2, &b3);
  1089   1067       if( blob_compare(&b1, &b3) ){
  1090   1068         fossil_fatal("compress/uncompress cycle failed for %s", g.argv[i]);
  1091   1069       }
  1092   1070       blob_reset(&b1);
  1093   1071       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">
   399         -  @ <table class='sortable' data-column-types='tkNtt' data-init-sort='2'>
          398  +  @ <div class="brlist"><table id="branchlisttable">
   400    399     @ <thead><tr>
   401    400     @ <th>Branch Name</th>
   402    401     @ <th>Age</th>
   403    402     @ <th>Check-ins</th>
   404    403     @ <th>Status</th>
   405    404     @ <th>Resolution</th>
   406    405     @ </tr></thead><tbody>
................................................................................
   438    437       }else{
   439    438         @ <td></td>
   440    439       }
   441    440       @ </tr>
   442    441     }
   443    442     @ </tbody></table></div>
   444    443     db_finalize(&q);
   445         -  style_table_sorter();
          444  +  output_table_sorting_javascript("branchlisttable","tkNtt",2);
   446    445     style_footer();
   447    446   }
   448    447   
   449    448   /*
   450    449   ** WEBPAGE: brlist
   451    450   ** Show a list of branches.  With no query parameters, a sortable table
   452    451   ** 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))
   203         -    if( zD ){
   204         -      @ &nbsp;&nbsp;%z(href("%R/timeline?chng=%T/*", zD))[history]</a>
   205         -    }
   206         -    @ </h2>
          202  +    @ %s(blob_str(&dirname))</h2>
   207    203       zSubdirLink = mprintf("%R/dir?name=%T", zPrefix);
   208    204     }
   209    205     style_submenu_element("All", "%s", url_render(&sURI, "ci", 0, 0, 0));
   210    206     style_submenu_element("Tree-View", "%s",
   211    207                           url_render(&sURI, "type", "tree", 0, 0));
   212    208   
   213    209     /* Compute the temporary table "localfiles" containing the names
................................................................................
   845    841         while( nClose-- > 0 ){
   846    842           @ </ul>
   847    843         }
   848    844       }
   849    845     }
   850    846     @ </ul>
   851    847     @ </ul></div>
   852         -  style_load_one_js_file("tree.js");
          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>
   853    918     style_footer();
   854    919   
   855    920     /* We could free memory used by sTree here if we needed to.  But
   856    921     ** the process is about to exit, so doing so would not really accomplish
   857    922     ** anything useful. */
   858    923   }
   859    924   
................................................................................
  1079   1144         }else{
  1080   1145           @ %z(href("%R/artifact/%!S",zFUuid))%h(zFile)</a><br />
  1081   1146         }
  1082   1147       }
  1083   1148       db_reset(&q2);
  1084   1149       @ </td>
  1085   1150       @ <td>
  1086         -    @ %W(zComment)
  1087         -    @ (check-in:&nbsp;%z(href("%R/ci/%!S",zUuid))%S(zUuid)</a>,
         1151  +    @ %z(href("%R/info/%!S",zUuid))[%S(zUuid)]</a>
  1088   1152       if( showId ){
  1089         -      @ id: %d(mid)
         1153  +      @ (%d(mid))
  1090   1154       }
  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>)
         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>)
  1094   1159       @ </td></tr>
  1095   1160       @
  1096   1161       fossil_free(zAge);
  1097   1162     }
  1098   1163     @ </table></div>
  1099   1164     db_finalize(&q1);
  1100   1165     db_finalize(&q2);
  1101   1166     style_footer();
  1102   1167   }

Changes to src/builtin.c.

    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     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         -}
    86         -
    87     70   /*
    88     71   ** COMMAND: test-builtin-get
    89     72   **
    90     73   ** Usage: %fossil test-builtin-get NAME ?OUTPUT-FILE?
    91     74   */
    92     75   void test_builtin_get(void){
    93     76     const unsigned char *pData;

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, ExtFILE)<0 ){
           60  +  if( !doInit && file_size(zFile)<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], ExtFILE);
          166  +    blob_read_from_file(&content, g.argv[i]);
   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, ExtFILE);
           54  +    sz = file_size(zDbName);
    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, ExtFILE));
          322  +                   nEntry, file_size(zDbName));
   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, ExtFILE));
          370  +    bigSizeName(sizeof(zBuf), zBuf, file_size(zDbName));
   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 ){
   215         -    zPath = g.zTop;
   216         -    if( zPath[0]==0 ) zPath = "/";
   217         -  }
          214  +  if( zPath==0 ) zPath = g.zTop;
   218    215     if( g.zBaseURL!=0 && strncmp(g.zBaseURL, "https:", 6)==0 ){
   219    216       zSecure = " secure;";
   220    217     }
   221    218     if( lifetime>0 ){
   222    219       lifetime += (int)time(0);
   223    220       blob_appendf(&extraHeader,
   224    221          "Set-Cookie: %s=%t; Path=%s; expires=%z; HttpOnly;%s Version=1\r\n",
................................................................................
  1341   1338   /* z[] is the value of an X-FORWARDED-FOR: line in an HTTP header.
  1342   1339   ** Return a pointer to a string containing the real IP address, or a
  1343   1340   ** NULL pointer to stick with the IP address previously computed and
  1344   1341   ** loaded into g.zIpAddr.
  1345   1342   */
  1346   1343   static const char *cgi_accept_forwarded_for(const char *z){
  1347   1344     int i;
  1348         -  if( !cgi_is_loopback(g.zIpAddr) ){
  1349         -    /* Only accept X-FORWARDED-FOR if input coming from the local machine */
  1350         -    return 0;
  1351         -  }
         1345  +  if( fossil_strcmp(g.zIpAddr, "127.0.0.1")!=0 ) return 0;
         1346  +
  1352   1347     i = strlen(z)-1;
  1353   1348     while( i>=0 && z[i]!=',' && !fossil_isspace(z[i]) ) i--;
  1354   1349     return &z[++i];
  1355   1350   }
  1356   1351   
  1357   1352   /*
  1358   1353   ** Remove the first space-delimited token from a string and return
................................................................................
  1754   1749   #define HTTP_SERVER_HAD_CHECKOUT   0x0008     /* Was a checkout open? */
  1755   1750   #define HTTP_SERVER_REPOLIST       0x0010     /* Allow repo listing */
  1756   1751   
  1757   1752   #endif /* INTERFACE */
  1758   1753   
  1759   1754   /*
  1760   1755   ** Maximum number of child processes that we can have running
  1761         -** at one time.  Set this to 0 for "no limit".
         1756  +** at one time before we start slowing things down.
  1762   1757   */
  1763         -#ifndef FOSSIL_MAX_CONNECTIONS
  1764         -# define FOSSIL_MAX_CONNECTIONS 1000
  1765         -#endif
         1758  +#define MAX_PARALLEL 2
  1766   1759   
  1767   1760   /*
  1768   1761   ** Implement an HTTP server daemon listening on port iPort.
  1769   1762   **
  1770   1763   ** As new connections arrive, fork a child and let child return
  1771   1764   ** out of this procedure call.  The child will handle the request.
  1772   1765   ** The parent never returns from this procedure.
................................................................................
  1782   1775   ){
  1783   1776   #if defined(_WIN32)
  1784   1777     /* Use win32_http_server() instead */
  1785   1778     fossil_exit(1);
  1786   1779   #else
  1787   1780     int listener = -1;           /* The server socket */
  1788   1781     int connection;              /* A socket for each individual connection */
  1789         -  int nRequest = 0;            /* Number of requests handled so far */
  1790   1782     fd_set readfds;              /* Set of file descriptors for select() */
  1791   1783     socklen_t lenaddr;           /* Length of the inaddr structure */
  1792   1784     int child;                   /* PID of the child process */
  1793   1785     int nchildren = 0;           /* Number of child processes */
  1794   1786     struct timeval delay;        /* How long to wait inside select() */
  1795   1787     struct sockaddr_in inaddr;   /* The socket address */
  1796   1788     int opt = 1;                 /* setsockopt flag */
................................................................................
  1853   1845       }else
  1854   1846   #endif
  1855   1847       if( system(zBrowser)<0 ){
  1856   1848         fossil_warning("cannot start browser: %s\n", zBrowser);
  1857   1849       }
  1858   1850     }
  1859   1851     while( 1 ){
  1860         -#if FOSSIL_MAX_CONNECTIONS>0
  1861         -    while( nchildren>=FOSSIL_MAX_CONNECTIONS ){
  1862         -      if( wait(0)>=0 ) nchildren--;
         1852  +    if( nchildren>MAX_PARALLEL ){
         1853  +      /* Slow down if connections are arriving too fast */
         1854  +      sleep( nchildren-MAX_PARALLEL );
  1863   1855       }
  1864         -#endif
  1865         -    delay.tv_sec = 0;
  1866         -    delay.tv_usec = 100000;
         1856  +    delay.tv_sec = 60;
         1857  +    delay.tv_usec = 0;
  1867   1858       FD_ZERO(&readfds);
  1868   1859       assert( listener>=0 );
  1869   1860       FD_SET( listener, &readfds);
  1870   1861       select( listener+1, &readfds, 0, 0, &delay);
  1871   1862       if( FD_ISSET(listener, &readfds) ){
  1872   1863         lenaddr = sizeof(inaddr);
  1873   1864         connection = accept(listener, (struct sockaddr*)&inaddr, &lenaddr);
  1874   1865         if( connection>=0 ){
  1875   1866           child = fork();
  1876   1867           if( child!=0 ){
  1877         -          if( child>0 ){
  1878         -            nchildren++;
  1879         -            nRequest++;
  1880         -          }
         1868  +          if( child>0 ) nchildren++;
  1881   1869             close(connection);
  1882   1870           }else{
  1883   1871             int nErr = 0, fd;
  1884   1872             close(0);
  1885   1873             fd = dup(connection);
  1886   1874             if( fd!=0 ) nErr++;
  1887   1875             close(1);
................................................................................
  1889   1877             if( fd!=1 ) nErr++;
  1890   1878             if( !g.fAnyTrace ){
  1891   1879               close(2);
  1892   1880               fd = dup(connection);
  1893   1881               if( fd!=2 ) nErr++;
  1894   1882             }
  1895   1883             close(connection);
  1896         -          g.nPendingRequest = nchildren+1;
  1897         -          g.nRequest = nRequest+1;
  1898   1884             return nErr;
  1899   1885           }
  1900   1886         }
  1901   1887       }
  1902   1888       /* Bury dead children */
  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         -    }  
         1889  +    while( waitpid(0, 0, WNOHANG)>0 ){
         1890  +      nchildren--;
         1891  +    }
  1911   1892     }
  1912   1893     /* NOT REACHED */
  1913   1894     fossil_exit(1);
  1914   1895   #endif
  1915   1896     /* NOT REACHED */
  1916   1897     return 0;
  1917   1898   }
................................................................................
  2031   2012       if( (zIndex = strchr(zSshClient,' '))!=0 ){
  2032   2013         zSshClient[zIndex-zSshClient] = '\0';
  2033   2014         return zSshClient;
  2034   2015       }
  2035   2016     }
  2036   2017     return zDefault;
  2037   2018   }
  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_isdir(zName, RepoFILE);
           91  +      isDir = file_wd_isdir(zName);
    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_isfile_or_link(zFullName);
          214  +    int isMissing = !file_wd_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_islink(zFullName)
          259  +    }else if( (flags & C_CONFLICT) && isChnged && !file_wd_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";
................................................................................
   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    476   
   477         -  fossil_pledge("stdio rpath wpath cpath id flock tty", "");
   478         -
   479    477     /* Load affirmative flag options. */
   480    478     for( i=0; i<count(flagDefs); ++i ){
   481    479       if( (command==CHANGES || !(flagDefs[i].mask & C_CLASSIFY))
   482    480        && find_option(flagDefs[i].option, 0, 0) ){
   483    481         flags |= flagDefs[i].mask;
   484    482       }
   485    483     }
................................................................................
   764    762       char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname);
   765    763       const char *type = "";
   766    764       if( verboseFlag ){
   767    765         if( isNew ){
   768    766           type = "ADDED      ";
   769    767         }else if( isDeleted ){
   770    768           type = "DELETED    ";
   771         -      }else if( !file_isfile_or_link(zFullName) ){
          769  +      }else if( !file_wd_isfile_or_link(zFullName) ){
   772    770           if( file_access(zFullName, F_OK)==0 ){
   773    771             type = "NOT_A_FILE ";
   774    772           }else{
   775    773             type = "MISSING    ";
   776    774           }
   777    775         }else if( chnged ){
   778    776           if( chnged==2 ){
................................................................................
  1221   1219     if( zEditor ){
  1222   1220       zCmd = mprintf("%s \"%s\"", zEditor, zFile);
  1223   1221       fossil_print("%s\n", zCmd);
  1224   1222       if( fossil_system(zCmd) ){
  1225   1223         fossil_fatal("editor aborted: \"%s\"", zCmd);
  1226   1224       }
  1227   1225   
  1228         -    blob_read_from_file(&reply, zFile, ExtFILE);
         1226  +    blob_read_from_file(&reply, zFile);
  1229   1227     }else{
  1230   1228       char zIn[300];
  1231   1229       blob_zero(&reply);
  1232   1230       while( fgets(zIn, sizeof(zIn), stdin)!=0 ){
  1233   1231         if( zIn[0]=='.' && (zIn[1]==0 || zIn[1]=='\r' || zIn[1]=='\n') ){
  1234   1232           break;
  1235   1233         }
................................................................................
  1562   1560       ** directly from the filesystem.  On windows, permissions are
  1563   1561       ** unchanged from the original.  However, only do this if the file
  1564   1562       ** itself is actually selected to be part of this check-in.
  1565   1563       */
  1566   1564       if( isSelected ){
  1567   1565         int mPerm;
  1568   1566   
  1569         -      mPerm = file_perm(blob_str(&filename), RepoFILE);
         1567  +      mPerm = file_wd_perm(blob_str(&filename));
  1570   1568         isExe = ( mPerm==PERM_EXE );
  1571   1569         isLink = ( mPerm==PERM_LNK );
  1572   1570       }
  1573   1571   #endif
  1574   1572       if( isExe ){
  1575   1573         zPerm = " x";
  1576   1574       }else if( isLink ){
................................................................................
  1908   1906   
  1909   1907       zFullname = db_column_text(&q, 0);
  1910   1908       zName = db_column_text(&q, 1);
  1911   1909       crlfOk = db_column_int(&q, 2);
  1912   1910       binOk = db_column_int(&q, 3);
  1913   1911       encodingOk = db_column_int(&q, 4);
  1914   1912       blob_zero(&content);
  1915         -    blob_read_from_file(&content, zFullname, RepoFILE);
         1913  +    if( file_wd_islink(zFullname) ){
         1914  +      blob_read_link(&content, zFullname);
         1915  +    }else{
         1916  +      blob_read_from_file(&content, zFullname);
         1917  +    }
  1916   1918       blob_zero(&reason);
  1917   1919       fileRc = commit_warning(&content, crlfOk, binOk, encodingOk, 2,
  1918   1920                               zFullname, &reason);
  1919   1921       if( fileRc || verboseFlag ){
  1920   1922         fossil_print("%d\t%s\t%s\n", fileRc, zName, blob_str(&reason));
  1921   1923       }
  1922   1924       blob_reset(&reason);
................................................................................
  2299   2301     }
  2300   2302   
  2301   2303     if( zComment ){
  2302   2304       blob_zero(&comment);
  2303   2305       blob_append(&comment, zComment, -1);
  2304   2306     }else if( zComFile ){
  2305   2307       blob_zero(&comment);
  2306         -    blob_read_from_file(&comment, zComFile, ExtFILE);
         2308  +    blob_read_from_file(&comment, zComFile);
  2307   2309       blob_to_utf8_no_bom(&comment, 1);
  2308   2310     }else if( dryRunFlag ){
  2309   2311       blob_zero(&comment);
  2310   2312     }else if( !noPrompt ){
  2311   2313       char *zInit = db_text(0, "SELECT value FROM vvar WHERE name='ci-comment'");
  2312   2314       prepare_commit_comment(&comment, zInit, &sCiInfo, vid);
  2313   2315       if( zInit && zInit[0] && fossil_strcmp(zInit, blob_str(&comment))==0 ){
................................................................................
  2370   2372       zFullname = db_column_text(&q, 1);
  2371   2373       rid = db_column_int(&q, 2);
  2372   2374       crlfOk = db_column_int(&q, 3);
  2373   2375       binOk = db_column_int(&q, 4);
  2374   2376       encodingOk = db_column_int(&q, 5);
  2375   2377   
  2376   2378       blob_zero(&content);
  2377         -    blob_read_from_file(&content, zFullname, RepoFILE);
         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  +    }
  2378   2385       /* Do not emit any warnings when they are disabled. */
  2379   2386       if( !noWarningFlag ){
  2380   2387         abortCommit |= commit_warning(&content, crlfOk, binOk,
  2381   2388                                       encodingOk, noPrompt,
  2382   2389                                       zFullname, 0);
  2383   2390       }
  2384   2391       if( contains_merge_marker(&content) ){
................................................................................
  2482   2489     if( dryRunFlag ){
  2483   2490       blob_write_to_file(&manifest, "");
  2484   2491     }
  2485   2492     if( outputManifest & MFESTFLG_RAW ){
  2486   2493       zManifestFile = mprintf("%smanifest", g.zLocalRoot);
  2487   2494       blob_write_to_file(&manifest, zManifestFile);
  2488   2495       blob_reset(&manifest);
  2489         -    blob_read_from_file(&manifest, zManifestFile, ExtFILE);
         2496  +    blob_read_from_file(&manifest, zManifestFile);
  2490   2497       free(zManifestFile);
  2491   2498     }
  2492   2499   
  2493   2500     nvid = content_put(&manifest);
  2494   2501     if( nvid==0 ){
  2495   2502       fossil_fatal("trouble committing manifest: %s", g.zErrMsg);
  2496   2503     }

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_setexe(blob_str(&filename), isExe);
          119  +    file_wd_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   

Deleted 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, ExtFILE);
           51  +    blob_read_from_file(pOut, zIn);
    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], ExtFILE) ){
          149  +  if( -1 != file_size(g.argv[3]) ){
   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();
   172    171       db_create_repository(g.argv[3]);
   173    172       db_open_repository(g.argv[3]);
   174         -    db_open_config(0,0);
   175    173       db_begin_transaction();
   176    174       db_record_repository_filename(g.argv[3]);
   177    175       db_initial_setup(0, 0, zDefaultUser);
   178    176       user_select();
   179    177       db_set("content-schema", CONTENT_SCHEMA, 0);
   180    178       db_set("aux-schema", AUX_SCHEMA_MAX, 0);
   181    179       db_set("rebuilt", get_version(), 0);

Changes to src/codecheck1.c.

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

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

Deleted 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], ExtFILE)<0 ){
           58  +  if( blob_read_from_file(&orig, g.argv[2])<0 ){
    59     59       fossil_fatal("cannot read %s", g.argv[2]);
    60     60     }
    61         -  if( blob_read_from_file(&target, g.argv[3], ExtFILE)<0 ){
           61  +  if( blob_read_from_file(&target, g.argv[3])<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], ExtFILE)<0 ){
           89  +  if( blob_read_from_file(&orig, g.argv[2])<0 ){
    90     90       fossil_fatal("cannot read %s", g.argv[2]);
    91     91     }
    92         -  if( blob_read_from_file(&target, g.argv[3], ExtFILE)<0 ){
           92  +  if( blob_read_from_file(&target, g.argv[3])<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], ExtFILE)<0 ){
          157  +  if( blob_read_from_file(&orig, g.argv[2])<0 ){
   158    158       fossil_fatal("cannot read %s", g.argv[2]);
   159    159     }
   160         -  if( blob_read_from_file(&delta, g.argv[3], ExtFILE)<0 ){
          160  +  if( blob_read_from_file(&delta, g.argv[3])<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], ExtFILE);
   188         -  blob_read_from_file(&f2, g.argv[3], ExtFILE);
          187  +  blob_read_from_file(&f1, g.argv[2]);
          188  +  blob_read_from_file(&f2, g.argv[3]);
   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], ExtFILE);
         2020  +  blob_read_from_file(&a, g.argv[2]);
  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], ExtFILE);
         2023  +    blob_read_from_file(&b, g.argv[i]);
  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], ExtFILE);
  2062         -  blob_read_from_file(&b, g.argv[3], ExtFILE);
         2061  +  blob_read_from_file(&a, g.argv[2]);
         2062  +  blob_read_from_file(&b, g.argv[3]);
  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>
  2463   2470   
  2464   2471     if( !ann.bMoreToDo ){
  2465   2472       assert( ann.origId==0 );  /* bMoreToDo always set for a point-to-point */
  2466   2473       @ <h2>Origin for each line in
  2467   2474       @ %z(href("%R/finfo?name=%h&ci=%!S", zFilename, zCI))%h(zFilename)</a>
  2468   2475       @ from check-in %z(href("%R/info/%!S",zCI))%S(zCI)</a>:</h2>
  2469   2476     }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 . <Control-q> exit
   238         -bind . <Control-p> {catch searchPrev; break}
   239         -bind . <Control-n> {catch searchNext; break}
   240         -bind . <Escape><Escape> exit
          237  +# bind . <q> exit
          238  +# bind . <p> {catch searchPrev; break}
          239  +# bind . <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 .
   401    400     } else {
   402    401       set ::search .txtA
   403    402       if {![winfo exists .bb.sframe]} {
   404    403         frame .bb.sframe
   405    404         ::ttk::entry .bb.sframe.e -width 10
   406    405         pack .bb.sframe.e -side left -fill y -expand 1
   407    406         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_size(zFile2, ExtFILE)<0 ){
          185  +    if( file_wd_size(zFile2)<0 ){
   186    186         zName2 = NULL_DEVICE;
   187    187       }else{
   188         -      blob_read_from_file(&file2, zFile2, ExtFILE);
          188  +      if( file_wd_islink(0) ){
          189  +        blob_read_link(&file2, zFile2);
          190  +      }else{
          191  +        blob_read_from_file(&file2, zFile2);
          192  +      }
   189    193         zName2 = zName;
   190    194       }
   191    195   
   192    196       /* Compute and output the differences */
   193    197       if( diffFlags & DIFF_BRIEF ){
   194    198         if( blob_compare(pFile1, &file2) ){
   195    199           fossil_print("CHANGED  %s\n", zName);
................................................................................
   231    235             fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY);
   232    236             glob_free(pBinary);
   233    237             return;
   234    238           }
   235    239           glob_free(pBinary);
   236    240         }
   237    241         blob_zero(&file2);
   238         -      if( file_size(zFile2, ExtFILE)>=0 ){
   239         -        blob_read_from_file(&file2, zFile2, ExtFILE);
          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  +        }
   240    248         }
   241    249         if( looks_like_binary(&file2) ){
   242    250           fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY);
   243    251           blob_reset(&file2);
   244    252           return;
   245    253         }
   246    254         blob_reset(&file2);
................................................................................
   479    487         if( !isNumStat ){ fossil_print("ADDED_BY_INTEGRATE %s\n", zPathname); }
   480    488         srcid = 0;
   481    489         if( !asNewFile ){ showDiff = 0; }
   482    490       }
   483    491       if( showDiff ){
   484    492         Blob content;
   485    493         int isBin;
   486         -      if( !isLink != !file_islink(zFullName) ){
          494  +      if( !isLink != !file_wd_islink(zFullName) ){
   487    495           diff_print_index(zPathname, diffFlags);
   488    496           diff_print_filenames(zPathname, zPathname, diffFlags);
   489    497           fossil_print("%s",DIFF_CANNOT_COMPUTE_SYMLINK);
   490    498           continue;
   491    499         }
   492    500         if( srcid>0 ){
   493    501           content_get(srcid, &content);
................................................................................
   955    963                         diffFlags, pFileDir);
   956    964     }
   957    965     if( pFileDir ){
   958    966       int i;
   959    967       for(i=0; pFileDir[i].zName; i++){
   960    968         if( pFileDir[i].nUsed==0
   961    969          && strcmp(pFileDir[0].zName,".")!=0
   962         -       && !file_isdir(g.argv[i+2], ExtFILE)
          970  +       && !file_wd_isdir(g.argv[i+2])
   963    971         ){
   964    972           fossil_fatal("not found: '%s'", g.argv[i+2]);
   965    973         }
   966    974         fossil_free(pFileDir[i].zName);
   967    975       }
   968    976       fossil_free(pFileDir);
   969    977     }

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 class='sortable mimetypetable' border=1 cellpadding=0 \
   388         -  @ data-column-types='tt' data-init-sort='1'>
          387  +  @ <table id='mimeTable' border=1 cellpadding=0 class='mimetypetable'>
   389    388     @ <thead>
   390    389     @ <tr><th>Suffix<th>Mimetype
   391    390     @ </thead>
   392    391     @ <tbody>
   393    392     for(i=0; i<count(aMime); i++){
   394    393       @ <tr><td>%h(aMime[i].zSuffix)<td>%h(aMime[i].zMimetype)</tr>
   395    394     }
   396    395     @ </tbody></table>
   397         -  style_table_sorter();
          396  +  output_table_sorting_javascript("mimeTable","tt",1);
   398    397     style_footer();
   399    398   }
   400    399   
   401    400   /*
   402    401   ** Check to see if the file in the pContent blob is "embedded HTML".  Return
   403    402   ** true if it is, and fill pTitle with the document title.
   404    403   **
................................................................................
   648    647           zDfltTitle = zName;
   649    648         }
   650    649       }else if( fossil_strcmp(zCheckin,"ckout")==0 ){
   651    650         /* Read from the local checkout */
   652    651         char *zFullpath;
   653    652         db_must_be_within_tree();
   654    653         zFullpath = mprintf("%s/%s", g.zLocalRoot, zName);
   655         -      if( file_isfile(zFullpath, RepoFILE)
   656         -       && blob_read_from_file(&filebody, zFullpath, RepoFILE)>0 ){
          654  +      if( file_isfile(zFullpath)
          655  +       && blob_read_from_file(&filebody, zFullpath)>0 ){
   657    656           rid = 1;  /* Fake RID just to get the loop to end */
   658    657         }
   659    658         fossil_free(zFullpath);
   660    659       }else{
   661    660         vid = name_to_typed_rid(zCheckin, "ci");
   662    661         rid = doc_load_content(vid, zName, &filebody);
   663    662       }

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         -** Required query parameter:
          348  +** Parameters:
   349    349   **
   350         -**    name=ID           Hex hash ID of the technote. If omitted, a new
          350  +**    name=ID           Hex hash ID of the tech-note. 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
   362    352   */
   363    353   void eventedit_page(void){
   364    354     char *zTag;
   365    355     int rid = 0;
   366    356     Blob event;
   367    357     const char *zId;
   368    358     int n;
   369    359     const char *z;
   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 */
          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");
   377    366     int isNew = 0;
   378    367   
   379    368     if( zBody ){
   380    369       zBody = mprintf("%s", zBody);
   381    370     }
   382    371     login_check_credentials();
   383    372     zId = P("name");
................................................................................
   416    405     /* Figure out the color */
   417    406     if( rid ){
   418    407       zClr = db_text("", "SELECT bgcolor FROM event WHERE objid=%d", rid);
   419    408     }else{
   420    409       zClr = "";
   421    410       isNew = 1;
   422    411     }
   423         -  if( P("newclr") ){
   424         -    zClr = PD("clr",zClr);
   425         -    if( zClr[0] ) zClrFlag = " checked";
   426         -  }
          412  +  zClr = PD("clr",zClr);
          413  +  if( fossil_strcmp(zClr,"##")==0 ) zClr = PD("cclr","");
          414  +
   427    415   
   428    416     /* If editing an existing event, extract the key fields to use as
   429    417     ** a starting point for the edit.
   430    418     */
   431    419     if( rid
   432    420      && (zBody==0 || zETime==0 || zComment==0 || zTags==0 || zMimetype==0)
   433    421     ){
................................................................................
   452    440         );
   453    441       }
   454    442     }
   455    443     zETime = db_text(0, "SELECT coalesce(datetime(%Q),datetime('now'))", zETime);
   456    444     if( P("submit")!=0 && (zBody!=0 && zComment!=0) ){
   457    445       login_verify_csrf_secret();
   458    446       if ( !event_commit_common(rid, zId, zBody, zETime,
   459         -                              zMimetype, zComment, zTags,
   460         -                              zClrFlag[0] ? zClr : 0) ){
          447  +                              zMimetype, zComment, zTags, zClr) ){
   461    448         style_header("Error");
   462    449         @ Internal error:  Fossil tried to make an invalid artifact for
   463    450         @ the edited technote.
   464    451         style_footer();
   465    452         return;
   466    453       }
   467         -    cgi_redirectf("%R/technote?name=%T", zId);
          454  +    cgi_redirectf("technote?name=%T", zId);
   468    455     }
   469    456     if( P("cancel")!=0 ){
   470         -    cgi_redirectf("%R/technote?name=%T", zId);
          457  +    cgi_redirectf("technote?name=%T", zId);
   471    458       return;
   472    459     }
   473    460     if( zBody==0 ){
   474    461       zBody = mprintf("Insert new content here...");
   475    462     }
   476    463     if( isNew ){
   477    464       style_header("New Tech-note %S", zId);
................................................................................
   479    466       style_header("Edit Tech-note %S", zId);
   480    467     }
   481    468     if( P("preview")!=0 ){
   482    469       Blob com;
   483    470       @ <p><b>Timeline comment preview:</b></p>
   484    471       @ <blockquote>
   485    472       @ <table border="0">
   486         -    if( zClrFlag[0] && zClr && zClr[0] ){
          473  +    if( zClr && zClr[0] ){
   487    474         @ <tr><td style="background-color: %h(zClr);">
   488    475       }else{
   489    476         @ <tr><td>
   490    477       }
   491    478       blob_zero(&com);
   492    479       blob_append(&com, zComment, -1);
   493    480       wiki_convert(&com, 0, WIKI_INLINE|WIKI_NOBADLINKS);
................................................................................
   520    507     @ <td valign="top">
   521    508     @ <textarea name="c" class="technoteedit" cols="80"
   522    509     @  rows="3" wrap="virtual">%h(zComment)</textarea>
   523    510     @ </td></tr>
   524    511   
   525    512     @ <tr><th align="right" valign="top">Timeline Background Color:</th>
   526    513     @ <td valign="top">
   527         -  @ <input type='checkbox' name='newclr'%s(zClrFlag) />
   528         -  @ Use custom color: \
   529         -  @ <input type='color' name='clr' value='%s(zClr[0]?zClr:"#c0f0ff")'>
          514  +  render_color_chooser(0, zClr, 0, "clr", "cclr");
   530    515     @ </td></tr>
   531    516   
   532    517     @ <tr><th align="right" valign="top">Tags:</th>
   533    518     @ <td valign="top">
   534    519     @   <input type="text" name="g" size="40" value="%h(zTags)" />
   535    520     @ </td></tr>
   536    521   
................................................................................
   542    527     @ <tr><th align="right" valign="top">Page&nbsp;Content:</th>
   543    528     @ <td valign="top">
   544    529     @ <textarea name="w" class="technoteedit" cols="80"
   545    530     @  rows="%d(n)" wrap="virtual">%h(zBody)</textarea>
   546    531     @ </td></tr>
   547    532   
   548    533     @ <tr><td colspan="2">
          534  +  @ <input type="submit" name="preview" value="Preview Your Changes" />
          535  +  @ <input type="submit" name="submit" value="Apply These Changes" />
   549    536     @ <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         -  }
   554    537     @ </td></tr></table>
   555    538     @ </div></form>
   556    539     style_footer();
   557    540   }
   558    541   
   559    542   /*
   560    543   ** 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/SHA-3 of artifact corresponding to rid.
           40  +**   -uuid: The SHA-1 of artifact corresponding to rid.
    41     41   */
    42     42   struct mark_t{
    43     43     char *name;
    44     44     int rid;
    45         -  char uuid[65];
           45  +  char uuid[41];
    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 && strlen(cur_tok)!=64) ){
          345  +  if( !cur_tok || strlen(cur_tok)!=40 ){
   346    346       free(mark->name);
   347         -    fossil_trace("Invalid SHA-1/SHA-3 in marks file: %s\n", cur_tok);
          347  +    fossil_trace("Invalid SHA-1 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/SHA-3 in marks file: %s\n", mark->uuid);
          356  +    fossil_trace("Non-existent SHA-1 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.
    19     24   */
    20     25   #include "config.h"
    21     26   #include <sys/types.h>
    22     27   #include <sys/stat.h>
    23     28   #include <unistd.h>
    24     29   #include <stdio.h>
    25     30   #include <string.h>
................................................................................
    35     40   # include <sys/utime.h>
    36     41   #else
    37     42   # include <sys/time.h>
    38     43   #endif
    39     44   
    40     45   #if INTERFACE
    41     46   
    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         -
    68     47   #include <dirent.h>
    69     48   #if defined(_WIN32)
    70     49   # define DIR _WDIR
    71     50   # define dirent _wdirent
    72     51   # define opendir _wopendir
    73     52   # define readdir _wreaddir
    74     53   # define closedir _wclosedir
    75     54   #endif /* _WIN32 */
    76     55   
    77     56   #if defined(_WIN32) && (defined(__MSVCRT__) || defined(_MSC_VER))
    78         -/*
    79         -** File status information for windows systems.
    80         -*/
    81     57   struct fossilStat {
    82     58       i64 st_size;
    83     59       i64 st_mtime;
    84     60       int st_mode;
    85     61   };
    86     62   #endif
    87     63   
................................................................................
    90     66   #else
    91     67   # define fossil_isdirsep(a)    ((a) == '/')
    92     68   #endif
    93     69   
    94     70   #endif /* INTERFACE */
    95     71   
    96     72   #if !defined(_WIN32) || !(defined(__MSVCRT__) || defined(_MSC_VER))
    97         -/*
    98         -** File status information for unix systems
    99         -*/
   100     73   # define fossilStat stat
   101     74   #endif
   102     75   
   103     76   /*
   104     77   ** On Windows S_ISLNK always returns FALSE.
   105     78   */
   106     79   #if !defined(S_ISLNK)
   107     80   # define S_ISLNK(x) (0)
   108     81   #endif
           82  +static int fileStatValid = 0;
           83  +static struct fossilStat fileStat;
   109     84   
   110     85   /*
   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.
           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.
   120     88   **
   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.
   129     89   */
   130     90   static int fossil_stat(
   131     91     const char *zFilename,  /* name of file or directory to inspect. */
   132     92     struct fossilStat *buf, /* pointer to buffer where info should go. */
   133         -  int eFType              /* Look at symlink itself if RepoFILE and enabled. */
           93  +  int isWd                /* non-zero to consider look at symlink itself. */
   134     94   ){
   135     95     int rc;
   136     96     void *zMbcs = fossil_utf8_to_path(zFilename, 0);
   137     97   #if !defined(_WIN32)
   138         -  if( eFType>=RepoFILE && (eFType==SymFILE || db_allow_symlinks()) ){
           98  +  if( isWd && db_allow_symlinks() ){
   139     99       rc = lstat(zMbcs, buf);
   140    100     }else{
   141    101       rc = stat(zMbcs, buf);
   142    102     }
   143    103   #else
   144         -  rc = win32_stat(zMbcs, buf, eFType);
          104  +  rc = win32_stat(zMbcs, buf, isWd);
   145    105   #endif
   146    106     fossil_path_free(zMbcs);
   147    107     return rc;
   148    108   }
   149    109   
   150    110   /*
   151         -** Clears the fx.fileStat variable and its associated validity flag.
          111  +** Clears the fileStat variable and its associated validity flag.
   152    112   */
   153    113   static void resetStat(){
   154         -  fx.fileStatValid = 0;
   155         -  memset(&fx.fileStat, 0, sizeof(struct fossilStat));
          114  +  fileStatValid = 0;
          115  +  memset(&fileStat, 0, sizeof(struct fossilStat));
   156    116   }
   157    117   
   158    118   /*
   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
          119  +** Fill in the fileStat variable for the file named zFilename.
          120  +** If zFilename==0, then use the previous value of fileStat if
   161    121   ** there is a previous value.
          122  +**
          123  +** If isWd is TRUE, do lstat() instead of stat() if allow-symlinks is on.
   162    124   **
   163    125   ** Return the number of errors.  No error messages are generated.
   164    126   */
   165         -static int getStat(const char *zFilename, int eFType){
          127  +static int getStat(const char *zFilename, int isWd){
   166    128     int rc = 0;
   167    129     if( zFilename==0 ){
   168         -    if( fx.fileStatValid==0 ) rc = 1;
          130  +    if( fileStatValid==0 ) rc = 1;
   169    131     }else{
   170         -    if( fossil_stat(zFilename, &fx.fileStat, eFType)!=0 ){
   171         -      fx.fileStatValid = 0;
          132  +    if( fossil_stat(zFilename, &fileStat, isWd)!=0 ){
          133  +      fileStatValid = 0;
   172    134         rc = 1;
   173    135       }else{
   174         -      fx.fileStatValid = 1;
          136  +      fileStatValid = 1;
   175    137         rc = 0;
   176    138       }
   177    139     }
   178    140     return rc;
   179    141   }
   180    142   
   181    143   /*
   182    144   ** Return the size of a file in bytes.  Return -1 if the file does not
   183    145   ** exist.  If zFilename is NULL, return the size of the most recently
   184    146   ** stat-ed file.
   185    147   */
   186         -i64 file_size(const char *zFilename, int eFType){
   187         -  return getStat(zFilename, eFType) ? -1 : fx.fileStat.st_size;
          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;
   188    157   }
   189    158   
   190    159   /*
   191    160   ** Return the modification time for a file.  Return -1 if the file
   192    161   ** does not exist.  If zFilename is NULL return the size of the most
   193    162   ** recently stat-ed file.
   194    163   */
   195         -i64 file_mtime(const char *zFilename, int eFType){
   196         -  return getStat(zFilename, eFType) ? -1 : fx.fileStat.st_mtime;
          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;
   197    173   }
   198    174   
   199    175   /*
   200    176   ** Return the mode bits for a file.  Return -1 if the file does not
   201    177   ** exist.  If zFilename is NULL return the size of the most recently
   202    178   ** stat-ed file.
   203    179   */
   204         -int file_mode(const char *zFilename, int eFType){
   205         -  return getStat(zFilename, eFType) ? -1 : fx.fileStat.st_mode;
          180  +int file_mode(const char *zFilename){
          181  +  return getStat(zFilename, 0) ? -1 : fileStat.st_mode;
   206    182   }
   207    183   
   208    184   /*
   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
          185  +** Same as file_mode(), but takes into account symlinks.
   215    186   */
   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);
          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);
   221    199   }
   222    200   
   223    201   /*
   224    202   ** Return TRUE if the named file is an ordinary file.  Return false
   225    203   ** for directories, devices, fifos, symlinks, etc.
   226    204   */
   227         -int file_isfile(const char *zFilename, int eFType){
   228         -  return getStat(zFilename, eFType) ? 0 : S_ISREG(fx.fileStat.st_mode);
          205  +int file_isfile(const char *zFilename){
          206  +  return getStat(zFilename, 0) ? 0 : S_ISREG(fileStat.st_mode);
   229    207   }
   230    208   
   231    209   /*
   232         -** Create a symbolic link named zLinkFile that points to zTargetFile.
          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.
   233    219   **
   234         -** If allow-symlinks is off, create an ordinary file named zLinkFile
   235         -** with the name of zTargetFile as its content.
          220  +** Arguments: target file (symlink will point to it), link file
   236    221   **/
   237    222   void symlink_create(const char *zTargetFile, const char *zLinkFile){
   238    223   #if !defined(_WIN32)
   239    224     if( db_allow_symlinks() ){
   240    225       int i, nName;
   241    226       char *zName, zBuf[1000];
   242    227   
................................................................................
   247    232         zName = zBuf;
   248    233         memcpy(zName, zLinkFile, nName+1);
   249    234       }
   250    235       nName = file_simplify_name(zName, nName, 0);
   251    236       for(i=1; i<nName; i++){
   252    237         if( zName[i]=='/' ){
   253    238           zName[i] = 0;
   254         -        if( file_mkdir(zName, ExtFILE, 1) ){
   255         -          fossil_fatal_recursive("unable to create directory %s", zName);
   256         -          return;
   257         -        }
          239  +          if( file_mkdir(zName, 1) ){
          240  +            fossil_fatal_recursive("unable to create directory %s", zName);
          241  +            return;
          242  +          }
   258    243           zName[i] = '/';
   259    244         }
   260    245       }
   261    246       if( symlink(zTargetFile, zName)!=0 ){
   262    247         fossil_fatal_recursive("unable to create symlink \"%s\"", zName);
   263    248       }
   264    249       if( zName!=zBuf ) free(zName);
................................................................................
   283    268   }
   284    269   
   285    270   /*
   286    271   ** Return file permissions (normal, executable, or symlink):
   287    272   **   - PERM_EXE on Unix if file is executable;
   288    273   **   - PERM_LNK on Unix if file is symlink and allow-symlinks option is on;
   289    274   **   - 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.
   295    275   */
   296         -int file_perm(const char *zFilename, int eFType){
          276  +int file_wd_perm(const char *zFilename){
   297    277   #if !defined(_WIN32)
   298         -  if( !getStat(zFilename, RepoFILE) ){
   299         -     if( S_ISREG(fx.fileStat.st_mode) && ((S_IXUSR)&fx.fileStat.st_mode)!=0 )
          278  +  if( !getStat(zFilename, 1) ){
          279  +     if( S_ISREG(fileStat.st_mode) && ((S_IXUSR)&fileStat.st_mode)!=0 )
   300    280         return PERM_EXE;
   301         -    else if( db_allow_symlinks() && S_ISLNK(fx.fileStat.st_mode) )
          281  +    else if( db_allow_symlinks() && S_ISLNK(fileStat.st_mode) )
   302    282         return PERM_LNK;
   303    283     }
   304    284   #endif
   305    285     return PERM_REG;
   306    286   }
   307    287   
   308    288   /*
   309    289   ** Return TRUE if the named file is an executable.  Return false
   310    290   ** for directories, devices, fifos, symlinks, etc.
   311    291   */
   312         -int file_isexe(const char *zFilename, int eFType){
   313         -  return file_perm(zFilename, eFType)==PERM_EXE;
          292  +int file_wd_isexe(const char *zFilename){
          293  +  return file_wd_perm(zFilename)==PERM_EXE;
   314    294   }
   315    295   
   316    296   /*
   317    297   ** Return TRUE if the named file is a symlink and symlinks are allowed.
   318    298   ** Return false for all other cases.
   319    299   **
   320         -** This routines RepoFILE - that zFilename is always a file under management.
   321         -**
   322    300   ** On Windows, always return False.
   323    301   */
   324         -int file_islink(const char *zFilename){
   325         -  return file_perm(zFilename, RepoFILE)==PERM_LNK;
          302  +int file_wd_islink(const char *zFilename){
          303  +  return file_wd_perm(zFilename)==PERM_LNK;
   326    304   }
   327    305   
   328    306   /*
   329    307   ** Return 1 if zFilename is a directory.  Return 0 if zFilename
   330    308   ** does not exist.  Return 2 if zFilename exists but is something
   331    309   ** other than a directory.
   332    310   */
   333         -int file_isdir(const char *zFilename, int eFType){
          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){
   334    332     int rc;
   335    333     char *zFN;
   336    334   
   337    335     zFN = mprintf("%s", zFilename);
   338    336     file_simplify_name(zFN, -1, 0);
   339         -  rc = getStat(zFN, eFType);
          337  +  rc = getStat(zFN, 1);
   340    338     if( rc ){
   341    339       rc = 0; /* It does not exist at all. */
   342         -  }else if( S_ISDIR(fx.fileStat.st_mode) ){
          340  +  }else if( S_ISDIR(fileStat.st_mode) ){
   343    341       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);
   344    347     }else{
   345    348       rc = 2; /* It exists and is something else. */
   346    349     }
   347    350     free(zFN);
   348    351     return rc;
   349    352   }
   350    353   
................................................................................
   393    396   ** Space to hold the new filename is obtained form mprintf() and should
   394    397   ** be freed by the caller.
   395    398   */
   396    399   char *file_newname(const char *zBase, const char *zSuffix, int relFlag){
   397    400     char *z = 0;
   398    401     int cnt = 0;
   399    402     z = mprintf("%s-%s", zBase, zSuffix);
   400         -  while( file_size(z, ExtFILE)>=0 ){
          403  +  while( file_size(z)>=0 ){
   401    404       fossil_free(z);
   402    405       z = mprintf("%s-%s-%d", zBase, zSuffix, cnt++);
   403    406     }
   404    407     if( relFlag ){
   405    408       Blob x;
   406    409       file_relative_name(z, &x, 0);
   407    410       fossil_free(z);
................................................................................
   469    472   */
   470    473   void file_copy(const char *zFrom, const char *zTo){
   471    474     FILE *in, *out;
   472    475     int got;
   473    476     char zBuf[8192];
   474    477     in = fossil_fopen(zFrom, "rb");
   475    478     if( in==0 ) fossil_fatal("cannot open \"%s\" for reading", zFrom);
   476         -  file_mkfolder(zTo, ExtFILE, 0, 0);
          479  +  file_mkfolder(zTo, 0, 0);
   477    480     out = fossil_fopen(zTo, "wb");
   478    481     if( out==0 ) fossil_fatal("cannot open \"%s\" for writing", zTo);
   479    482     while( (got=fread(zBuf, 1, sizeof(zBuf), in))>0 ){
   480    483       fwrite(zBuf, 1, got, out);
   481    484     }
   482    485     fclose(in);
   483    486     fclose(out);
................................................................................
   498    501     }
   499    502     file_copy(g.argv[2], g.argv[3]);
   500    503   }
   501    504   
   502    505   /*
   503    506   ** Set or clear the execute bit on a file.  Return true if a change
   504    507   ** 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.
   509    508   */
   510         -int file_setexe(const char *zFilename, int onoff){
          509  +int file_wd_setexe(const char *zFilename, int onoff){
   511    510     int rc = 0;
   512    511   #if !defined(_WIN32)
   513    512     struct stat buf;
   514         -  if( fossil_stat(zFilename, &buf, RepoFILE)!=0 || S_ISLNK(buf.st_mode) ){
   515         -    return 0;
   516         -  }
          513  +  if( fossil_stat(zFilename, &buf, 1)!=0 || S_ISLNK(buf.st_mode) ) return 0;
   517    514     if( onoff ){
   518    515       int targetMode = (buf.st_mode & 0444)>>2;
   519    516       if( (buf.st_mode & 0100)==0 ){
   520    517         chmod(zFilename, buf.st_mode | targetMode);
   521    518         rc = 1;
   522    519       }
   523    520     }else{
................................................................................
   566    563     if( g.argc!=4 ){
   567    564       usage("FILENAME DATE/TIME");
   568    565     }
   569    566     db_open_or_attach(":memory:", "mem");
   570    567     iMTime = db_int64(0, "SELECT strftime('%%s',%Q)", g.argv[3]);
   571    568     zFile = g.argv[2];
   572    569     file_set_mtime(zFile, iMTime);
   573         -  iMTime = file_mtime(zFile, RepoFILE);
          570  +  iMTime = file_wd_mtime(zFile);
   574    571     zDate = db_text(0, "SELECT datetime(%lld, 'unixepoch')", iMTime);
   575    572     fossil_print("Set mtime of \"%s\" to %s (%lld)\n", zFile, zDate, iMTime);
   576    573   }
   577    574   
   578    575   /*
   579    576   ** Delete a file.
   580    577   **
   581         -** If zFilename is a symbolic link, then it is the link itself that is
   582         -** removed, not the object that zFilename points to.
   583         -**
   584    578   ** Returns zero upon success.
   585    579   */
   586    580   int file_delete(const char *zFilename){
   587    581     int rc;
   588    582   #ifdef _WIN32
   589    583     wchar_t *z = fossil_utf8_to_path(zFilename, 0);
   590    584     rc = _wunlink(z);
................................................................................
   593    587     rc = unlink(zFilename);
   594    588   #endif
   595    589     fossil_path_free(z);
   596    590     return rc;
   597    591   }
   598    592   
   599    593   /*
   600         -** Create a directory called zName, if it does not already exist.
   601         -** If forceFlag is 1, delete any prior non-directory object
          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
   602    596   ** with the same name.
   603    597   **
   604    598   ** Return the number of errors.
   605    599   */
   606         -int file_mkdir(const char *zName, int eFType, int forceFlag){
   607         -  int rc = file_isdir(zName, eFType);
          600  +int file_mkdir(const char *zName, int forceFlag){
          601  +  int rc = file_wd_isdir(zName);
   608    602     if( rc==2 ){
   609    603       if( !forceFlag ) return 1;
   610    604       file_delete(zName);
   611    605     }
   612    606     if( rc!=1 ){
   613    607   #if defined(_WIN32)
   614    608       wchar_t *zMbcs = fossil_utf8_to_path(zName, 1);
................................................................................
   626    620   /*
   627    621   ** Create the tree of directories in which zFilename belongs, if that sequence
   628    622   ** of directories does not already exist.
   629    623   **
   630    624   ** On success, return zero.  On error, return errorReturn if positive, otherwise
   631    625   ** print an error message and abort.
   632    626   */
   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         -){
          627  +int file_mkfolder(const char *zFilename, int forceFlag, int errorReturn){
   639    628     int nName, rc = 0;
   640    629     char *zName;
   641    630   
   642    631     nName = strlen(zFilename);
   643    632     zName = mprintf("%s", zFilename);
   644    633     nName = file_simplify_name(zName, nName, 0);
   645    634     while( nName>0 && zName[nName-1]!='/' ){ nName--; }
   646    635     if( nName ){
   647    636       zName[nName-1] = 0;
   648         -    if( file_isdir(zName, eFType)!=1 ){
   649         -      rc = file_mkfolder(zName, eFType, forceFlag, errorReturn);
          637  +    if( file_wd_isdir(zName)!=1 ){
          638  +      rc = file_mkfolder(zName, forceFlag, errorReturn);
   650    639         if( rc==0 ){
   651         -        if( file_mkdir(zName, eFType, forceFlag)
   652         -         && file_isdir(zName, eFType)!=1
   653         -        ){
          640  +        if( file_mkdir(zName, forceFlag) && file_wd_isdir(zName)!=1 ){
   654    641             if( errorReturn <= 0 ){
   655    642               fossil_fatal_recursive("unable to create directory %s", zName);
   656    643             }
   657    644             rc = errorReturn;
   658    645           }
   659    646         }
   660    647       }
................................................................................
   666    653   /*
   667    654   ** Removes the directory named in the argument, if it exists.  The directory
   668    655   ** must be empty and cannot be the current directory or the root directory.
   669    656   **
   670    657   ** Returns zero upon success.
   671    658   */
   672    659   int file_rmdir(const char *zName){
   673         -  int rc = file_isdir(zName, RepoFILE);
          660  +  int rc = file_wd_isdir(zName);
   674    661     if( rc==2 ) return 1; /* cannot remove normal file */
   675    662     if( rc==1 ){
   676    663   #if defined(_WIN32)
   677    664       wchar_t *zMbcs = fossil_utf8_to_path(zName, 1);
   678    665       rc = _wrmdir(zMbcs);
   679    666   #else
   680    667       char *zMbcs = fossil_utf8_to_path(zName, 1);
................................................................................
   971    958   /*
   972    959   ** Emits the effective or raw stat() information for the specified
   973    960   ** file or directory, optionally preserving the trailing slash and
   974    961   ** resetting the cached stat() information.
   975    962   */
   976    963   static void emitFileStat(
   977    964     const char *zPath,
          965  +  int raw,
   978    966     int slash,
   979    967     int reset
   980    968   ){
   981         -  char zBuf[200];
   982         -  char *z;
          969  +  char zBuf[100];
   983    970     Blob x;
   984         -  int rc;
   985         -  sqlite3_int64 iMtime;
   986         -  struct fossilStat testFileStat;
   987    971     memset(zBuf, 0, sizeof(zBuf));
   988    972     blob_zero(&x);
   989    973     file_canonical_name(zPath, &x, slash);
   990         -  fossil_print("[%s] -> [%s]\n", zPath, blob_buffer(&x));
          974  +  fossil_print("%s[%s] -> [%s]\n", raw ? "RAW " : "", zPath, blob_buffer(&x));
   991    975     blob_reset(&x);
   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();
          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  +  }
  1038   1009   }
  1039   1010   
  1040   1011   /*
  1041   1012   ** COMMAND: test-file-environment
  1042   1013   **
  1043   1014   ** Usage: %fossil test-file-environment FILENAME...
  1044   1015   **
  1045   1016   ** Display the effective file handling subsystem "settings" and then
  1046   1017   ** display file system information about the files specified, if any.
  1047   1018   **
  1048   1019   ** Options:
  1049   1020   **
  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.
         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.
  1054   1024   */
  1055   1025   void cmd_test_file_environment(void){
  1056   1026     int i;
  1057   1027     int slashFlag = find_option("slash",0,0)!=0;
  1058   1028     int resetFlag = find_option("reset",0,0)!=0;
  1059         -  const char *zAllow = find_option("allow-symlinks",0,1);
  1060   1029     if( find_option("open-config", 0, 0)!=0 ){
  1061   1030       Th_OpenConfig(1);
  1062   1031     }
  1063         -  db_find_and_open_repository(OPEN_ANY_SCHEMA, 0);
  1064   1032     fossil_print("filenames_are_case_sensitive() = %d\n",
  1065   1033                  filenames_are_case_sensitive());
  1066   1034     fossil_print("db_allow_symlinks_by_default() = %d\n",
  1067   1035                  db_allow_symlinks_by_default());
  1068         -  if( zAllow ){
  1069         -    g.allowSymlinks = !is_false(zAllow);
  1070         -  }
  1071   1036     fossil_print("db_allow_symlinks() = %d\n", db_allow_symlinks());
  1072   1037     for(i=2; i<g.argc; i++){
  1073         -    emitFileStat(g.argv[i], slashFlag, resetFlag);
         1038  +    emitFileStat(g.argv[i], 1, slashFlag, resetFlag);
         1039  +    emitFileStat(g.argv[i], 0, slashFlag, resetFlag);
  1074   1040     }
  1075   1041   }
  1076   1042   
  1077   1043   /*
  1078   1044   ** COMMAND: test-canonical-name
  1079   1045   **
  1080   1046   ** Usage: %fossil test-canonical-name FILENAME...
................................................................................
  1089   1055     blob_zero(&x);
  1090   1056     for(i=2; i<g.argc; i++){
  1091   1057       char zBuf[100];
  1092   1058       const char *zName = g.argv[i];
  1093   1059       file_canonical_name(zName, &x, slashFlag);
  1094   1060       fossil_print("[%s] -> [%s]\n", zName, blob_buffer(&x));
  1095   1061       blob_reset(&x);
  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));
         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));
  1105   1071     }
  1106   1072   }
  1107   1073   
  1108   1074   /*
  1109   1075   ** Return TRUE if the given filename is canonical.
  1110   1076   **
  1111   1077   ** Canonical names are full pathnames using "/" not "\" and which
................................................................................
  1437   1403     azDirs[2] = fossil_getenv("TMP");
  1438   1404   #else
  1439   1405     azDirs[0] = fossil_getenv("TMPDIR");
  1440   1406   #endif
  1441   1407   
  1442   1408     for(i=0; i<count(azDirs); i++){
  1443   1409       if( azDirs[i]==0 ) continue;
  1444         -    if( !file_isdir(azDirs[i], ExtFILE) ) continue;
         1410  +    if( !file_isdir(azDirs[i]) ) continue;
  1445   1411       zDir = azDirs[i];
  1446   1412       break;
  1447   1413     }
  1448   1414   
  1449   1415     do{
  1450   1416       blob_zero(pBuf);
  1451   1417       if( cnt++>20 ) fossil_panic("cannot generate a temporary filename");
  1452   1418       sqlite3_randomness(15, zRand);
  1453   1419       for(i=0; i<15; i++){
  1454   1420         zRand[i] = (char)zChars[ ((unsigned char)zRand[i])%(sizeof(zChars)-1) ];
  1455   1421       }
  1456   1422       zRand[15] = 0;
  1457   1423       blob_appendf(pBuf, "%s/%s-%s.txt", zDir, zPrefix ? zPrefix : "", zRand);
  1458         -  }while( file_size(blob_str(pBuf), ExtFILE)>=0 );
         1424  +  }while( file_size(blob_str(pBuf))>=0 );
  1459   1425   
  1460   1426   #if defined(_WIN32)
  1461   1427     fossil_path_free((char *)azDirs[0]);
  1462   1428     fossil_path_free((char *)azDirs[1]);
  1463   1429     fossil_path_free((char *)azDirs[2]);
  1464   1430     /* Change all \ characters in the windows path into / so that they can
  1465   1431     ** be safely passed to a subcommand, such as by gdiff */
................................................................................
  1488   1454   }
  1489   1455   
  1490   1456   
  1491   1457   /*
  1492   1458   ** Return true if a file named zName exists and has identical content
  1493   1459   ** to the blob pContent.  If zName does not exist or if the content is
  1494   1460   ** different in any way, then return false.
  1495         -**
  1496         -** This routine assumes RepoFILE
  1497   1461   */
  1498   1462   int file_is_the_same(Blob *pContent, const char *zName){
  1499   1463     i64 iSize;
  1500   1464     int rc;
  1501   1465     Blob onDisk;
  1502   1466   
  1503         -  iSize = file_size(zName, RepoFILE);
         1467  +  iSize = file_wd_size(zName);
  1504   1468     if( iSize<0 ) return 0;
  1505   1469     if( iSize!=blob_size(pContent) ) return 0;
  1506         -  blob_read_from_file(&onDisk, zName, RepoFILE);
         1470  +  if( file_wd_islink(zName) ){
         1471  +    blob_read_link(&onDisk, zName);
         1472  +  }else{
         1473  +    blob_read_from_file(&onDisk, zName);
         1474  +  }
  1507   1475     rc = blob_compare(&onDisk, pContent);
  1508   1476     blob_reset(&onDisk);
  1509   1477     return rc==0;
  1510   1478   }
  1511   1479   
  1512   1480   /*
  1513   1481   ** Return the value of an environment variable as UTF8.
................................................................................
  1542   1510     /* fossil_free(zString); */
  1543   1511   #endif
  1544   1512     return rc;
  1545   1513   }
  1546   1514   
  1547   1515   /*
  1548   1516   ** Like fopen() but always takes a UTF8 argument.
  1549         -**
  1550         -** This function assumes ExtFILE. In other words, symbolic links
  1551         -** are always followed.
  1552   1517   */
  1553   1518   FILE *fossil_fopen(const char *zName, const char *zMode){
  1554   1519   #ifdef _WIN32
  1555   1520     wchar_t *uMode = fossil_utf8_to_unicode(zMode);
  1556   1521     wchar_t *uName = fossil_utf8_to_path(zName, 0);
  1557   1522     FILE *f = _wfopen(uName, uMode);
  1558   1523     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
   286    285   **    n=NUM      Show the first NUM changes only
   287    286   **    brbg       Background color by branch name
   288    287   **    ubg        Background color by user name
   289    288   **    ci=UUID    Ancestors of a particular check-in
   290    289   **    orig=UUID  If both ci and orig are supplied, only show those
   291    290   **                 changes on a direct path from orig to ci.
   292    291   **    showid     Show RID values for debugging
................................................................................
   312    311     GraphContext *pGraph;
   313    312     int brBg = P("brbg")!=0;
   314    313     int uBg = P("ubg")!=0;
   315    314     int fDebug = atoi(PD("debug","0"));
   316    315     int fShowId = P("showid")!=0;
   317    316     Stmt qparent;
   318    317     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 */
   323    318   
   324    319     login_check_credentials();
   325    320     if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
   326    321     style_header("File History");
   327    322     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         -  }
   338    323     url_initialize(&url, "finfo");
   339    324     if( brBg ) url_add_parameter(&url, "brbg", 0);
   340    325     if( uBg ) url_add_parameter(&url, "ubg", 0);
   341    326     baseCheckin = name_to_rid_www("ci");
   342    327     zPrevDate[0] = 0;
   343    328     zFilename = PD("name","");
   344         -  cookie_render();
   345    329     fnid = db_int(0, "SELECT fnid FROM filename WHERE name=%Q", zFilename);
   346    330     if( fnid==0 ){
   347    331       @ No such file: %h(zFilename)
   348    332       style_footer();
   349    333       return;
   350    334     }
   351    335     if( g.perm.Admin ){
................................................................................
   415    399       blob_append_sql(&sql, " LIMIT %d", n);
   416    400       url_add_parameter(&url, "n", P("n"));
   417    401     }
   418    402     db_prepare(&q, "%s", blob_sql_text(&sql));
   419    403     if( P("showsql")!=0 ){
   420    404       @ <p>SQL: %h(blob_str(&sql))</p>
   421    405     }
   422         -  zMark = P("m");
   423         -  if( zMark ){
   424         -    selRid = symbolic_name_to_rid(zMark, "*");
   425         -  }
   426    406     blob_reset(&sql);
   427    407     blob_zero(&title);
   428    408     if( baseCheckin ){
   429    409       char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", baseCheckin);
   430    410       char *zLink = href("%R/info/%!S", zUuid);
   431    411       if( origCheckin ){
   432    412         blob_appendf(&title, "Changes to file ");
................................................................................
   511    491         sqlite3_snprintf(sizeof(zPrevDate), zPrevDate, "%.10s", zDate);
   512    492         @ <tr><td>
   513    493         @   <div class="divider timelineDate">%s(zPrevDate)</div>
   514    494         @ </td><td></td><td></td></tr>
   515    495       }
   516    496       memcpy(zTime, &zDate[11], 5);
   517    497       zTime[5] = 0;
   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>
          498  +    @ <tr><td class="timelineTime">
          499  +    @ %z(href("%R/timeline?c=%t",zDate))%s(zTime)</a></td>
   525    500       @ <td class="timelineGraph"><div id="m%d(gidx)" class="tl-nodemark"></div>
   526    501       @ </td>
   527    502       if( zBgClr && zBgClr[0] ){
   528         -      @ <td class="timeline%s(zStyle)Cell" id='mc%d(gidx)'>
          503  +      @ <td class="timelineTableCell" style="background-color: %h(zBgClr);">
   529    504       }else{
   530         -      @ <td class="timeline%s(zStyle)Cell">
          505  +      @ <td class="timelineTableCell">
   531    506       }
   532         -    if( tmFlags & TIMELINE_COMPACT ){
   533         -      @ <span class='timelineCompactComment' data-id='%d(frid)'>
   534         -    }else{
   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);
          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  +        }
   540    517         }
   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);
   557         -    }
   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>
          518  +      @ %z(href("%R/artifact/%!S",zUuid))[%S(zUuid)]</a>
   562    519         if( fShowId ){
   563    520           int srcId = delta_source_rid(frid);
   564    521           if( srcId>0 ){
   565         -          @ id:&nbsp;%d(frid)&larr;%d(srcId)
          522  +          @ (%d(frid)&larr;%d(srcId))
   566    523           }else{
   567         -          @ id:&nbsp;%d(frid)
          524  +          @ (%d(frid))
   568    525           }
   569    526         }
   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))
          527  +      @ part of check-in
   581    528       }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 ){
   595    529         char *zNewName;
   596    530         zNewName = db_text(0,
   597    531           "SELECT name FROM filename WHERE fnid = "
   598    532           "   (SELECT fnid FROM mlink"
   599    533           "     WHERE mid=%d"
   600    534           "       AND pfnid IN (SELECT fnid FROM filename WHERE name=%Q))",
   601    535           fmid, zFilename);
   602    536         if( zNewName ){
   603    537           @ <b>Renamed</b> to
   604         -        @ %z(href("%R/finfo?name=%t",zNewName))%h(zNewName)</a>
          538  +        @ %z(href("%R/finfo?name=%t",zNewName))%h(zNewName)</a> by check-in
   605    539           fossil_free(zNewName);
   606    540         }else{
   607         -        @ <b>Deleted</b>
          541  +        @ <b>Deleted</b> by check-in
   608    542         }
          543  +    }
          544  +    hyperlink_to_uuid(zCkin);
          545  +    if( fShowId ){
          546  +      @ (%d(fmid))
   609    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))
   610    552       if( g.perm.Hyperlink && zUuid ){
   611    553         const char *z = zFilename;
   612         -      @ <span id='links-%d(frid)'><span class='timelineExtraLinks'>
   613    554         @ %z(href("%R/annotate?filename=%h&checkin=%s",z,zCkin))
   614    555         @ [annotate]</a>
   615    556         @ %z(href("%R/blame?filename=%h&checkin=%s",z,zCkin))
   616    557         @ [blame]</a>
   617         -      @ %z(href("%R/timeline?n=all&uf=%!S",zUuid))[check-ins&nbsp;using]</a>
          558  +      @ %z(href("%R/timeline?n=200&uf=%!S",zUuid))[check-ins&nbsp;using]</a>
   618    559         if( fpid>0 ){
   619         -        @ %z(href("%R/fdiff?v1=%!S&v2=%!S",zPUuid,zUuid))[diff]</a>
          560  +        @ %z(href("%R/fdiff?sbs=1&v1=%!S&v2=%!S",zPUuid,zUuid))[diff]</a>
   620    561         }
   621         -      @ </span></span>
   622    562       }
   623    563       if( fDebug & FINFO_DEBUG_MLINK ){
   624    564         int ii;
   625    565         char *zAncLink;
   626    566         @ <br />fid=%d(frid) pid=%d(fpid) mid=%d(fmid)
   627    567         if( nParent>0 ){
   628    568           @ parents=%d(aParent[0])
................................................................................
   630    570             @ %d(aParent[ii])
   631    571           }
   632    572         }
   633    573         zAncLink = href("%R/finfo?name=%T&ci=%!S&debug=1",zFilename,zCkin);
   634    574         @ %z(zAncLink)[ancestry]</a>
   635    575       }
   636    576       tag_private_status(frid);
   637         -    /* End timelineDetail */
   638         -    if( tmFlags & TIMELINE_COMPACT ){
   639         -      @ </span></span>
   640         -    }else{
   641         -      @ </span>
   642         -    }
   643    577       @ </td></tr>
   644    578     }
   645    579     db_finalize(&q);
   646    580     db_finalize(&qparent);
   647    581     if( pGraph ){
   648    582       graph_finish(pGraph, 1);
   649    583       if( pGraph->nErr ){
................................................................................
   650    584         graph_free(pGraph);
   651    585         pGraph = 0;
   652    586       }else{
   653    587         @ <tr class="timelineBottom"><td></td><td></td><td></td></tr>
   654    588       }
   655    589     }
   656    590     @ </table>
   657         -  timeline_output_graph_javascript(pGraph, TIMELINE_FILEDIFF, iTableId);
          591  +  timeline_output_graph_javascript(pGraph, 0, iTableId, 1);
   658    592     style_footer();
   659    593   }
   660    594   
   661    595   /*
   662    596   ** WEBPAGE: mlink
   663    597   ** URL: /mlink?name=FILENAME
   664    598   ** URL: /mlink?ci=NAME
   665    599   **
   666    600   ** Show all MLINK table entries for a particular file, or for
   667    601   ** a particular check-in.
   668    602   **
   669    603   ** This screen is intended for use by Fossil developers to help
   670         -** in debugging Fossil itself.  Ordinary Fossil users are not
          604  +** in debugging Fossil itself.  Ordinary Fossil users are not 
   671    605   ** expected to know what the MLINK table is or why it is important.
   672    606   **
   673    607   ** To avoid confusing ordinary users, this page is only available
   674         -** to administrators.
          608  +** to adminstrators.
   675    609   */
   676    610   void mlink_page(void){
   677    611     const char *zFName = P("name");
   678    612     const char *zCI = P("ci");
   679    613     Stmt q;
   680    614   
   681    615     login_check_credentials();
................................................................................
   701    635          /* 8 */ "  (SELECT name FROM filename WHERE fnid=mlink.pfnid)"
   702    636          "  FROM mlink, event"
   703    637          " WHERE mlink.fnid=%d"
   704    638          "   AND event.objid=mlink.mid"
   705    639          " ORDER BY 1 DESC",
   706    640          fnid
   707    641       );
   708         -    style_table_sorter();
   709    642       @ <h1>MLINK table for file
   710    643       @ <a href='%R/finfo?name=%t(zFName)'>%h(zFName)</a></h1>
   711    644       @ <div class='brlist'>
   712         -    @ <table class='sortable' data-column-types='tttxtttt' data-init-sort='1'>
          645  +    @ <table id='mlinktable'>
   713    646       @ <thead><tr>
   714    647       @ <th>Date</th>
   715    648       @ <th>Check-in</th>
   716    649       @ <th>Parent<br>Check-in</th>
   717    650       @ <th>Merge?</th>
   718    651       @ <th>New</th>
   719    652       @ <th>Old</th>
................................................................................
   759    692         }
   760    693         @ </tr>
   761    694       }
   762    695       db_finalize(&q);
   763    696       @ </tbody>
   764    697       @ </table>
   765    698       @ </div>
          699  +    output_table_sorting_javascript("mlinktable","tttxtttt",1);
   766    700     }else{
   767    701       int mid = name_to_rid_www("ci");
   768    702       db_prepare(&q,
   769    703          "SELECT"
   770    704          /* 0 */ "  (SELECT name FROM filename WHERE fnid=mlink.fnid),"
   771    705          /* 1 */ "  (SELECT uuid FROM blob WHERE rid=mlink.fid),"
   772    706          /* 2 */ "  pid,"
................................................................................
   776    710          /* 6 */ "  mperm,"
   777    711          /* 7 */ "  isaux"
   778    712          "  FROM mlink WHERE mid=%d ORDER BY 1",
   779    713          mid
   780    714       );
   781    715       @ <h1>MLINK table for check-in %h(zCI)</h1>
   782    716       render_checkin_context(mid, 1);
   783         -    style_table_sorter();
   784    717       @ <hr />
   785    718       @ <div class='brlist'>
   786         -    @ <table class='sortable' data-column-types='ttxtttt' data-init-sort='1'>
          719  +    @ <table id='mlinktable'>
   787    720       @ <thead><tr>
   788    721       @ <th>File</th>
   789    722       @ <th>Parent<br>Check-in</th>
   790    723       @ <th>Merge?</th>
   791    724       @ <th>New</th>
   792    725       @ <th>Old</th>
   793    726       @ <th>Exe<br>Bit?</th>
................................................................................
   830    763         }
   831    764         @ </tr>
   832    765       }
   833    766       db_finalize(&q);
   834    767       @ </tbody>
   835    768       @ </table>
   836    769       @ </div>
          770  +    output_table_sorting_javascript("mlinktable","ttxtttt",1);
   837    771     }
   838    772     style_footer();
   839    773   }

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, ExtFILE, 0) ){
          328  +  if( file_mkdir(zMountPoint, 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;

Deleted 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], ExtFILE);
          137  +  blob_read_from_file(&b, g.argv[2]);
   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, RepoFILE, &hash) ) break;
          138  +      if( sha1sum_file(zFile, &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, RepoFILE, 256, &hash) ) break;
          145  +      if( sha3sum_file(zFile, 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   }

Deleted 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);
   125    124       closesocket(iSocket);
   126    125   #else
   127    126       close(iSocket);
   128    127   #endif
   129    128       iSocket = -1;
   130    129     }
   131    130   }

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, ExtFILE) ){
          114  +      switch( file_isdir(zCaSetting) ){
   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
   207         -   && (fsize = file_size(g.argv[2], ExtFILE))>0
   208         -   && (fsize&0x1ff)==0
   209         -  ){
          206  +  if( g.argc==3 && (fsize = file_size(g.argv[2]))>0 && (fsize&0x1ff)==0 ){
   210    207       db_open_config(0, 0);
   211    208       db_open_repository(g.argv[2]);
   212    209       db_record_repository_filename(g.argv[2]);
   213    210       fossil_print("project-name: %s\n", db_get("project-name", "<unnamed>"));
   214    211       fossil_print("project-code: %s\n", db_get("project-code", "<none>"));
   215    212       showParentProject();
   216    213       extraRepoInfo();
................................................................................
   244    241       rid = name_to_rid(g.argv[2]);
   245    242       if( rid==0 ){
   246    243         fossil_fatal("no such object: %s", g.argv[2]);
   247    244       }
   248    245       show_common_info(rid, "uuid:", 1, 1);
   249    246     }
   250    247   }
          248  +
          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  +}
   251    315   
   252    316   /*
   253    317   ** Show the context graph (immediate parents and children) for
   254    318   ** check-in rid.
   255    319   */
   256    320   void render_checkin_context(int rid, int parentsOnly){
   257    321     Blob sql;
................................................................................
   268    332     if( !parentsOnly ){
   269    333       db_multi_exec(
   270    334         "INSERT OR IGNORE INTO ok SELECT cid FROM plink WHERE pid=%d;", rid
   271    335       );
   272    336     }
   273    337     blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
   274    338     db_prepare(&q, "%s", blob_sql_text(&sql));
   275         -  www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL,
   276         -                     0, 0, rid, 0);
          339  +  www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH, 0, 0, rid, 0);
   277    340     db_finalize(&q);
   278    341   }
   279    342   
   280    343   /*
   281    344   ** Show a graph all wiki, tickets, and check-ins that refer to object zUuid.
   282    345   **
   283    346   ** If zLabel is not NULL and the graph is not empty, then output zLabel as
................................................................................
   299    362     );
   300    363     if( !db_exists("SELECT 1 FROM ok") ) return;
   301    364     if( zLabel ) cgi_printf("%s", zLabel);
   302    365     blob_zero(&sql);
   303    366     blob_append(&sql, timeline_query_for_www(), -1);
   304    367     blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
   305    368     db_prepare(&q, "%s", blob_sql_text(&sql));
   306         -  www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL,
   307         -                     0, 0, 0, 0);
          369  +  www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH, 0, 0, 0, 0);
   308    370     db_finalize(&q);
   309    371   }
   310    372   
   311    373   /*
   312    374   ** WEBPAGE: test-backlinks
   313    375   **
   314    376   ** Show a timeline of all check-ins and other events that have entries
................................................................................
   332    394        " SELECT blob.rid FROM backlink, blob"
   333    395        "  WHERE blob.uuid BETWEEN backlink.target AND (backlink.target||'x')"
   334    396     );
   335    397     blob_zero(&sql);
   336    398     blob_append(&sql, timeline_query_for_www(), -1);
   337    399     blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
   338    400     db_prepare(&q, "%s", blob_sql_text(&sql));
   339         -  www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL,
   340         -                     0, 0, 0, 0);
          401  +  www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH, 0, 0, 0, 0);
   341    402     db_finalize(&q);
   342    403     style_footer();
   343    404   }
   344    405   
   345    406   
   346    407   /*
   347    408   ** Append the difference between artifacts to the output
................................................................................
   417    478       }
   418    479       if( diffFlags ){
   419    480         append_diff(zOld, zNew, diffFlags, pRe);
   420    481       }
   421    482     }else{
   422    483       if( zOld && zNew ){
   423    484         if( fossil_strcmp(zOld, zNew)!=0 ){
   424         -        @ Modified %z(href("%R/finfo?name=%T&m=%!S",zName,zNew))%h(zName)</a>
          485  +        @ Modified %z(href("%R/finfo?name=%T",zName))%h(zName)</a>
   425    486           @ from %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a>
   426    487           @ to %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>.
   427    488         }else if( zOldName!=0 && fossil_strcmp(zName,zOldName)!=0 ){
   428    489           @ Name change
   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>.
          490  +        @ from %z(href("%R/finfo?name=%T",zOldName))%h(zOldName)</a>
          491  +        @ to %z(href("%R/finfo?name=%T",zName))%h(zName)</a>.
   431    492         }else{
   432         -        @ %z(href("%R/finfo?name=%T&m=%!S",zName,zNew))%h(zName)</a> became
          493  +        @ %z(href("%R/finfo?name=%T",zName))%h(zName)</a> became
   433    494           if( mperm==PERM_EXE ){
   434    495             @ executable with contents
   435    496           }else if( mperm==PERM_LNK ){
   436    497             @ a symlink with target
   437    498           }else{
   438    499             @ a regular file with contents
   439    500           }
   440    501           @ %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>.
   441    502         }
   442    503       }else if( zOld ){
   443         -      @ Deleted %z(href("%R/finfo?name=%T&m=%!S",zName,zOld))%h(zName)</a>
          504  +      @ Deleted %z(href("%R/finfo?name=%T",zName))%h(zName)</a>
   444    505         @ version %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a>.
   445    506       }else{
   446         -      @ Added %z(href("%R/finfo?name=%T&m=%!S",zName,zNew))%h(zName)</a>
          507  +      @ Added %z(href("%R/finfo?name=%T",zName))%h(zName)</a>
   447    508         @ version %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>.
   448    509       }
   449    510       if( diffFlags ){
   450    511         append_diff(zOld, zNew, diffFlags, pRe);
   451    512       }else if( zOld && zNew && fossil_strcmp(zOld,zNew)!=0 ){
   452    513         @ &nbsp;&nbsp;
   453         -      @ %z(href("%R/fdiff?v1=%!S&v2=%!S",zOld,zNew))[diff]</a>
          514  +      @ %z(href("%R/fdiff?v1=%!S&v2=%!S&sbs=1",zOld,zNew))[diff]</a>
   454    515       }
   455    516     }
   456    517     @ </p>
   457    518   }
   458    519   
   459    520   /*
   460    521   ** Generate javascript to enhance HTML diffs.
   461    522   */
   462    523   void append_diff_javascript(int sideBySide){
   463    524     if( !sideBySide ) return;
   464         -  style_load_one_js_file("sbsdiff.js");
          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>
   465    552   }
   466    553   
   467    554   /*
   468    555   ** Construct an appropriate diffFlag for text_diff() based on query
   469    556   ** parameters and the to boolean arguments.
   470    557   */
   471         -u64 construct_diff_flags(int diffType){
          558  +u64 construct_diff_flags(int verboseFlag, int sideBySide){
   472    559     u64 diffFlags = 0;  /* Zero means do not show any diff */
   473         -  if( diffType>0 ){
          560  +  if( verboseFlag!=0 ){
   474    561       int x;
   475         -    if( diffType==2 ){
          562  +    if( sideBySide ){
   476    563         diffFlags = DIFF_SIDEBYSIDE;
   477    564   
   478    565         /* "dw" query parameter determines width of each column */
   479    566         x = atoi(PD("dw","80"))*(DIFF_CONTEXT_MASK+1);
   480    567         if( x<0 || x>DIFF_WIDTH_MASK ) x = DIFF_WIDTH_MASK;
   481    568         diffFlags += x;
   482    569       }
................................................................................
   492    579       /* The "noopt" parameter disables diff optimization */
   493    580       if( PD("noopt",0)!=0 ) diffFlags |= DIFF_NOOPT;
   494    581       diffFlags |= DIFF_STRIP_EOLCR;
   495    582     }
   496    583     return diffFlags;
   497    584   }
   498    585   
   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         -}
   613         -
   614    586   /*
   615    587   ** WEBPAGE: vinfo
   616    588   ** WEBPAGE: ci
   617    589   ** URL:  /ci?name=ARTIFACTID
   618    590   ** URL:  /vinfo?name=ARTIFACTID
   619    591   **
   620    592   ** Display information about a particular check-in.
   621    593   **
   622    594   ** We also jump here from /info if the name is a check-in
   623    595   **
   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.
          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.
   627    601   */
   628    602   void ci_page(void){
   629    603     Stmt q1, q2, q3;
   630    604     int rid;
   631    605     int isLeaf;
   632         -  int diffType;        /* 0: no diff,  1: unified,  2: side-by-side */
          606  +  int verboseFlag;     /* True to show diffs */
          607  +  int sideBySide;      /* True for side-by-side diffs */
   633    608     u64 diffFlags;       /* Flag parameter for text_diff() */
   634    609     const char *zName;   /* Name of the check-in to be displayed */
   635    610     const char *zUuid;   /* UUID of zName */
   636    611     const char *zParent; /* UUID of the parent check-in (if any) */
   637    612     const char *zRe;     /* regex parameter */
   638    613     ReCompiled *pRe = 0; /* regex */
   639    614     const char *zW;      /* URL param for ignoring whitespace */
................................................................................
   654    629     if( zRe ) re_compile(&pRe, zRe, 0);
   655    630     zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
   656    631     zParent = db_text(0,
   657    632       "SELECT uuid FROM plink, blob"
   658    633       " WHERE plink.cid=%d AND blob.rid=plink.pid AND plink.isprim",
   659    634       rid
   660    635     );
   661         -  isLeaf = !db_exists("SELECT 1 FROM plink WHERE pid=%d", rid);
          636  +  isLeaf = is_a_leaf(rid);
   662    637     db_prepare(&q1,
   663    638        "SELECT uuid, datetime(mtime,toLocal()), user, comment,"
   664    639        "       datetime(omtime,toLocal()), mtime"
   665    640        "  FROM blob, event"
   666    641        " WHERE blob.rid=%d"
   667    642        "   AND event.objid=%d",
   668    643        rid, rid
   669    644     );
   670         -  
   671         -  cookie_link_parameter("diff","diff","2");
   672         -  diffType = atoi(PD("diff","2"));
          645  +  sideBySide = !is_false(PD("sbs","1"));
   673    646     if( db_step(&q1)==SQLITE_ROW ){
   674    647       const char *zUuid = db_column_text(&q1, 0);
   675    648       int nUuid = db_column_bytes(&q1, 0);
   676    649       char *zEUser, *zEComment;
   677    650       const char *zUser;
   678         -    const char *zOrigUser;
   679    651       const char *zComment;
   680    652       const char *zDate;
   681    653       const char *zOrigDate;
   682    654   
   683    655       style_header("Check-in [%S]", zUuid);
   684    656       login_anonymous_available();
   685    657       zEUser = db_text(0,
   686    658                      "SELECT value FROM tagxref"
   687    659                      " WHERE tagid=%d AND rid=%d AND tagtype>0",
   688    660                       TAG_USER, rid);
   689    661       zEComment = db_text(0,
   690    662                      "SELECT value FROM tagxref WHERE tagid=%d AND rid=%d",
   691    663                      TAG_COMMENT, rid);
   692         -    zOrigUser = db_column_text(&q1, 2);
   693         -    zUser = zEUser ? zEUser : zOrigUser;
          664  +    zUser = db_column_text(&q1, 2);
   694    665       zComment = db_column_text(&q1, 3);
   695    666       zDate = db_column_text(&q1,1);
   696    667       zOrigDate = db_column_text(&q1, 4);
   697         -    if( zOrigDate==0 ) zOrigDate = zDate;
   698    668       @ <div class="section">Overview</div>
   699    669       @ <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         -
   758    670       @ <tr><th>%s(hname_alg(nUuid)):</th><td>%s(zUuid)
   759    671       if( g.perm.Setup ){
   760    672         @ (Record ID: %d(rid))
   761    673       }
   762    674       @ </td></tr>
   763         -    @ <tr><th>User&nbsp;&amp;&nbsp;Date:</th><td>
   764         -    hyperlink_to_user(zUser,zDate," on ");
          675  +    @ <tr><th>Date:</th><td>
   765    676       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  +    }
   766    690       if( zEComment ){
          691  +      @ <tr><th>Edited&nbsp;Comment:</th>
          692  +      @     <td class="infoComment">%!W(zEComment)</td></tr>
   767    693         @ <tr><th>Original&nbsp;Comment:</th>
   768    694         @     <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>");
          695  +    }else{
          696  +      @ <tr><th>Comment:</th><td class="infoComment">%!W(zComment)</td></tr>
   776    697       }
   777    698       if( g.perm.Admin ){
   778    699         db_prepare(&q2,
   779    700            "SELECT rcvfrom.ipaddr, user.login, datetime(rcvfrom.mtime)"
   780    701            "  FROM blob JOIN rcvfrom USING(rcvid) LEFT JOIN user USING(uid)"
   781    702            " WHERE blob.rid=%d",
   782    703            rid
................................................................................
   788    709           if( zUser==0 || zUser[0]==0 ) zUser = "unknown";
   789    710           @ <tr><th>Received&nbsp;From:</th>
   790    711           @ <td>%h(zUser) @ %h(zIpAddr) on %s(zDate)</td></tr>
   791    712         }
   792    713         db_finalize(&q2);
   793    714       }
   794    715       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>
   795    763         @ <tr><th>Other&nbsp;Links:</th>
   796    764         @   <td>
   797         -      @   %z(href("%R/artifact/%!S",zUuid))manifest</a>
   798         -      @ | %z(href("%R/ci_tags/%!S",zUuid))tags</a>
          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>
   799    769         if( g.perm.Admin ){
   800    770           @   | %z(href("%R/mlink?ci=%!S",zUuid))mlink table</a>
   801    771         }
   802    772         if( g.anon.Write ){
   803    773           @   | %z(href("%R/ci_edit?r=%!S",zUuid))edit</a>
   804    774         }
   805    775         @   </td>
   806    776         @ </tr>
          777  +      blob_reset(&projName);
   807    778       }
   808    779       @ </table>
   809    780     }else{
   810    781       style_header("Check-in Information");
   811    782       login_anonymous_available();
   812    783     }
   813    784     db_finalize(&q1);
   814    785     render_backlink_graph(zUuid, "<div class=\"section\">References</div>\n");
          786  +  showTags(rid);
   815    787     @ <div class="section">Context</div>
   816    788     render_checkin_context(rid, 0);
   817    789     @ <div class="section">Changes</div>
   818    790     @ <div class="sectionmenu">
   819         -  diffFlags = construct_diff_flags(diffType);
          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);
   820    798     zW = (diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
   821         -  if( diffType!=0 ){
   822         -    @ %z(chref("button","%R/%s/%T?diff=0",zPageHide,zName))\
          799  +  if( verboseFlag ){
          800  +    @ %z(xhref("class='button'","%R/%s/%T",zPageHide,zName))
   823    801       @ Hide&nbsp;Diffs</a>
   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 ){
          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  +    }
   834    809       if( *zW ){
   835         -      @ %z(chref("button","%R/%s/%T",zPage,zName))
          810  +      @ %z(xhref("class='button'","%R/%s/%T?sbs=%d",zPage,zName,sideBySide))
   836    811         @ Show&nbsp;Whitespace&nbsp;Changes</a>
   837    812       }else{
   838         -      @ %z(chref("button","%R/%s/%T?w",zPage,zName))
          813  +      @ %z(xhref("class='button'","%R/%s/%T?sbs=%d&w",zPage,zName,sideBySide))
   839    814         @ Ignore&nbsp;Whitespace</a>
   840    815       }
          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>
   841    821     }
   842    822     if( zParent ){
   843         -    @ %z(chref("button","%R/vpatch?from=%!S&to=%!S",zParent,zUuid))
          823  +    @ %z(xhref("class='button'","%R/vpatch?from=%!S&to=%!S",zParent,zUuid))
   844    824       @ Patch</a>
   845    825     }
   846    826     if( g.perm.Admin ){
   847         -    @ %z(chref("button","%R/mlink?ci=%!S",zUuid))MLink Table</a>
          827  +    @ %z(xhref("class='button'","%R/mlink?ci=%!S",zUuid))MLink Table</a>
   848    828     }
   849    829     @</div>
   850    830     if( pRe ){
   851    831       @ <p><b>Only differences that match regular expression "%h(zRe)"
   852    832       @ are shown.</b></p>
   853    833     }
   854    834     db_prepare(&q3,
................................................................................
   869    849       int mperm = db_column_int(&q3, 1);
   870    850       const char *zOld = db_column_text(&q3,2);
   871    851       const char *zNew = db_column_text(&q3,3);
   872    852       const char *zOldName = db_column_text(&q3, 4);
   873    853       append_file_change_line(zName, zOld, zNew, zOldName, diffFlags,pRe,mperm);
   874    854     }
   875    855     db_finalize(&q3);
   876         -  append_diff_javascript(diffType==2);
   877         -  cookie_render();
          856  +  append_diff_javascript(sideBySide);
   878    857     style_footer();
   879    858   }
   880    859   
   881    860   /*
   882    861   ** WEBPAGE: winfo
   883    862   ** URL:  /winfo?name=UUID
   884    863   **
................................................................................
  1089   1068   ** to= query parameters.
  1090   1069   **
  1091   1070   ** Query parameters:
  1092   1071   **
  1093   1072   **   from=TAG        Left side of the comparison
  1094   1073   **   to=TAG          Right side of the comparison
  1095   1074   **   branch=TAG      Show all changes on a particular branch
  1096         -**   diff=INTEGER    0: none, 1: unified, 2: side-by-side
         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
  1097   1077   **   glob=STRING     only diff files matching this glob
  1098   1078   **   dc=N            show N lines of context around each diff
  1099   1079   **   w=BOOLEAN       ignore whitespace when computing diffs
  1100   1080   **   nohdr           omit the description at the top of the page
  1101   1081   **
  1102   1082   **
  1103   1083   ** Show all differences between two check-ins.
  1104   1084   */
  1105   1085   void vdiff_page(void){
  1106   1086     int ridFrom, ridTo;
  1107         -  int diffType = 0;        /* 0: none, 1: unified, 2: side-by-side */
         1087  +  int verboseFlag;
         1088  +  int sideBySide;
  1108   1089     u64 diffFlags = 0;
  1109   1090     Manifest *pFrom, *pTo;
  1110   1091     ManifestFile *pFileFrom, *pFileTo;
  1111   1092     const char *zBranch;
  1112   1093     const char *zFrom;
  1113   1094     const char *zTo;
  1114   1095     const char *zRe;
  1115   1096     const char *zW;
         1097  +  const char *zVerbose;
  1116   1098     const char *zGlob;
  1117   1099     ReCompiled *pRe = 0;
  1118   1100     login_check_credentials();
  1119   1101     if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
  1120   1102     login_anonymous_available();
  1121         -  cookie_link_parameter("diff","diff","2");
  1122         -  diffType = atoi(PD("diff","2"));
  1123         -  cookie_render();
  1124   1103     zRe = P("regex");
  1125   1104     if( zRe ) re_compile(&pRe, zRe, 0);
  1126   1105     zBranch = P("branch");
  1127   1106     if( zBranch && zBranch[0] ){
  1128   1107       cgi_replace_parameter("from", mprintf("root:%s", zBranch));
  1129   1108       cgi_replace_parameter("to", zBranch);
  1130   1109     }
  1131   1110     pTo = vdiff_parse_manifest("to", &ridTo);
  1132   1111     if( pTo==0 ) return;
  1133   1112     pFrom = vdiff_parse_manifest("from", &ridFrom);
  1134   1113     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;
  1135   1124     zGlob = P("glob");
  1136   1125     zFrom = P("from");
  1137   1126     zTo = P("to");
  1138   1127     if(zGlob && !*zGlob){
  1139   1128       zGlob = NULL;
  1140   1129     }
  1141         -  diffFlags = construct_diff_flags(diffType);
         1130  +  diffFlags = construct_diff_flags(verboseFlag, sideBySide);
  1142   1131     zW = (diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
  1143   1132     style_submenu_element("Path", "%R/timeline?me=%T&you=%T", zFrom, zTo);
  1144         -  if( diffType!=0 ){
  1145         -    style_submenu_element("Hide Diff", "%R/vdiff?from=%T&to=%T&diff=0%s%T%s",
         1133  +  if( sideBySide || verboseFlag ){
         1134  +    style_submenu_element("Hide Diff", "%R/vdiff?from=%T&to=%T&sbs=0%s%T%s",
  1146   1135                             zFrom, zTo,
  1147   1136                             zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW);
  1148   1137     }
  1149         -  if( diffType!=2 ){
         1138  +  if( !sideBySide ){
  1150   1139       style_submenu_element("Side-by-Side Diff",
  1151         -                          "%R/vdiff?from=%T&to=%T&diff=2%s%T%s",
         1140  +                          "%R/vdiff?from=%T&to=%T&sbs=1%s%T%s",
  1152   1141                             zFrom, zTo,
  1153   1142                             zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW);
  1154   1143     }
  1155         -  if( diffType!=1 ) {
         1144  +  if( sideBySide || !verboseFlag ) {
  1156   1145       style_submenu_element("Unified Diff",
  1157         -                          "%R/vdiff?from=%T&to=%T&diff=1%s%T%s",
         1146  +                          "%R/vdiff?from=%T&to=%T&sbs=0&v%s%T%s",
  1158   1147                             zFrom, zTo,
  1159   1148                             zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW);
  1160   1149     }
  1161   1150     style_submenu_element("Invert",
  1162         -                        "%R/vdiff?from=%T&to=%T&%s%T%s", zTo, zFrom,
         1151  +                        "%R/vdiff?from=%T&to=%T&sbs=%d%s%s%T%s", zTo, zFrom,
         1152  +                        sideBySide, (verboseFlag && !sideBySide)?"&v":"",
  1163   1153                           zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW);
  1164   1154     if( zGlob ){
  1165   1155       style_submenu_element("Clear glob",
  1166         -                          "%R/vdiff?from=%T&to=%T&%s", zFrom, zTo, zW);
         1156  +                          "%R/vdiff?from=%T&to=%T&sbs=%d%s%s", zFrom, zTo,
         1157  +                          sideBySide, (verboseFlag && !sideBySide)?"&v":"", zW);
  1167   1158     }else{
  1168   1159       style_submenu_element("Patch", "%R/vpatch?from=%T&to=%T%s", zFrom, zTo, zW);
  1169   1160     }
  1170         -  if( diffType!=0 ){
         1161  +  if( sideBySide || verboseFlag ){
  1171   1162       style_submenu_checkbox("w", "Ignore Whitespace", 0, 0);
  1172   1163     }
  1173   1164     style_header("Check-in Differences");
  1174   1165     if( P("nohdr")==0 ){
  1175   1166       @ <h2>Difference From:</h2><blockquote>
  1176   1167       checkin_description(ridFrom);
  1177   1168       @ </blockquote><h2>To:</h2><blockquote>
................................................................................
  1226   1217         }
  1227   1218         pFileFrom = manifest_file_next(pFrom, 0);
  1228   1219         pFileTo = manifest_file_next(pTo, 0);
  1229   1220       }
  1230   1221     }
  1231   1222     manifest_destroy(pFrom);
  1232   1223     manifest_destroy(pTo);
  1233         -  append_diff_javascript(diffType==2);
         1224  +  append_diff_javascript(sideBySide);
  1234   1225     style_footer();
  1235   1226   }
  1236   1227   
  1237   1228   #if INTERFACE
  1238   1229   /*
  1239   1230   ** Possible return values from object_description()
  1240   1231   */
................................................................................
  1285   1276   
  1286   1277     db_prepare(&q,
  1287   1278       "SELECT filename.name, datetime(event.mtime,toLocal()),"
  1288   1279       "       coalesce(event.ecomment,event.comment),"
  1289   1280       "       coalesce(event.euser,event.user),"
  1290   1281       "       b.uuid, mlink.mperm,"
  1291   1282       "       coalesce((SELECT value FROM tagxref"
  1292         -                  "  WHERE tagid=%d AND tagtype>0 AND rid=mlink.mid),'trunk'),"
         1283  +                    "  WHERE tagid=%d AND tagtype>0 AND rid=mlink.mid),'trunk'),"
  1293   1284       "       a.size"
  1294   1285       "  FROM mlink, filename, event, blob a, blob b"
  1295   1286       " WHERE filename.fnid=mlink.fnid"
  1296   1287       "   AND event.objid=mlink.mid"
  1297   1288       "   AND a.rid=mlink.fid"
  1298   1289       "   AND b.rid=mlink.mid"
  1299   1290       "   AND mlink.fid=%d"
................................................................................
  1328   1319         }else if( mPerm==PERM_EXE ){
  1329   1320           @ <li>Executable file
  1330   1321           objType |= OBJTYPE_EXE;
  1331   1322         }else{
  1332   1323           @ <li>File
  1333   1324         }
  1334   1325         objType |= OBJTYPE_CONTENT;
  1335         -      @ %z(href("%R/finfo?name=%T&m=%!S",zName,zUuid))%h(zName)</a>
         1326  +      @ %z(href("%R/finfo?name=%T",zName))%h(zName)</a>
  1336   1327         tag_private_status(rid);
  1337   1328         if( showDetail ){
  1338   1329           @ <ul>
  1339   1330         }
  1340   1331         prevName = fossil_strdup(zName);
  1341   1332       }
  1342   1333       if( showDetail ){
................................................................................
  1353   1344       if( zBr && zBr[0] ){
  1354   1345         @ on branch %z(href("%R/timeline?r=%T",zBr))%h(zBr)</a>
  1355   1346       }
  1356   1347       @ &mdash; %!W(zCom) (user:
  1357   1348       hyperlink_to_user(zUser,zDate,",");
  1358   1349       @ size: %d(szFile))
  1359   1350       if( g.perm.Hyperlink ){
         1351  +      @ %z(href("%R/finfo?name=%T&ci=%!S",zName,zVers))[ancestry]</a>
  1360   1352         @ %z(href("%R/annotate?filename=%T&checkin=%!S",zName,zVers))
  1361   1353         @ [annotate]</a>
  1362   1354         @ %z(href("%R/blame?filename=%T&checkin=%!S",zName,zVers))
  1363   1355         @ [blame]</a>
  1364         -      @ %z(href("%R/timeline?n=all&uf=%!S",zUuid))[check-ins&nbsp;using]</a>
  1365   1356       }
  1366   1357       cnt++;
  1367   1358       if( pDownloadName && blob_size(pDownloadName)==0 ){
  1368   1359         blob_append(pDownloadName, zName, -1);
  1369   1360       }
  1370   1361     }
  1371   1362     if( prevName && showDetail ){
................................................................................
  1556   1547   **      sbs=BOOLEAN      Turn side-by-side diffs on and off (default: on)
  1557   1548   **      verbose=BOOLEAN  Show more detail when describing artifacts
  1558   1549   **      w=BOOLEAN        Ignore whitespace
  1559   1550   */
  1560   1551   void diff_page(void){
  1561   1552     int v1, v2;
  1562   1553     int isPatch = P("patch")!=0;
  1563         -  int diffType;          /* 0: none, 1: unified,  2: side-by-side */
         1554  +  int sideBySide = PB("sbs");
         1555  +  int verbose = PB("verbose");
  1564   1556     char *zV1;
  1565   1557     char *zV2;
  1566   1558     const char *zRe;
  1567   1559     ReCompiled *pRe = 0;
  1568   1560     u64 diffFlags;
  1569   1561     u32 objdescFlags = 0;
  1570         -  int verbose = PB("verbose");
  1571   1562   
  1572   1563     login_check_credentials();
  1573   1564     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();
  1577   1565     if( P("from") && P("to") ){
  1578   1566       v1 = artifact_from_ci_and_filename(0, "from");
  1579   1567       v2 = artifact_from_ci_and_filename(0, "to");
  1580   1568     }else{
  1581   1569       Stmt q;
  1582   1570       v1 = name_to_rid_www("v1");
  1583   1571       v2 = name_to_rid_www("v2");
................................................................................
  1625   1613       blob_reset(&c1);
  1626   1614       blob_reset(&c2);
  1627   1615       return;
  1628   1616     }
  1629   1617   
  1630   1618     zV1 = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", v1);
  1631   1619     zV2 = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", v2);
  1632         -  diffFlags = construct_diff_flags(diffType) | DIFF_HTML;
         1620  +  diffFlags = construct_diff_flags(1, sideBySide) | DIFF_HTML;
  1633   1621   
  1634   1622     style_header("Diff");
  1635   1623     style_submenu_checkbox("w", "Ignore Whitespace", 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         -  }
         1624  +  style_submenu_checkbox("sbs", "Side-by-Side Diff", 0, 0);
  1643   1625     style_submenu_checkbox("verbose", "Verbose", 0, 0);
  1644         -  style_submenu_element("Patch", "%R/fdiff?v1=%T&v2=%T&patch",
  1645         -                        P("v1"), P("v2"));
         1626  +  style_submenu_element("Patch", "%s/fdiff?v1=%T&v2=%T&patch",
         1627  +                        g.zTop, P("v1"), P("v2"));
  1646   1628   
  1647   1629     if( P("smhdr")!=0 ){
  1648   1630       @ <h2>Differences From Artifact
  1649   1631       @ %z(href("%R/artifact/%!S",zV1))[%S(zV1)]</a> To
  1650   1632       @ %z(href("%R/artifact/%!S",zV2))[%S(zV2)]</a>.</h2>
  1651   1633     }else{
  1652   1634       @ <h2>Differences From
................................................................................
  1657   1639     }
  1658   1640     if( pRe ){
  1659   1641       @ <b>Only differences that match regular expression "%h(zRe)"
  1660   1642       @ are shown.</b>
  1661   1643     }
  1662   1644     @ <hr />
  1663   1645     append_diff(zV1, zV2, diffFlags, pRe);
  1664         -  append_diff_javascript(diffType);
         1646  +  append_diff_javascript(sideBySide);
  1665   1647     style_footer();
  1666   1648   }
  1667   1649   
  1668   1650   /*
  1669   1651   ** WEBPAGE: raw
  1670   1652   ** URL: /raw?name=ARTIFACTID&m=TYPE
  1671   1653   ** URL: /raw?ci=BRANCH&filename=NAME
................................................................................
  1925   1907         " WHERE iStart <= %d AND iEnd >= %d", n, n);
  1926   1908       if( db_step(&q)==SQLITE_ROW ){
  1927   1909         iStart = db_column_int(&q, 0);
  1928   1910         iEnd = db_column_int(&q, 1);
  1929   1911       }
  1930   1912       db_finalize(&q);
  1931   1913       for(i=0; z[i] && z[i]!='\n'; i++){}
  1932         -    if( n==iTop ) cgi_append_content("<span id=\"scrollToMe\">", -1);
         1914  +    if( n==iTop ) cgi_append_content("<span id=\"topln\">", -1);
  1933   1915       if( n==iStart ){
  1934   1916         cgi_append_content("<div class=\"selectedText\">",-1);
  1935   1917       }
  1936   1918       cgi_printf("%6d  ", n);
  1937   1919       if( i>0 ){
  1938   1920         char *zHtml = htmlize(z, i);
  1939   1921         cgi_append_content(zHtml, -1);
................................................................................
  1944   1926       else cgi_append_content("\n", 1);
  1945   1927       z += i;
  1946   1928       if( z[0]=='\n' ) z++;
  1947   1929     }
  1948   1930     if( n<iEnd ) cgi_printf("</div>");
  1949   1931     @ </pre>
  1950   1932     if( db_int(0, "SELECT EXISTS(SELECT 1 FROM lnos)") ){
  1951         -    style_load_one_js_file("scroll.js");
         1933  +    @ <script>gebi('topln').scrollIntoView(true);</script>
  1952   1934     }
  1953   1935   }
  1954   1936   
  1955   1937   
  1956   1938   /*
  1957   1939   ** WEBPAGE: artifact
  1958   1940   ** WEBPAGE: file
................................................................................
  2225   2207         }
  2226   2208       }
  2227   2209       if( strcmp(zModAction,"approve")==0 ){
  2228   2210         moderation_approve(rid);
  2229   2211       }
  2230   2212     }
  2231   2213     zTktTitle = db_table_has_column("repository", "ticket", "title" )
  2232         -      ? db_text("(No title)", 
  2233         -                "SELECT title FROM ticket WHERE tkt_uuid=%Q", zTktName)
         2214  +      ? db_text("(No title)", "SELECT title FROM ticket WHERE tkt_uuid=%Q", zTktName)
  2234   2215         : 0;
  2235   2216     style_header("Ticket Change Details");
  2236   2217     style_submenu_element("Raw", "%R/artifact/%s", zUuid);
  2237   2218     style_submenu_element("History", "%R/tkthistory/%s", zTktName);
  2238   2219     style_submenu_element("Page", "%R/tktview/%t", zTktName);
  2239   2220     style_submenu_element("Timeline", "%R/tkttimeline/%t", zTktName);
  2240   2221     if( P("plaintext") ){
................................................................................
  2256   2237     }
  2257   2238     @ <tr><th>Ticket:</th>
  2258   2239     @ <td>%z(href("%R/tktview/%s",zTktName))%s(zTktName)</a>
  2259   2240     if( zTktTitle ){
  2260   2241           @<br />%h(zTktTitle)
  2261   2242     }
  2262   2243     @</td></tr>
  2263         -  @ <tr><th>User&nbsp;&amp;&nbsp;Date:</th><td>
  2264         -  hyperlink_to_user(pTktChng->zUser, zDate, " on ");
         2244  +  @ <tr><th>Date:</th><td>
  2265   2245     hyperlink_to_date(zDate, "</td></tr>");
         2246  +  @ <tr><th>User:</th><td>
         2247  +  hyperlink_to_user(pTktChng->zUser, zDate, "</td></tr>");
  2266   2248     @ </table>
  2267   2249     free(zDate);
  2268   2250     free(zTktTitle);
  2269   2251   
  2270   2252     if( g.perm.ModTkt && modPending ){
  2271   2253       @ <div class="section">Moderation</div>
  2272   2254       @ <blockquote>
................................................................................
  2367   2349     if( db_exists("SELECT 1 FROM attachment WHERE attachid=%d", rid) ){
  2368   2350       ainfo_page();
  2369   2351     }else
  2370   2352     {
  2371   2353       artifact_page();
  2372   2354     }
  2373   2355   }
         2356  +
         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  +}
  2374   2477   
  2375   2478   /*
  2376   2479   ** Do a comment comparison.
  2377   2480   **
  2378   2481   ** +  Leading and trailing whitespace are ignored.
  2379   2482   ** +  \r\n characters compare equal to \n
  2380   2483   **
................................................................................
  2525   2628   */
  2526   2629   int is_datetime(const char* zDate){
  2527   2630     return db_int(0, "SELECT datetime(%Q) NOT NULL", zDate);
  2528   2631   }
  2529   2632   
  2530   2633   /*
  2531   2634   ** WEBPAGE: ci_edit
         2635  +** URL:  /ci_edit?r=RID&c=NEWCOMMENT&u=NEWUSER
  2532   2636   **
  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.)
         2637  +** Present a dialog for updating properties of a check-in.
  2536   2638   **
  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":
  2542         -**
  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
  2556         -**
  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
         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
  2560   2644   */
  2561   2645   void ci_edit_page(void){
  2562   2646     int rid;
  2563   2647     const char *zComment;         /* Current comment on the check-in */
  2564   2648     const char *zNewComment;      /* Revised check-in comment */
  2565   2649     const char *zUser;            /* Current user for the check-in */
  2566   2650     const char *zNewUser;         /* Revised user */
  2567   2651     const char *zDate;            /* Current date of the check-in */
  2568   2652     const char *zNewDate;         /* Revised check-in date */
  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 */
         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;
  2578   2661     int fPropagateColor;          /* True if color propagates before edit */
  2579   2662     int fNewPropagateColor;       /* True if color propagates after edit */
  2580   2663     int fHasHidden = 0;           /* True if hidden tag already set */
  2581   2664     int fHasClosed = 0;           /* True if closed tag already set */
  2582         -  const char *zChngTime = 0;    /* Value of chngtime= query param, if any */
         2665  +  const char *zChngTime = 0;     /* Value of chngtime= query param, if any */
  2583   2666     char *zUuid;
  2584   2667     Blob comment;
  2585   2668     char *zBranchName = 0;
  2586   2669     Stmt q;
  2587   2670   
  2588   2671     login_check_credentials();
  2589   2672     if( !g.perm.Write ){ login_needed(g.anon.Write); return; }
  2590   2673     rid = name_to_typed_rid(P("r"), "ci");
  2591   2674     zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
  2592   2675     zComment = db_text(0, "SELECT coalesce(ecomment,comment)"
  2593   2676                           "  FROM event WHERE objid=%d", rid);
  2594   2677     if( zComment==0 ) fossil_redirect_home();
  2595   2678     if( P("cancel") ){
  2596         -    cgi_redirectf("%R/ci/%S", zUuid);
         2679  +    cgi_redirectf("ci?name=%s", zUuid);
  2597   2680     }
  2598   2681     if( g.perm.Setup ) zChngTime = P("chngtime");
  2599   2682     zNewComment = PD("c",zComment);
  2600   2683     zUser = db_text(0, "SELECT coalesce(euser,user)"
  2601   2684                        "  FROM event WHERE objid=%d", rid);
  2602   2685     if( zUser==0 ) fossil_redirect_home();
  2603   2686     zNewUser = PDT("u",zUser);
................................................................................
  2604   2687     zDate = db_text(0, "SELECT datetime(mtime)"
  2605   2688                        "  FROM event WHERE objid=%d", rid);
  2606   2689     if( zDate==0 ) fossil_redirect_home();
  2607   2690     zNewDate = PDT("dt",zDate);
  2608   2691     zColor = db_text("", "SELECT bgcolor"
  2609   2692                           "  FROM event WHERE objid=%d", rid);
  2610   2693     zNewColor = PDT("clr",zColor);
         2694  +  if( fossil_strcmp(zNewColor,"##")==0 ){
         2695  +    zNewColor = PT("clrcust");
         2696  +  }
  2611   2697     fPropagateColor = db_int(0, "SELECT tagtype FROM tagxref"
  2612   2698                                 " WHERE rid=%d AND tagid=%d",
  2613   2699                                 rid, TAG_BGCOLOR)==2;
  2614   2700     fNewPropagateColor = P("clr")!=0 ? P("pclr")!=0 : fPropagateColor;
  2615         -  zNewColorFlag = P("newclr") ? " checked" : "";
  2616   2701     zNewTagFlag = P("newtag") ? " checked" : "";
  2617   2702     zNewTag = PDT("tagname","");
  2618   2703     zNewBrFlag = P("newbr") ? " checked" : "";
  2619   2704     zNewBranch = PDT("brname","");
  2620   2705     zCloseFlag = P("close") ? " checked" : "";
  2621   2706     zHideFlag = P("hide") ? " checked" : "";
  2622   2707     if( P("apply") ){
................................................................................
  2624   2709       char *zNow;
  2625   2710   
  2626   2711       login_verify_csrf_secret();
  2627   2712       blob_zero(&ctrl);
  2628   2713       zNow = date_in_standard_format(zChngTime ? zChngTime : "now");
  2629   2714       blob_appendf(&ctrl, "D %s\n", zNow);
  2630   2715       init_newtags();
  2631         -    if( zNewColorFlag[0]
  2632         -     && zNewColor[0]
         2716  +    if( zNewColor[0]
  2633   2717        && (fPropagateColor!=fNewPropagateColor
  2634   2718                || fossil_strcmp(zColor,zNewColor)!=0)
  2635         -    ){
  2636         -      add_color(zNewColor,fNewPropagateColor);
  2637         -    }
         2719  +    ) add_color(zNewColor,fNewPropagateColor);
         2720  +    if( zNewColor[0]==0 && zColor[0]!=0 ) cancel_color();
  2638   2721       if( comment_compare(zComment,zNewComment)==0 ) add_comment(zNewComment);
  2639   2722       if( fossil_strcmp(zDate,zNewDate)!=0 ) add_date(zNewDate);
  2640   2723       if( fossil_strcmp(zUser,zNewUser)!=0 ) add_user(zNewUser);
  2641   2724       db_prepare(&q,
  2642   2725          "SELECT tag.tagid, tagname FROM tagxref, tag"
  2643   2726          " WHERE tagxref.rid=%d AND tagtype>0 AND tagxref.tagid=tag.tagid",
  2644   2727          rid
................................................................................
  2652   2735       }
  2653   2736       db_finalize(&q);
  2654   2737       if( zHideFlag[0] ) hide_branch();
  2655   2738       if( zCloseFlag[0] ) close_leaf(rid);
  2656   2739       if( zNewTagFlag[0] && zNewTag[0] ) add_tag(zNewTag);
  2657   2740       if( zNewBrFlag[0] && zNewBranch[0] ) change_branch(rid,zNewBranch);
  2658   2741       apply_newtags(&ctrl, rid, zUuid);
  2659         -    cgi_redirectf("%R/ci/%S", zUuid);
         2742  +    cgi_redirectf("ci?name=%s", zUuid);
  2660   2743     }
  2661   2744     blob_zero(&comment);
  2662   2745     blob_append(&comment, zNewComment, -1);
  2663   2746     zUuid[10] = 0;
  2664   2747     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>
  2665   2769     if( P("preview") ){
  2666   2770       Blob suffix;
  2667   2771       int nTag = 0;
  2668   2772       @ <b>Preview:</b>
  2669   2773       @ <blockquote>
  2670   2774       @ <table border=0>
  2671         -    if( zNewColorFlag[0] && zNewColor && zNewColor[0] ){
         2775  +    if( zNewColor && zNewColor[0] ){
  2672   2776         @ <tr><td style="background-color: %h(zNewColor);">
  2673         -    }else if( zColor[0] ){
  2674         -      @ <tr><td style="background-color: %h(zColor);">
  2675   2777       }else{
  2676   2778         @ <tr><td>
  2677   2779       }
  2678   2780       @ %!W(blob_str(&comment))
  2679   2781       blob_zero(&suffix);
  2680   2782       blob_appendf(&suffix, "(user: %h", zNewUser);
  2681   2783       db_prepare(&q, "SELECT substr(tagname,5) FROM tagxref, tag"
................................................................................
  2728   2830     if( zChngTime ){
  2729   2831       @ <tr><th align="right" valign="top">Timestamp of this change:</th>
  2730   2832       @ <td valign="top">
  2731   2833       @   <input type="text" name="chngtime" size="20" value="%h(zChngTime)" />
  2732   2834       @ </td></tr>
  2733   2835     }
  2734   2836   
  2735         -  @ <tr><th align="right" valign="top">Background&nbsp;Color:</th>
         2837  +  @ <tr><th align="right" valign="top">Background Color:</th>
  2736   2838     @ <td valign="top">
  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>
         2839  +  render_color_chooser(fNewPropagateColor, zNewColor, "pclr", "clr", "clrcust");
  2748   2840     @ </td></tr>
  2749   2841   
  2750   2842     @ <tr><th align="right" valign="top">Tags:</th>
  2751   2843     @ <td valign="top">
  2752   2844     @ <label><input type="checkbox" id="newtag" name="newtag"%s(zNewTagFlag) />
  2753   2845     @ Add the following new tag name to this check-in:</label>
  2754         -  @ <input type="text" size='15' name="tagname" value="%h(zNewTag)" \
  2755         -  @ id='tagname' />
         2846  +  @ <input type="text" style="width:15;" name="tagname" value="%h(zNewTag)"
         2847  +  @ onkeyup="gebi('newtag').checked=!!this.value" />
  2756   2848     zBranchName = db_text(0, "SELECT value FROM tagxref, tag"
  2757   2849        " WHERE tagxref.rid=%d AND tagtype>0 AND tagxref.tagid=tag.tagid"
  2758   2850        " AND tagxref.tagid=%d", rid, TAG_BRANCH);
  2759   2851     db_prepare(&q,
  2760   2852        "SELECT tag.tagid, tagname, tagxref.value FROM tagxref, tag"
  2761   2853        " WHERE tagxref.rid=%d AND tagtype>0 AND tagxref.tagid=tag.tagid"
  2762   2854        " ORDER BY CASE WHEN tagname GLOB 'sym-*' THEN substr(tagname,5)"
................................................................................
  2799   2891       zBranchName = db_get("main-branch", "trunk");
  2800   2892     }
  2801   2893     if( !zNewBranch || !zNewBranch[0]){
  2802   2894       zNewBranch = zBranchName;
  2803   2895     }
  2804   2896     @ <tr><th align="right" valign="top">Branching:</th>
  2805   2897     @ <td valign="top">
  2806         -  @ <label><input id="newbr" type="checkbox" name="newbr" \
  2807         -  @ data-branch='%h(zBranchName)'%s(zNewBrFlag) />
         2898  +  @ <label><input id="newbr" type="checkbox" name="newbr"%s(zNewBrFlag)
         2899  +  @ onchange="chgcbn(this.checked,'%h(zBranchName)')" />
  2808   2900     @ Make this check-in the start of a new branch named:</label>
  2809         -  @ <input id="brname" type="text" style="width:15;" name="brname" \
  2810         -  @ value="%h(zNewBranch)" /></td></tr>
         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>
  2811   2904     if( !fHasHidden ){
  2812   2905       @ <tr><th align="right" valign="top">Branch Hiding:</th>
  2813   2906       @ <td valign="top">
  2814   2907       @ <label><input type="checkbox" id="hidebr" name="hide"%s(zHideFlag) />
  2815   2908       @ Hide branch
  2816   2909       @ <span style="font-weight:bold" id="hbranch">%h(zBranchName)</span>
  2817   2910       @ from the timeline starting from this check-in</label>
................................................................................
  2835   2928         @ </td></tr>
  2836   2929       }
  2837   2930     }
  2838   2931     if( zBranchName ) fossil_free(zBranchName);
  2839   2932   
  2840   2933   
  2841   2934     @ <tr><td colspan="2">
  2842         -  @ <input type="submit" name="cancel" value="Cancel" />
  2843   2935     @ <input type="submit" name="preview" value="Preview" />
  2844         -  if( P("preview") ){
  2845         -    @ <input type="submit" name="apply" value="Apply Changes" />
  2846         -  }
         2936  +  @ <input type="submit" name="apply" value="Apply Changes" />
         2937  +  @ <input type="submit" name="cancel" value="Cancel" />
  2847   2938     @ </td></tr>
  2848   2939     @ </table>
  2849   2940     @ </div></form>
  2850         -  style_load_one_js_file("ci_edit.js");
  2851   2941     style_footer();
  2852   2942   }
  2853   2943   
  2854   2944   /*
  2855   2945   ** Prepare an ammended commit comment.  Let the user modify it using the
  2856   2946   ** editor specified in the global_config table or either
  2857   2947   ** the VISUAL or EDITOR environment variable.
................................................................................
  3024   3114       cancel_color();
  3025   3115     }
  3026   3116     if( fEditComment ){
  3027   3117       prepare_amend_comment(&comment, zComment, zUuid);
  3028   3118       zNewComment = blob_str(&comment);
  3029   3119     }else if( zComFile ){
  3030   3120       blob_zero(&comment);
  3031         -    blob_read_from_file(&comment, zComFile, ExtFILE);
         3121  +    blob_read_from_file(&comment, zComFile);
  3032   3122       blob_to_utf8_no_bom(&comment, 1);
  3033   3123       zNewComment = blob_str(&comment);
  3034   3124     }
  3035   3125     if( zNewComment && zNewComment[0]
  3036   3126         && comment_compare(zComment,zNewComment)==0 ) add_comment(zNewComment);
  3037   3127     if( zNewDate && zNewDate[0] && fossil_strcmp(zDate,zNewDate)!=0 ){
  3038   3128       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, "%s", warnMsg );
         1613  +    json_warn( FSL_JSON_W_ROW_TO_JSON_FAILED, 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, ExtFILE);
  1934         -  cson_object_set(jo, "repositorySize", 
  1935         -                  cson_value_new_integer((cson_int_t)fsize));
         1933  +  fsize = file_size(g.zRepositoryName);
         1934  +  cson_object_set(jo, "repositorySize", cson_value_new_integer((cson_int_t)fsize));
  1936   1935   
  1937   1936     if(full){
  1938   1937       n = db_int(0, "SELECT count(*) FROM blob");
  1939   1938       m = db_int(0, "SELECT count(*) FROM delta");
  1940   1939       cson_object_set(jo, "blobCount", cson_value_new_integer((cson_int_t)n));
  1941   1940       cson_object_set(jo, "deltaCount", cson_value_new_integer((cson_int_t)m));
  1942   1941       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,"%s",sawConversionError);
          145  +    json_warn(FSL_JSON_W_COL_TO_JSON_FAILED,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, "%s", opt.rcErrMsg);
          364  +    json_set_err(rc, 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? */
    31     30   static const JsonPageDef JsonPageDefs_Dir[] = {
    32     31   /* Last entry MUST have a NULL name. */
    33     32   {NULL,NULL,0}
    34     33   };
    35         -#endif
    36     34   
    37     35   #if 0 /* TODO: Not used? */
    38     36   static char const * json_dir_path_extra(){
    39     37     static char const * zP = NULL;
    40     38     if( !zP ){
    41     39       zP = g.zExtra;
    42     40       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_isfile_or_link(zFullName) ){
          119  +    }else if( !file_wd_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 -- guerrilla line editing library against the idea that a
     2         - * line editing lib needs to be 20,000 lines of C code.
            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.
     3      5    *
     4      6    * You can find the latest source code at:
     5      7    *
     6      8    *   http://github.com/antirez/linenoise
     7      9    *
     8     10    * Does a number of crazy assumptions that happen to be true in 99.9999% of
     9     11    * the 2010 UNIX computers around.
    10     12    *
    11     13    * ------------------------------------------------------------------------
    12     14    *
    13         - * Copyright (c) 2010-2016, Salvatore Sanfilippo <antirez at gmail dot com>
           15  + * Copyright (c) 2010-2014, Salvatore Sanfilippo <antirez at gmail dot com>
    14     16    * Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
    15     17    *
    16     18    * All rights reserved.
    17     19    *
    18     20    * Redistribution and use in source and binary forms, with or without
    19     21    * modification, are permitted provided that the following conditions are
    20     22    * met:
................................................................................
   101    103    *    Sequence: ESC [ 2 J
   102    104    *    Effect: clear the whole screen
   103    105    *
   104    106    */
   105    107   
   106    108   #include <termios.h>
   107    109   #include <unistd.h>
          110  +#include <stdarg.h>
   108    111   #include <stdlib.h>
   109    112   #include <stdio.h>
   110    113   #include <errno.h>
   111    114   #include <string.h>
   112    115   #include <stdlib.h>
   113    116   #include <ctype.h>
   114         -#include <sys/stat.h>
   115    117   #include <sys/types.h>
   116    118   #include <sys/ioctl.h>
   117    119   #include <unistd.h>
   118    120   #include "linenoise.h"
          121  +#include "sqlite3.h"
   119    122   
   120    123   #define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100
   121    124   #define LINENOISE_MAX_LINE 4096
   122         -static char *unsupported_term[] = {"dumb","cons25","emacs",NULL};
          125  +static const char *unsupported_term[] = {"dumb","cons25","emacs",NULL};
   123    126   static linenoiseCompletionCallback *completionCallback = NULL;
   124         -static linenoiseHintsCallback *hintsCallback = NULL;
   125         -static linenoiseFreeHintsCallback *freeHintsCallback = NULL;
   126    127   
   127    128   static struct termios orig_termios; /* In order to restore at exit.*/
   128    129   static int rawmode = 0; /* For atexit() function to check if restore is needed*/
   129    130   static int mlmode = 0;  /* Multi line mode. Default is single line. */
   130    131   static int atexit_registered = 0; /* Register atexit just 1 time. */
   131    132   static int history_max_len = LINENOISE_DEFAULT_HISTORY_MAX_LEN;
   132    133   static int history_len = 0;
................................................................................
   175    176   static void linenoiseAtExit(void);
   176    177   int linenoiseHistoryAdd(const char *line);
   177    178   static void refreshLine(struct linenoiseState *l);
   178    179   
   179    180   /* Debugging macro. */
   180    181   #if 0
   181    182   FILE *lndebug_fp = NULL;
   182         -#define lndebug(...) \
          183  +#define lndebug(fmt, arg1) \
   183    184       do { \
   184    185           if (lndebug_fp == NULL) { \
   185    186               lndebug_fp = fopen("/tmp/lndebug.txt","a"); \
   186    187               fprintf(lndebug_fp, \
   187    188               "[%d %d %d] p: %d, rows: %d, rpos: %d, max: %d, oldmax: %d\n", \
   188    189               (int)l->len,(int)l->pos,(int)l->oldpos,plen,rows,rpos, \
   189    190               (int)l->maxrows,old_rows); \
   190    191           } \
   191         -        fprintf(lndebug_fp, ", " __VA_ARGS__); \
          192  +        fprintf(lndebug_fp, ", " fmt, arg1); \
   192    193           fflush(lndebug_fp); \
   193    194       } while (0)
   194    195   #else
   195         -#define lndebug(fmt, ...)
          196  +#define lndebug(fmt, arg1)
   196    197   #endif
          198  +
          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
   197    230   
   198    231   /* ======================= Low level terminal handling ====================== */
   199    232   
   200    233   /* Set if to use or not the multi line mode. */
   201    234   void linenoiseSetMultiLine(int ml) {
   202    235       mlmode = ml;
   203    236   }
................................................................................
   210    243   
   211    244       if (term == NULL) return 0;
   212    245       for (j = 0; unsupported_term[j]; j++)
   213    246           if (!strcasecmp(term,unsupported_term[j])) return 1;
   214    247       return 0;
   215    248   }
   216    249   
   217         -/* Raw mode: 1960 magic shit. */
          250  +/* Raw mode */
   218    251   static int enableRawMode(int fd) {
   219    252       struct termios raw;
   220    253   
   221    254       if (!isatty(STDIN_FILENO)) goto fatal;
   222    255       if (!atexit_registered) {
   223    256           atexit(linenoiseAtExit);
   224    257           atexit_registered = 1;
................................................................................
   280    313       if (sscanf(buf+2,"%d;%d",&rows,&cols) != 2) return -1;
   281    314       return cols;
   282    315   }
   283    316   
   284    317   /* Try to get the number of columns in the current terminal, or assume 80
   285    318    * if it fails. */
   286    319   static int getColumns(int ifd, int ofd) {
          320  +#if !defined(__sun__)
   287    321       struct winsize ws;
   288    322   
   289    323       if (ioctl(1, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) {
   290    324           /* ioctl() failed. Try to query the terminal itself. */
   291    325           int start, cols;
   292    326   
   293    327           /* Get the initial position so we can restore it later. */
................................................................................
   309    343           }
   310    344           return cols;
   311    345       } else {
   312    346           return ws.ws_col;
   313    347       }
   314    348   
   315    349   failed:
          350  +#endif
   316    351       return 80;
   317    352   }
   318    353   
   319    354   /* Clear the screen. Used to handle ctrl+l */
   320    355   void linenoiseClearScreen(void) {
   321    356       if (write(STDOUT_FILENO,"\x1b[H\x1b[2J",7) <= 0) {
   322    357           /* nothing to do, just to avoid warning. */
................................................................................
   406    441   }
   407    442   
   408    443   /* Register a callback function to be called for tab-completion. */
   409    444   void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn) {
   410    445       completionCallback = fn;
   411    446   }
   412    447   
   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         -}
   424         -
   425    448   /* This function is used by the callback function registered by the user
   426    449    * in order to add completion options given the input string when the
   427    450    * user typed <tab>. See the example.c source code for a very easy to
   428    451    * understand example. */
   429    452   void linenoiseAddCompletion(linenoiseCompletions *lc, const char *str) {
   430    453       size_t len = strlen(str);
   431    454       char *copy, **cvec;
................................................................................
   467    490       ab->len += len;
   468    491   }
   469    492   
   470    493   static void abFree(struct abuf *ab) {
   471    494       free(ab->b);
   472    495   }
   473    496   
   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         -}
   497         -
   498    497   /* Single line low level line refresh.
   499    498    *
   500    499    * Rewrite the currently edited line accordingly to the buffer content,
   501    500    * cursor position, and number of columns of the terminal. */
   502    501   static void refreshSingleLine(struct linenoiseState *l) {
   503    502       char seq[64];
   504    503       size_t plen = strlen(l->prompt);
................................................................................
   520    519       abInit(&ab);
   521    520       /* Cursor to left edge */
   522    521       snprintf(seq,64,"\r");
   523    522       abAppend(&ab,seq,strlen(seq));
   524    523       /* Write the prompt and the current buffer content */
   525    524       abAppend(&ab,l->prompt,strlen(l->prompt));
   526    525       abAppend(&ab,buf,len);
   527         -    /* Show hits if any. */
   528         -    refreshShowHints(&ab,l,plen);
   529    526       /* Erase to right */
   530    527       snprintf(seq,64,"\x1b[0K");
   531    528       abAppend(&ab,seq,strlen(seq));
   532    529       /* Move cursor to original position. */
   533    530       snprintf(seq,64,"\r\x1b[%dC", (int)(pos+plen));
   534    531       abAppend(&ab,seq,strlen(seq));
   535    532       if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */
................................................................................
   554    551       /* Update maxrows if needed. */
   555    552       if (rows > (int)l->maxrows) l->maxrows = rows;
   556    553   
   557    554       /* First step: clear all the lines used before. To do so start by
   558    555        * going to the last row. */
   559    556       abInit(&ab);
   560    557       if (old_rows-rpos > 0) {
   561         -        /* lndebug("go down %d", old_rows-rpos); */
          558  +        lndebug("go down %d", old_rows-rpos);
   562    559           snprintf(seq,64,"\x1b[%dB", old_rows-rpos);
   563    560           abAppend(&ab,seq,strlen(seq));
   564    561       }
   565    562   
   566    563       /* Now for every row clear it, go up. */
   567    564       for (j = 0; j < old_rows-1; j++) {
   568         -        /* lndebug("clear+up"); */
          565  +        lndebug("clear+up", 0);
   569    566           snprintf(seq,64,"\r\x1b[0K\x1b[1A");
   570    567           abAppend(&ab,seq,strlen(seq));
   571    568       }
   572    569   
   573    570       /* Clean the top line. */
   574         -    /* lndebug("clear"); */
          571  +    lndebug("clear", 0);
   575    572       snprintf(seq,64,"\r\x1b[0K");
   576    573       abAppend(&ab,seq,strlen(seq));
   577    574   
   578    575       /* Write the prompt and the current buffer content */
   579    576       abAppend(&ab,l->prompt,strlen(l->prompt));
   580    577       abAppend(&ab,l->buf,l->len);
   581    578   
   582         -    /* Show hits if any. */
   583         -    refreshShowHints(&ab,l,plen);
   584         -
   585    579       /* If we are at the very end of the screen with our prompt, we need to
   586    580        * emit a newline and move the prompt to the first column. */
   587    581       if (l->pos &&
   588    582           l->pos == l->len &&
   589    583           (l->pos+plen) % l->cols == 0)
   590    584       {
   591         -        /* lndebug("<newline>"); */
          585  +        lndebug("<newline>", 0);
   592    586           abAppend(&ab,"\n",1);
   593    587           snprintf(seq,64,"\r");
   594    588           abAppend(&ab,seq,strlen(seq));
   595    589           rows++;
   596    590           if (rows > (int)l->maxrows) l->maxrows = rows;
   597    591       }
   598    592   
   599    593       /* Move cursor to right position. */
   600    594       rpos2 = (plen+l->pos+l->cols)/l->cols; /* current cursor relative row. */
   601         -    /* lndebug("rpos2 %d", rpos2); */
          595  +    lndebug("rpos2 %d", rpos2);
   602    596   
   603    597       /* Go up till we reach the expected positon. */
   604    598       if (rows-rpos2 > 0) {
   605         -        /* lndebug("go-up %d", rows-rpos2); */
          599  +        lndebug("go-up %d", rows-rpos2);
   606    600           snprintf(seq,64,"\x1b[%dA", rows-rpos2);
   607    601           abAppend(&ab,seq,strlen(seq));
   608    602       }
   609    603   
   610    604       /* Set column. */
   611    605       col = (plen+(int)l->pos) % (int)l->cols;
   612         -    /* lndebug("set col %d", 1+col); */
          606  +    lndebug("set col %d", 1+col);
   613    607       if (col)
   614    608           snprintf(seq,64,"\r\x1b[%dC", col);
   615    609       else
   616    610           snprintf(seq,64,"\r");
   617    611       abAppend(&ab,seq,strlen(seq));
   618    612   
   619         -    /* lndebug("\n"); */
          613  +    lndebug("\n", 0);
   620    614       l->oldpos = l->pos;
   621    615   
   622    616       if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */
   623    617       abFree(&ab);
   624    618   }
   625    619   
   626    620   /* Calls the two low level functions refreshSingleLine() or
................................................................................
   638    632   int linenoiseEditInsert(struct linenoiseState *l, char c) {
   639    633       if (l->len < l->buflen) {
   640    634           if (l->len == l->pos) {
   641    635               l->buf[l->pos] = c;
   642    636               l->pos++;
   643    637               l->len++;
   644    638               l->buf[l->len] = '\0';
   645         -            if ((!mlmode && l->plen+l->len < l->cols && !hintsCallback)) {
          639  +            if ((!mlmode && l->plen+l->len < l->cols) /* || mlmode */) {
   646    640                   /* Avoid a full update of the line in the
   647    641                    * trivial case. */
   648    642                   if (write(l->ofd,&c,1) == -1) return -1;
   649    643               } else {
   650    644                   refreshLine(l);
   651    645               }
   652    646           } else {
................................................................................
   812    806           }
   813    807   
   814    808           switch(c) {
   815    809           case ENTER:    /* enter */
   816    810               history_len--;
   817    811               free(history[history_len]);
   818    812               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         -            }
   827    813               return (int)l.len;
   828    814           case CTRL_C:     /* ctrl-c */
   829    815               errno = EAGAIN;
   830    816               return -1;
   831    817           case BACKSPACE:   /* backspace */
   832    818           case 8:     /* ctrl-h */
   833    819               linenoiseEditBackspace(&l);
................................................................................
   966    952           nread = read(STDIN_FILENO,&c,1);
   967    953           if (nread <= 0) continue;
   968    954           memmove(quit,quit+1,sizeof(quit)-1); /* shift string to left. */
   969    955           quit[sizeof(quit)-1] = c; /* Insert current char on the right. */
   970    956           if (memcmp(quit,"quit",sizeof(quit)) == 0) break;
   971    957   
   972    958           printf("'%c' %02x (%d) (type quit to exit)\n",
   973         -            isprint(c) ? c : '?', (int)c, (int)c);
          959  +            isprint((int)c) ? c : '?', (int)c, (int)c);
   974    960           printf("\r"); /* Go left edge manually, we are in raw mode. */
   975    961           fflush(stdout);
   976    962       }
   977    963       disableRawMode(STDIN_FILENO);
   978    964   }
   979    965   
   980    966   /* This function calls the line editing function linenoiseEdit() using
................................................................................
   982    968   static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) {
   983    969       int count;
   984    970   
   985    971       if (buflen == 0) {
   986    972           errno = EINVAL;
   987    973           return -1;
   988    974       }
   989         -
   990         -    if (enableRawMode(STDIN_FILENO) == -1) return -1;
   991         -    count = linenoiseEdit(STDIN_FILENO, STDOUT_FILENO, buf, buflen, prompt);
   992         -    disableRawMode(STDIN_FILENO);
   993         -    printf("\n");
   994         -    return count;
   995         -}
   996         -
   997         -/* This function is called when linenoise() is called with the standard
   998         - * input file descriptor not attached to a TTY. So for example when the
   999         - * program using linenoise is called in pipe or with a file redirected
  1000         - * to its standard input. In this case, we want to be able to return the
  1001         - * line regardless of its length (by default we are limited to 4k). */
  1002         -static char *linenoiseNoTTY(void) {
  1003         -    char *line = NULL;
  1004         -    size_t len = 0, maxlen = 0;
  1005         -
  1006         -    while(1) {
  1007         -        if (len == maxlen) {
  1008         -            if (maxlen == 0) maxlen = 16;
  1009         -            maxlen *= 2;
  1010         -            char *oldval = line;
  1011         -            line = realloc(line,maxlen);
  1012         -            if (line == NULL) {
  1013         -                if (oldval) free(oldval);
  1014         -                return NULL;
  1015         -            }
          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';
  1016    982           }
  1017         -        int c = fgetc(stdin);
  1018         -        if (c == EOF || c == '\n') {
  1019         -            if (c == EOF && len == 0) {
  1020         -                free(line);
  1021         -                return NULL;
  1022         -            } else {
  1023         -                line[len] = '\0';
  1024         -                return line;
  1025         -            }
  1026         -        } else {
  1027         -            line[len] = c;
  1028         -            len++;
  1029         -        }
          983  +    } else {
          984  +        /* Interactive editing. */
          985  +        if (enableRawMode(STDIN_FILENO) == -1) return -1;
          986  +        count = linenoiseEdit(STDIN_FILENO, STDOUT_FILENO, buf, buflen, prompt);
          987  +        disableRawMode(STDIN_FILENO);
          988  +        printf("\n");
  1030    989       }
          990  +    return count;
  1031    991   }
  1032    992   
  1033    993   /* The high level function that is the main API of the linenoise library.
  1034    994    * This function checks if the terminal has basic capabilities, just checking
  1035    995    * for a blacklist of stupid terminals, and later either calls the line
  1036    996    * editing function or uses dummy fgets() so that you will be able to type
  1037    997    * something even in the most desperate of the conditions. */
  1038    998   char *linenoise(const char *prompt) {
  1039    999       char buf[LINENOISE_MAX_LINE];
  1040   1000       int count;
  1041   1001   
  1042         -    if (!isatty(STDIN_FILENO)) {
  1043         -        /* Not a tty: read from file / pipe. In this mode we don't want any
  1044         -         * limit to the line size, so we call a function to handle that. */
  1045         -        return linenoiseNoTTY();
  1046         -    } else if (isUnsupportedTerm()) {
         1002  +    if (isUnsupportedTerm()) {
  1047   1003           size_t len;
  1048   1004   
  1049   1005           printf("%s",prompt);
  1050   1006           fflush(stdout);
  1051   1007           if (fgets(buf,LINENOISE_MAX_LINE,stdin) == NULL) return NULL;
  1052   1008           len = strlen(buf);
  1053   1009           while(len && (buf[len-1] == '\n' || buf[len-1] == '\r')) {
................................................................................
  1058   1014       } else {
  1059   1015           count = linenoiseRaw(buf,LINENOISE_MAX_LINE,prompt);
  1060   1016           if (count == -1) return NULL;
  1061   1017           return strdup(buf);
  1062   1018       }
  1063   1019   }
  1064   1020   
  1065         -/* This is just a wrapper the user may want to call in order to make sure
  1066         - * the linenoise returned buffer is freed with the same allocator it was
  1067         - * created with. Useful when the main program is using an alternative
  1068         - * allocator. */
  1069         -void linenoiseFree(void *ptr) {
  1070         -    free(ptr);
  1071         -}
  1072         -
  1073   1021   /* ================================ History ================================= */
  1074   1022   
  1075   1023   /* Free the history, but does not reset it. Only used when we have to
  1076   1024    * exit() to avoid memory leaks are reported by valgrind & co. */
  1077   1025   static void freeHistory(void) {
  1078   1026       if (history) {
  1079   1027           int j;
................................................................................
  1157   1105           history_len = history_max_len;
  1158   1106       return 1;
  1159   1107   }
  1160   1108   
  1161   1109   /* Save the history in the specified file. On success 0 is returned
  1162   1110    * otherwise -1 is returned. */
  1163   1111   int linenoiseHistorySave(const char *filename) {
  1164         -    mode_t old_umask = umask(S_IXUSR|S_IRWXG|S_IRWXO);
  1165         -    FILE *fp;
         1112  +    FILE *fp = fopen(filename,"w");
  1166   1113       int j;
  1167   1114   
  1168         -    fp = fopen(filename,"w");
  1169         -    umask(old_umask);
  1170   1115       if (fp == NULL) return -1;
  1171         -    chmod(filename,S_IRUSR|S_IWUSR);
  1172   1116       for (j = 0; j < history_len; j++)
  1173   1117           fprintf(fp,"%s\n",history[j]);
  1174   1118       fclose(fp);
  1175   1119       return 0;
  1176   1120   }
  1177   1121   
  1178   1122   /* Load the history from the specified file. If the file does not exist

Changes to src/linenoise.h.

    45     45   
    46     46   typedef struct linenoiseCompletions {
    47     47     size_t len;
    48     48     char **cvec;
    49     49   } linenoiseCompletions;
    50     50   
    51     51   typedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *);
    52         -typedef char*(linenoiseHintsCallback)(const char *, int *color, int *bold);
    53         -typedef void(linenoiseFreeHintsCallback)(void *);
    54     52   void linenoiseSetCompletionCallback(linenoiseCompletionCallback *);
    55         -void linenoiseSetHintsCallback(linenoiseHintsCallback *);
    56         -void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *);
    57     53   void linenoiseAddCompletion(linenoiseCompletions *, const char *);
    58     54   
    59     55   char *linenoise(const char *prompt);
    60         -void linenoiseFree(void *ptr);
    61     56   int linenoiseHistoryAdd(const char *line);
    62     57   int linenoiseHistorySetMaxLen(int len);
    63     58   int linenoiseHistorySave(const char *filename);
    64     59   int linenoiseHistoryLoad(const char *filename);
    65     60   void linenoiseClearScreen(void);
    66     61   void linenoiseSetMultiLine(int ml);
    67     62   void linenoisePrintKeyCodes(void);
    68     63   
    69     64   #ifdef __cplusplus
    70     65   }
    71     66   #endif
    72     67   
    73     68   #endif /* __LINENOISE_H */

Changes to src/login.c.

   630    630       }else if( anonFlag ){
   631    631         @ <p>Login as <b>anonymous</b> or any named user
   632    632         @ to access page <b>%h(zAbbrev)</b>.
   633    633       }else{
   634    634         @ <p>Login as a named user to access page <b>%h(zAbbrev)</b>.
   635    635       }
   636    636     }
   637         -  if( g.sslNotAvailable==0
   638         -   && strncmp(g.zBaseURL,"https:",6)!=0
   639         -   && db_get_boolean("https-login",0)
   640         -  ){
   641         -    form_begin(0, "https:%s/login", g.zBaseURL+5);
   642         -  }else{
   643         -    form_begin(0, "%R/login");
   644         -  }
          637  +  form_begin(0, "%R/login");
   645    638     if( zGoto ){
   646    639       @ <input type="hidden" name="g" value="%h(zGoto)" />
   647    640     }else if( zReferer && strncmp(g.zBaseURL, zReferer, strlen(g.zBaseURL))==0 ){
   648    641       @ <input type="hidden" name="g" value="%h(zReferer)" />
   649    642     }
   650    643     if( anonFlag ){
   651    644       @ <input type="hidden" name="anon" value="1" />
................................................................................
   672    665     if( g.zLogin==0 && (anonFlag || zGoto==0) ){
   673    666       zAnonPw = db_text(0, "SELECT pw FROM user"
   674    667                            " WHERE login='anonymous'"
   675    668                            "   AND cap!=''");
   676    669     }
   677    670     @ <tr>
   678    671     @   <td></td>
   679         -  @   <td><input type="submit" name="in" value="Login">
          672  +  @   <td><input type="submit" name="in" value="Login"
          673  +  @        onClick="chngAction(this.form)" /></td>
   680    674     @ </tr>
   681    675     @ </table>
          676  +  @ <script>
          677  +  @   gebi('u').focus()
          678  +  @   function chngAction(form){
          679  +  if( g.sslNotAvailable==0
          680  +   && strncmp(g.zBaseURL,"https:",6)!=0
          681  +   && db_get_boolean("https-login",0)
          682  +  ){
          683  +     char *zSSL = mprintf("https:%s", &g.zBaseURL[5]);
          684  +     @  if( form.u.value!="anonymous" ){
          685  +     @     form.action = "%h(zSSL)/login";
          686  +     @  }
          687  +  }
          688  +  @ }
          689  +  @ </script>
   682    690     @ <p>Pressing the Login button grants permission to store a cookie.</p>
   683    691     if( db_get_boolean("self-register", 0) ){
   684    692       @ <p>If you do not have an account, you can
   685    693       @ <a href="%R/register?g=%T(P("G"))">create one</a>.
   686    694     }
   687    695     if( zAnonPw ){
   688    696       unsigned int uSeed = captcha_seed();
................................................................................
   693    701       @ <p><input type="hidden" name="cs" value="%u(uSeed)" />
   694    702       @ Visitors may enter <b>anonymous</b> as the user-ID with
   695    703       @ the 8-character hexadecimal password shown below:</p>
   696    704       @ <div class="captcha"><table class="captcha"><tr><td><pre>
   697    705       @ %h(zCaptcha)
   698    706       @ </pre></td></tr></table>
   699    707       if( bAutoCaptcha ) {
   700         -       @ <input type="button" value="Fill out captcha" id='autofillButton' \
   701         -       @ data-af='%s(zDecoded)' />
   702         -       style_load_one_js_file("login.js");
          708  +        @ <input type="button" value="Fill out captcha"
          709  +        @  onclick="gebi('u').value='anonymous'; gebi('p').value='%s(zDecoded)';" />
   703    710       }
   704    711       @ </div>
   705    712       free(zCaptcha);
   706    713     }
   707    714     @ </form>
   708    715     if( g.zLogin && g.perm.Password ){
   709    716       @ <hr />
................................................................................
   924    931     ** local login is disabled and if we are using HTTP and not HTTPS,
   925    932     ** then there is no need to check user credentials.
   926    933     **
   927    934     ** This feature allows the "fossil ui" command to give the user
   928    935     ** full access rights without having to log in.
   929    936     */
   930    937     zRemoteAddr = ipPrefix(zIpAddr = PD("REMOTE_ADDR","nil"));
   931         -  if( ( cgi_is_loopback(zIpAddr)
   932         -       || (g.fSshClient & CGI_SSH_CLIENT)!=0 )
          938  +  if( ( fossil_strcmp(zIpAddr, "127.0.0.1")==0 ||
          939  +        (g.fSshClient & CGI_SSH_CLIENT)!=0 )
   933    940      && g.useLocalauth
   934    941      && db_get_int("localauth",0)==0
   935    942      && P("HTTPS")==0
   936    943     ){
   937    944       if( g.localOpen ) zLogin = db_lget("default-user",0);
   938    945       if( zLogin!=0 ){
   939    946         uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zLogin);
................................................................................
  1556   1563       " WHERE name GLOB 'peer-repo-*'"
  1557   1564       "   AND name <> 'peer-repo-%q'"
  1558   1565       " ORDER BY +value",
  1559   1566       zSelfCode
  1560   1567     );
  1561   1568     while( db_step(&q)==SQLITE_ROW ){
  1562   1569       const char *zRepoName = db_column_text(&q, 1);
  1563         -    if( file_size(zRepoName, ExtFILE)<0 ){
         1570  +    if( file_size(zRepoName)<0 ){
  1564   1571         /* Silently remove non-existent repositories from the login group. */
  1565   1572         const char *zLabel = db_column_text(&q, 0);
  1566   1573         db_multi_exec(
  1567   1574            "DELETE FROM config WHERE name GLOB 'peer-*-%q'",
  1568   1575            &zLabel[10]
  1569   1576         );
  1570   1577         continue;
................................................................................
  1652   1659     /* Make sure we are not trying to join ourselves */
  1653   1660     if( fossil_strcmp(zRepo, zSelfRepo)==0 ){
  1654   1661       *pzErrMsg = mprintf("The \"other\" repository is the same as this one.");
  1655   1662       return;
  1656   1663     }
  1657   1664   
  1658   1665     /* Make sure the other repository is a valid Fossil database */
  1659         -  if( file_size(zRepo, ExtFILE)<0 ){
         1666  +  if( file_size(zRepo)<0 ){
  1660   1667       *pzErrMsg = mprintf("repository file \"%s\" does not exist", zRepo);
  1661   1668       return;
  1662   1669     }
  1663   1670     rc = sqlite3_open_v2(
  1664   1671          zRepo, &pOther,
  1665   1672          SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
  1666   1673          g.zVfsName

Deleted src/login.js.

     1         -/* Javascript code to handle button actions on the login page */
     2         -var autofillButton = document.getElementById('autofillButton');
     3         -autofillButton.onclick = function(){
     4         -  document.getElementById('u').value = 'anonymous';
     5         -  document.getElementById('p').value = autofillButton.getAttribute('data-af');
     6         -};

Changes to src/lookslike.c.

   420    420     const char *zCount = find_option("limit","n",1);
   421    421     int nRepeat = 1;
   422    422   
   423    423     if( g.argc!=3 ) usage("FILENAME");
   424    424     if( zCount ){
   425    425       nRepeat = atoi(zCount);
   426    426     }
   427         -  blob_read_from_file(&blob, g.argv[2], ExtFILE);
          427  +  blob_read_from_file(&blob, g.argv[2]);
   428    428     while( --nRepeat >= 0 ){
   429    429       fUtf8 = starts_with_utf8_bom(&blob, 0);
   430    430       fUtf16 = starts_with_utf16_bom(&blob, 0, &bRevUtf16);
   431    431       if( fForceUtf8 ){
   432    432         fUnicode = 0;
   433    433       }else{
   434    434         fUnicode = could_be_utf16(&blob, 0) || fForceUtf16;

Changes to src/main.c.

   229    229     /* Storage for the aux() and/or option() SQL function arguments */
   230    230     int nAux;                    /* Number of distinct aux() or option() values */
   231    231     const char *azAuxName[MX_AUX]; /* Name of each aux() or option() value */
   232    232     char *azAuxParam[MX_AUX];      /* Param of each aux() or option() value */
   233    233     const char *azAuxVal[MX_AUX];  /* Value of each aux() or option() value */
   234    234     const char **azAuxOpt[MX_AUX]; /* Options of each option() value */
   235    235     int anAuxCols[MX_AUX];         /* Number of columns for option() values */
          236  +
   236    237     int allowSymlinks;             /* Cached "allow-symlinks" option */
          238  +
   237    239     int mainTimerId;               /* Set to fossil_timer_start() */
   238         -  int nPendingRequest;           /* # of HTTP requests in "fossil server" */
   239         -  int nRequest;                  /* Total # of HTTP request */
   240    240   #ifdef FOSSIL_ENABLE_JSON
   241    241     struct FossilJsonBits {
   242    242       int isJsonMode;            /* True if running in JSON mode, else
   243    243                                     false. This changes how errors are
   244    244                                     reported. In JSON mode we try to
   245    245                                     always output JSON-form error
   246    246                                     responses and always exit() with
................................................................................
   516    516   #ifdef __APPLE__
   517    517     /* Disable the file alias warning on apple products because Time Machine
   518    518     ** creates lots of aliases and the warning alarms people. */
   519    519     if( iCode==SQLITE_WARNING ) return;
   520    520   #endif
   521    521     if( iCode==SQLITE_SCHEMA ) return;
   522    522     if( g.dbIgnoreErrors ) return;
   523         -#ifdef SQLITE_READONLY_DIRECTORY
   524         -  if( iCode==SQLITE_READONLY_DIRECTORY ){
   525         -    zErrmsg = "database is in a read-only directory";
   526         -  }
   527         -#endif
   528    523     fossil_warning("%s: %s", fossil_sqlite_return_code_name(iCode), zErrmsg);
   529    524   }
   530    525   
   531    526   /*
   532    527   ** This function attempts to find command line options known to contain
   533    528   ** bitwise flags and initializes the associated global variables.  After
   534    529   ** this function executes, all global variables (i.e. in the "g" struct)
................................................................................
  1184   1179       if( g.db!=0 ){
  1185   1180         db_close(1);
  1186   1181       }
  1187   1182   
  1188   1183       file_canonical_name(zRepo, &dir, 0);
  1189   1184       zDir = blob_str(&dir);
  1190   1185       if( !noJail ){
  1191         -      if( file_isdir(zDir, ExtFILE)==1 ){
         1186  +      if( file_isdir(zDir)==1 ){
  1192   1187           if( file_chdir(zDir, 1) ){
  1193   1188             fossil_fatal("unable to chroot into %s", zDir);
  1194   1189           }
  1195   1190           g.fJail = 1;
  1196   1191           zRepo = "/";
  1197   1192         }else{
  1198   1193           for(i=strlen(zDir)-1; i>0 && zDir[i]!='/'; i--){}
................................................................................
  1211   1206         fossil_fatal("cannot stat() repository: %s", zRepo);
  1212   1207       }
  1213   1208       i = setgid(sStat.st_gid);
  1214   1209       i = i || setuid(sStat.st_uid);
  1215   1210       if(i){
  1216   1211         fossil_fatal("setgid/uid() failed with errno %d", errno);
  1217   1212       }
  1218         -    if( g.db==0 && file_isfile(zRepo, ExtFILE) ){
         1213  +    if( g.db==0 && file_isfile(zRepo) ){
  1219   1214         db_open_repository(zRepo);
  1220   1215       }
  1221   1216     }
  1222   1217   #endif
  1223   1218     return zRepo;
  1224   1219   }
  1225   1220   
................................................................................
  1430   1425         ** does not exist, szFile will become -1.  If the file does exist,
  1431   1426         ** then szFile will become zero (for an empty file) or positive.
  1432   1427         ** Special case:  Assume any file with a basename of ".fossil" does
  1433   1428         ** not exist.
  1434   1429         */
  1435   1430         zCleanRepo = file_cleanup_fullpath(zRepo);
  1436   1431         if( szFile==0 && sqlite3_strglob("*/.fossil",zRepo)!=0 ){
  1437         -        szFile = file_size(zCleanRepo, ExtFILE);
         1432  +        szFile = file_size(zCleanRepo);
  1438   1433           if( g.fHttpTrace ){
  1439   1434             char zBuf[24];
  1440   1435             sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", szFile);
  1441   1436             @ <!-- file_size(%h(zCleanRepo)) is %s(zBuf) -->
  1442   1437             fprintf(stderr, "# file_size(%s) = %s\n", zCleanRepo, zBuf);
  1443   1438           }
  1444   1439         }
................................................................................
  1450   1445         if( szFile<0 && i>0 ){
  1451   1446           const char *zMimetype;
  1452   1447           assert( fossil_strcmp(&zRepo[j], ".fossil")==0 );
  1453   1448           zRepo[j] = 0;  /* Remove the ".fossil" suffix */
  1454   1449   
  1455   1450           /* The PATH_INFO prefix seen so far is a valid directory.
  1456   1451           ** Continue the loop with the next element of the PATH_INFO */
  1457         -        if( zPathInfo[i]=='/' && file_isdir(zCleanRepo, ExtFILE)==1 ){
         1452  +        if( zPathInfo[i]=='/' && file_isdir(zCleanRepo)==1 ){
  1458   1453             fossil_free(zToFree);
  1459   1454             i++;
  1460   1455             continue;
  1461   1456           }
  1462   1457   
  1463   1458           /* If zRepo is the name of an ordinary file that matches the
  1464   1459           ** "--file GLOB" pattern, then the CGI reply is the text of
................................................................................
  1468   1463           ** to be returned this way, to prevent complete repositories from
  1469   1464           ** being delivered accidently.  This is not intended to be a
  1470   1465           ** general-purpose web server.  The "--file GLOB" mechanism is
  1471   1466           ** designed to allow the delivery of a few static images or HTML
  1472   1467           ** pages.
  1473   1468           */
  1474   1469           if( pFileGlob!=0
  1475         -         && file_isfile(zCleanRepo, ExtFILE)
         1470  +         && file_isfile(zCleanRepo)
  1476   1471            && glob_match(pFileGlob, file_cleanup_fullpath(zRepo))
  1477   1472            && sqlite3_strglob("*.fossil*",zRepo)!=0
  1478   1473            && (zMimetype = mimetype_from_name(zRepo))!=0
  1479   1474            && strcmp(zMimetype, "application/x-fossil-artifact")!=0
  1480   1475           ){
  1481   1476             Blob content;
  1482         -          blob_read_from_file(&content, file_cleanup_fullpath(zRepo), ExtFILE);
         1477  +          blob_read_from_file(&content, file_cleanup_fullpath(zRepo));
  1483   1478             cgi_set_content_type(zMimetype);
  1484   1479             cgi_set_content(&content);
  1485   1480             cgi_reply();
  1486   1481             return;
  1487   1482           }
  1488   1483           zRepo[j] = '.';
  1489   1484         }
................................................................................
  1545   1540           @ <!-- translated g.zBaseURL: "%h(g.zBaseURL)" -->
  1546   1541           fprintf(stderr, "# translated g.zBaseURL = [%s]\n", g.zBaseURL);
  1547   1542         }
  1548   1543       }
  1549   1544     }
  1550   1545   
  1551   1546     /* At this point, the appropriate repository database file will have
  1552         -  ** been opened.
  1553         -  **
  1554         -  ** Check to see if the the PATH_INFO begins with "draft[1-9]" and if
  1555         -  ** so activate the special handling for draft skins
  1556         -  */
  1557         -  if( zPathInfo && strncmp(zPathInfo,"/draft",6)==0
  1558         -   && zPathInfo[6]>='1' && zPathInfo[6]<='9'
  1559         -   && (zPathInfo[7]=='/' || zPathInfo[7]==0)
  1560         -  ){
  1561         -    int iSkin = zPathInfo[6] - '0';
  1562         -    char *zNewScript;
  1563         -    skin_use_draft(iSkin);
  1564         -    zNewScript = mprintf("%s/draft%d", P("SCRIPT_NAME"), iSkin);
  1565         -    if( g.zTop ) g.zTop = mprintf("%s/draft%d", g.zTop, iSkin);
  1566         -    if( g.zBaseURL ) g.zBaseURL = mprintf("%s/draft%d", g.zBaseURL, iSkin);
  1567         -    zPathInfo += 7;
  1568         -    cgi_replace_parameter("PATH_INFO", zPathInfo);
  1569         -    cgi_replace_parameter("SCRIPT_NAME", zNewScript);
  1570         -  }
  1571         -
  1572         -  /* If the content type is application/x-fossil or 
  1573         -  ** application/x-fossil-debug, then a sync/push/pull/clone is
  1574         -  ** desired, so default the PATH_INFO to /xfer
         1547  +  ** been opened.  Use the first element of PATH_INFO as the page name
         1548  +  ** and deliver the appropriate page back to the user.
  1575   1549     */
  1576   1550     if( g.zContentType &&
  1577   1551         strncmp(g.zContentType, "application/x-fossil", 20)==0 ){
  1578   1552       /* Special case:  If the content mimetype shows that it is "fossil sync"
  1579   1553       ** payload, then pretend that the PATH_INFO is /xfer so that we always
  1580   1554       ** invoke the sync page. */
  1581   1555       zPathInfo = "/xfer";
  1582   1556     }
  1583         -
  1584         -  /* Use the first element of PATH_INFO as the page name
  1585         -  ** and deliver the appropriate page back to the user.
  1586         -  */
  1587   1557     set_base_url(0);
  1588   1558     if( zPathInfo==0 || zPathInfo[0]==0
  1589   1559         || (zPathInfo[0]=='/' && zPathInfo[1]==0) ){
  1590   1560       /* Second special case: If the PATH_INFO is blank, issue a redirect to
  1591   1561       ** the home page identified by the "index-page" setting in the repository
  1592   1562       ** CONFIG table, to "/index" if there no "index-page" setting. */
  1593   1563   #ifdef FOSSIL_ENABLE_JSON
................................................................................
  1872   1842       zFile = g.argv[1];
  1873   1843     }
  1874   1844     g.httpOut = stdout;
  1875   1845     g.httpIn = stdin;
  1876   1846     fossil_binary_mode(g.httpOut);
  1877   1847     fossil_binary_mode(g.httpIn);
  1878   1848     g.cgiOutput = 1;
  1879         -  blob_read_from_file(&config, zFile, ExtFILE);
         1849  +  blob_read_from_file(&config, zFile);
  1880   1850     while( blob_line(&config, &line) ){
  1881   1851       if( !blob_token(&line, &key) ) continue;
  1882   1852       if( blob_buffer(&key)[0]=='#' ) continue;
  1883   1853       if( blob_eq(&key, "repository:") && blob_tail(&line, &value) ){
  1884   1854         /* repository: FILENAME
  1885   1855         **
  1886   1856         ** The name of the Fossil repository to be served via CGI.  Most
................................................................................
  2046   2016   ** does not already exist. Always use "auto" hash-policy in this case.
  2047   2017   */
  2048   2018   static void find_server_repository(int arg, int fCreate){
  2049   2019     if( g.argc<=arg ){
  2050   2020       db_must_be_within_tree();
  2051   2021     }else{
  2052   2022       const char *zRepo = g.argv[arg];
  2053         -    int isDir = file_isdir(zRepo, ExtFILE);
         2023  +    int isDir = file_isdir(zRepo);
  2054   2024       if( isDir==1 ){
  2055   2025         g.zRepositoryName = mprintf("%s", zRepo);
  2056   2026         file_simplify_name(g.zRepositoryName, -1, 0);
  2057   2027       }else{
  2058   2028         if( isDir==0 && fCreate ){
  2059   2029           const char *zPassword;
  2060   2030           db_create_repository(zRepo);
................................................................................
  2322   2292       zPath += i;
  2323   2293     }
  2324   2294     return 0;
  2325   2295   }
  2326   2296   #endif
  2327   2297   #endif
  2328   2298   
  2329         -/*
  2330         -** Send a time-out reply
  2331         -*/
  2332         -void sigalrm_handler(int x){
  2333         -  printf("TIMEOUT\n");
  2334         -  fflush(stdout);
  2335         -  exit(1);
  2336         -}
  2337         -
  2338   2299   /*
  2339   2300   ** COMMAND: server*
  2340   2301   ** COMMAND: ui
  2341   2302   **
  2342   2303   ** Usage: %fossil server ?OPTIONS? ?REPOSITORY?
  2343   2304   **    or: %fossil ui ?OPTIONS? ?REPOSITORY?
  2344   2305   **
................................................................................
  2382   2343   **   --baseurl URL       Use URL as the base (useful for reverse proxies)
  2383   2344   **   --create            Create a new REPOSITORY if it does not already exist
  2384   2345   **   --page PAGE         Start "ui" on PAGE.  ex: --page "timeline?y=ci"
  2385   2346   **   --files GLOBLIST    Comma-separated list of glob patterns for static files
  2386   2347   **   --localauth         enable automatic login for requests from localhost
  2387   2348   **   --localhost         listen on 127.0.0.1 only (always true for "ui")
  2388   2349   **   --https             signal a request coming in via https
  2389         -**   --max-latency N     Do not let any single HTTP request run for more than N
  2390         -**                       seconds (only works on unix)
  2391   2350   **   --nojail            Drop root privileges but do not enter the chroot jail
  2392   2351   **   --nossl             signal that no SSL connections are available
  2393   2352   **   --notfound URL      Redirect
  2394   2353   **   -P|--port TCPPORT   listen to request on port TCPPORT
  2395   2354   **   --th-trace          trace TH1 execution (for debugging purposes)
  2396   2355   **   --repolist          If REPOSITORY is dir, URL "/" lists repos.
  2397   2356   **   --scgi              Accept SCGI rather than HTTP
................................................................................
  2407   2366     const char *zBrowser;     /* Name of web browser program */
  2408   2367     char *zBrowserCmd = 0;    /* Command to launch the web browser */
  2409   2368     int isUiCmd;              /* True if command is "ui", not "server' */
  2410   2369     const char *zNotFound;    /* The --notfound option or NULL */
  2411   2370     int flags = 0;            /* Server flags */
  2412   2371   #if !defined(_WIN32)
  2413   2372     int noJail;               /* Do not enter the chroot jail */
  2414         -  const char *zMaxLatency;   /* Maximum runtime of any single HTTP request */
  2415   2373   #endif
  2416   2374     int allowRepoList;         /* List repositories on URL "/" */
  2417   2375     const char *zAltBase;      /* Argument to the --baseurl option */
  2418   2376     const char *zFileGlob;     /* Static content must match this */
  2419   2377     char *zIpAddr = 0;         /* Bind to this IP address */
  2420   2378     int fCreate = 0;           /* The --create flag */
  2421   2379     const char *zInitPage = 0; /* Start on this page.  --page option */
................................................................................
  2435   2393       zFileGlob = z;
  2436   2394     }else{
  2437   2395       z